CHECKPOINT unlogged data

Started by Christoph Berg8 months ago34 messages
#1Christoph Berg
myon@debian.org
1 attachment(s)

A customer reported to use CHECKPOINT before shutdowns to make the
shutdowns themselves faster and asked if it was possible to make
CHECKPOINT optionally also write out unlogged table data for that
purpose. I think the idea makes sense, so there's the patch.

Christoph

Attachments:

v1-0001-Add-immediate-and-flush_all-options-to-checkpoint.patchtext/x-diff; charset=us-asciiDownload
From 1d7d7b7fab78312f5423dff578dd2689eac57591 Mon Sep 17 00:00:00 2001
From: Christoph Berg <myon@debian.org>
Date: Fri, 30 May 2025 17:58:35 +0200
Subject: [PATCH v1] Add immediate and flush_all 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. Since it's easy enough, also add an IMMEDIATE
option to allow avoiding triggering a fast checkpoint.
---
 doc/src/sgml/ref/checkpoint.sgml    | 59 +++++++++++++++++++++++++----
 src/backend/parser/gram.y           |  8 ++++
 src/backend/tcop/utility.c          | 26 ++++++++++++-
 src/bin/psql/tab-complete.in.c      |  5 +++
 src/include/nodes/parsenodes.h      |  1 +
 src/test/regress/expected/stats.out |  4 +-
 src/test/regress/sql/stats.sql      |  4 +-
 7 files changed, 95 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/checkpoint.sgml b/doc/src/sgml/ref/checkpoint.sgml
index db011a47d04..4889f7ba1f3 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>
+
+    IMMEDIATE [ <replaceable class="parameter">boolean</replaceable> ]
+    FLUSH_ALL [ <replaceable class="parameter">boolean</replaceable> ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -31,18 +36,18 @@ 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
+   log.  All data files will be flushed to disk, except for relations marked <literal>UNLOGGED</literal>.  Refer to
    <xref linkend="wal-configuration"/> for more details about what happens
    during a checkpoint.
   </para>
 
   <para>
+   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"/>).
    The <command>CHECKPOINT</command> command forces an immediate
-   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.
+   checkpoint by default when the command is issued, without waiting for a
+   regular checkpoint scheduled by the system.
   </para>
 
   <para>
@@ -58,6 +63,46 @@ CHECKPOINT
   </para>
  </refsect1>
 
+ <refsect1>
+  <title>Parameters</title>
+
+  <variablelist>
+   <varlistentry>
+    <term><literal>IMMEDIATE</literal></term>
+    <listitem>
+     <para>
+      Requests the checkpoint to start immediately and run at full speed
+      without spreading the I/O load out. Defaults to on.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>FLUSH_ALL</literal></term>
+    <listitem>
+     <para>
+      Requests the checkpoint to also flush data of <literal>UNLOGGED</literal>
+      relations. Defaults to 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 25fe3d58016..76d334dd5a7 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -943,6 +943,11 @@ standard_ProcessUtility(PlannedStmt *pstmt,
 			break;
 
 		case T_CheckPointStmt:
+			CheckPointStmt   *stmt = (CheckPointStmt *) parsetree;
+			ListCell   *lc;
+			bool		immediate = true;
+			bool		flush_all = false;
+
 			if (!has_privs_of_role(GetUserId(), ROLE_PG_CHECKPOINT))
 				ereport(ERROR,
 						(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
@@ -952,7 +957,26 @@ standard_ProcessUtility(PlannedStmt *pstmt,
 						 errdetail("Only roles with privileges of the \"%s\" role may execute this command.",
 								   "pg_checkpoint")));
 
-			RequestCheckpoint(CHECKPOINT_IMMEDIATE | CHECKPOINT_WAIT |
+			/* Parse options list */
+			foreach(lc, stmt->options)
+			{
+				DefElem    *opt = (DefElem *) lfirst(lc);
+
+				if (strcmp(opt->defname, "immediate") == 0)
+					immediate = defGetBoolean(opt);
+				else if (strcmp(opt->defname, "flush_all") == 0)
+					flush_all = defGetBoolean(opt);
+				else
+					ereport(ERROR,
+							(errcode(ERRCODE_SYNTAX_ERROR),
+							 errmsg("unrecognized CHECKPOINT option \"%s\"", opt->defname),
+							 errhint("valid options are \"IMMEDIATE\" and \"FLUSH_ALL\""),
+							 parser_errposition(pstate, opt->location)));
+			}
+
+			RequestCheckpoint(CHECKPOINT_WAIT |
+							  (immediate ? CHECKPOINT_IMMEDIATE : 0) |
+							  (flush_all ? CHECKPOINT_FLUSH_ALL : 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 ec65ab79fec..bfea5fc0d6c 100644
--- a/src/bin/psql/tab-complete.in.c
+++ b/src/bin/psql/tab-complete.in.c
@@ -3125,6 +3125,11 @@ 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("IMMEDIATE", "FLUSH_ALL");
 /* 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..a760b28b7fa 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;
+-- results of the first. And while at it, test checkpoint options.
 CHECKPOINT;
+CHECKPOINT (immediate, flush_all);
 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..5f45a2ceee3 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;
+-- results of the first. And while at it, test checkpoint options.
 CHECKPOINT;
+CHECKPOINT (immediate, flush_all);
 
 SELECT num_requested > :rqst_ckpts_before FROM pg_stat_checkpointer;
 SELECT wal_bytes > :wal_bytes_before FROM pg_stat_wal;
-- 
2.47.2

#2Andres Freund
andres@anarazel.de
In reply to: Christoph Berg (#1)
Re: CHECKPOINT unlogged data

Hi,

On 2025-05-30 18:17:45 +0200, Christoph Berg wrote:

A customer reported to use CHECKPOINT before shutdowns to make the
shutdowns themselves faster and asked if it was possible to make
CHECKPOINT optionally also write out unlogged table data for that
purpose. I think the idea makes sense, so there's the patch.

I've seen the need for this quite a few times, fwiw. So +1 to the idea.

Greetings,

Andres Freund

#3Christoph Berg
myon@debian.org
In reply to: Andres Freund (#2)
Re: CHECKPOINT unlogged data

Re: Andres Freund

Hi,

On 2025-05-30 18:17:45 +0200, Christoph Berg wrote:

A customer reported to use CHECKPOINT before shutdowns to make the
shutdowns themselves faster and asked if it was possible to make
CHECKPOINT optionally also write out unlogged table data for that
purpose. I think the idea makes sense, so there's the patch.

I've seen the need for this quite a few times, fwiw. So +1 to the idea.

Do we want to officially mention this use case in checkpoint.sgml? I've
already replaced the "is not intended for use during normal operation"
wording by "not required during normal operation", but we might go one
step further and endorse it.

Christoph

#4Andres Freund
andres@anarazel.de
In reply to: Christoph Berg (#3)
Re: CHECKPOINT unlogged data

Hi,

On May 30, 2025 12:55:28 PM EDT, Christoph Berg <myon@debian.org> wrote:

Re: Andres Freund

Hi,

On 2025-05-30 18:17:45 +0200, Christoph Berg wrote:

A customer reported to use CHECKPOINT before shutdowns to make the
shutdowns themselves faster and asked if it was possible to make
CHECKPOINT optionally also write out unlogged table data for that
purpose. I think the idea makes sense, so there's the patch.

I've seen the need for this quite a few times, fwiw. So +1 to the idea.

Do we want to officially mention this use case in checkpoint.sgml? I've
already replaced the "is not intended for use during normal operation"
wording by "not required during normal operation", but we might go one
step further and endorse it.

Yes, I think it's good to mention what it is useful for.

Greetings,

Andres
--
Sent from my Android device with K-9 Mail. Please excuse my brevity.

#5Nathan Bossart
nathandbossart@gmail.com
In reply to: Andres Freund (#2)
Re: CHECKPOINT unlogged data

On Fri, May 30, 2025 at 12:39:02PM -0400, Andres Freund wrote:

On 2025-05-30 18:17:45 +0200, Christoph Berg wrote:

A customer reported to use CHECKPOINT before shutdowns to make the
shutdowns themselves faster and asked if it was possible to make
CHECKPOINT optionally also write out unlogged table data for that
purpose. I think the idea makes sense, so there's the patch.

I've seen the need for this quite a few times, fwiw. So +1 to the idea.

This patch also adds an IMMEDIATE option, which I proposed some time ago
[0]: /messages/by-id/17A03557-CF5C-4D4B-B719-A1D98DD75E75@amazon.com
usefulness. FWIW I have no concerns about adding a few retail options to
CHECKPOINT, but others might balk at options without solid use-cases. The
unlogged table one seems reasonable enough.

[0]: /messages/by-id/17A03557-CF5C-4D4B-B719-A1D98DD75E75@amazon.com

--
nathan

#6Christoph Berg
myon@debian.org
In reply to: Nathan Bossart (#5)
Re: CHECKPOINT unlogged data

Re: Nathan Bossart

This patch also adds an IMMEDIATE option, which I proposed some time ago
[0]. I ended up withdrawing it due to general skepticism about its

Thanks for the pointer, I did not go that far back when looking for
older threads.

When writing the patch, I was also thinking about naming the option
"fast" or "spread" but ultimately went with "immediate" because that's
what the log message is using:

=# checkpoint;
2025-05-30 18:23:17.433 CEST [579834] LOG: Checkpoint beginnt: immediate force wait

SQL command "(options)" tend to be booleans, hence "immediate {on|off}".
Introducing two separate keywords "fast" and "spread" seemed
confusing, and there is no precedent for "fast=on" in other tools or
the replication protocol.

usefulness. FWIW I have no concerns about adding a few retail options to
CHECKPOINT, but others might balk at options without solid use-cases. The
unlogged table one seems reasonable enough.

I think the two options immediate and flush_all are actually useful in
combination for the shutdown case. If operation is to continue
normally until just before the shutdown, it might make sense to run
these 3 commands (or just #1 and #3):

checkpoint (flush_all, immediate false);
checkpoint (flush_all);
pg_ctl stop

(I also thought about a VERBOSE option, but since the checkpoint
messages are generated by a different process, it's probably harder
than I initially thought.)

Christoph

#7Andres Freund
andres@anarazel.de
In reply to: Christoph Berg (#6)
Re: CHECKPOINT unlogged data

On 2025-05-30 19:23:04 +0200, Christoph Berg wrote:

Re: Nathan Bossart

This patch also adds an IMMEDIATE option, which I proposed some time ago
[0]. I ended up withdrawing it due to general skepticism about its

Thanks for the pointer, I did not go that far back when looking for
older threads.

When writing the patch, I was also thinking about naming the option
"fast" or "spread" but ultimately went with "immediate" because that's
what the log message is using:

=# checkpoint;
2025-05-30 18:23:17.433 CEST [579834] LOG: Checkpoint beginnt: immediate force wait

SQL command "(options)" tend to be booleans, hence "immediate {on|off}".
Introducing two separate keywords "fast" and "spread" seemed
confusing, and there is no precedent for "fast=on" in other tools or
the replication protocol.

I'd add a 'mode' that can be set to an arbitrary string, which then can be
validated in C code. That seems more future proof.

#8Christoph Berg
myon@debian.org
In reply to: Andres Freund (#7)
1 attachment(s)
Re: CHECKPOINT unlogged data

Re: Andres Freund

I'd add a 'mode' that can be set to an arbitrary string, which then can be
validated in C code. That seems more future proof.

Changed in the attached v2, thanks.

Christoph

Attachments:

v2-0001-Add-mode-and-flush_all-options-to-checkpoint.patchtext/x-diff; charset=us-asciiDownload
From c8975cbd1dbe5e5cae18414ea211117bc3f2c0e8 Mon Sep 17 00:00:00 2001
From: Christoph Berg <myon@debian.org>
Date: Fri, 30 May 2025 17:58:35 +0200
Subject: [PATCH v2] Add mode and flush_all 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 IMMEDIATE and SPREAD.
IMMEDIATE is the spelling used by the log_checkpoints message; FAST is
accepted as alternative spelling, it is used by pg_basebackup
--checkpoint=fast.
---
 doc/src/sgml/ref/checkpoint.sgml    | 76 ++++++++++++++++++++++++-----
 src/backend/parser/gram.y           |  8 +++
 src/backend/tcop/utility.c          | 38 ++++++++++++++-
 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, 124 insertions(+), 18 deletions(-)

diff --git a/doc/src/sgml/ref/checkpoint.sgml b/doc/src/sgml/ref/checkpoint.sgml
index db011a47d04..3014f90f9cd 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 { IMMEDIATE | FAST | SPREAD }
+    FLUSH_ALL [ <replaceable class="parameter">boolean</replaceable> ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -31,20 +36,11 @@ 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
+   log.  All data files will be flushed to disk, except for relations marked <literal>UNLOGGED</literal>.  Refer to
    <xref linkend="wal-configuration"/> for more details about what happens
    during a checkpoint.
   </para>
 
-  <para>
-   The <command>CHECKPOINT</command> command forces an immediate
-   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.
-  </para>
-
   <para>
    If executed during recovery, the <command>CHECKPOINT</command> command
    will force a restartpoint (see <xref linkend="wal-configuration"/>)
@@ -56,6 +52,64 @@ CHECKPOINT
    the <xref linkend="predefined-role-pg-checkpoint"/>
    role can call <command>CHECKPOINT</command>.
   </para>
+
+  <para>
+   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>
+ </refsect1>
+
+ <refsect1>
+  <title>Parameters</title>
+
+  <variablelist>
+   <varlistentry>
+    <term><literal>MODE</literal></term>
+    <listitem>
+     <para>
+      The <command>CHECKPOINT</command> command forces an immediate
+      checkpoint by default when the command is issued, without waiting for a
+      regular checkpoint scheduled by the system. <literal>FAST</literal> is a
+      synonym for <literal>IMMEDIATE</literal>.
+     </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_ALL</literal></term>
+    <listitem>
+     <para>
+      Requests the checkpoint to also flush data of <literal>UNLOGGED</literal>
+      relations. Defaults to 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>
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 25fe3d58016..3a08c54b46e 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -943,6 +943,11 @@ standard_ProcessUtility(PlannedStmt *pstmt,
 			break;
 
 		case T_CheckPointStmt:
+			CheckPointStmt   *stmt = (CheckPointStmt *) parsetree;
+			ListCell   *lc;
+			bool		immediate = true;
+			bool		flush_all = false;
+
 			if (!has_privs_of_role(GetUserId(), ROLE_PG_CHECKPOINT))
 				ereport(ERROR,
 						(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
@@ -952,7 +957,38 @@ standard_ProcessUtility(PlannedStmt *pstmt,
 						 errdetail("Only roles with privileges of the \"%s\" role may execute this command.",
 								   "pg_checkpoint")));
 
-			RequestCheckpoint(CHECKPOINT_IMMEDIATE | CHECKPOINT_WAIT |
+			/* 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, "immediate") == 0 || strcmp(mode, "fast") == 0)
+						immediate = true;
+					else if (strcmp(mode, "spread") == 0)
+						immediate = false;
+					else
+						ereport(ERROR,
+								(errcode(ERRCODE_SYNTAX_ERROR),
+								 errmsg("CHECKPOINT option \"%s\" argument \"%s\" is invalid", opt->defname, mode),
+								 errhint("valid arguments are \"IMMEDIATE\" (or \"FAST\") and \"SPREAD\""),
+								 parser_errposition(pstate, opt->location)));
+				}
+				else if (strcmp(opt->defname, "flush_all") == 0)
+					flush_all = defGetBoolean(opt);
+				else
+					ereport(ERROR,
+							(errcode(ERRCODE_SYNTAX_ERROR),
+							 errmsg("unrecognized CHECKPOINT option \"%s\"", opt->defname),
+							 errhint("valid options are \"IMMEDIATE\" and \"FLUSH_ALL\""),
+							 parser_errposition(pstate, opt->location)));
+			}
+
+			RequestCheckpoint(CHECKPOINT_WAIT |
+							  (immediate ? CHECKPOINT_IMMEDIATE : 0) |
+							  (flush_all ? CHECKPOINT_FLUSH_ALL : 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 ec65ab79fec..7757d576a64 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_ALL");
+	else if (Matches("CHECKPOINT", "(", "MODE"))
+		COMPLETE_WITH("IMMEDIATE", "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..d8b4bfa666e 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 immediate);
+CHECKPOINT (mode spread, flush_all);
 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..6141c6a69a9 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 immediate);
+CHECKPOINT (mode spread, flush_all);
 
 SELECT num_requested > :rqst_ckpts_before FROM pg_stat_checkpointer;
 SELECT wal_bytes > :wal_bytes_before FROM pg_stat_wal;
-- 
2.47.2

#9Fujii Masao
masao.fujii@oss.nttdata.com
In reply to: Christoph Berg (#8)
Re: CHECKPOINT unlogged data

On 2025/06/06 19:03, Christoph Berg wrote:

Re: Andres Freund

I'd add a 'mode' that can be set to an arbitrary string, which then can be
validated in C code. That seems more future proof.

Changed in the attached v2, thanks.

When I applied the patch and compiled it, I got the following warnings:

utility.c:946:4: warning: label followed by a declaration is a C23 extension [-Wc23-extensions]
946 | CheckPointStmt *stmt = (CheckPointStmt *) parsetree;
| ^
utility.c:947:16: warning: mixing declarations and code is incompatible with standards before C99 [-Wdeclaration-after-statement]
947 | ListCell *lc;
| ^
2 warnings generated.

			RequestCheckpoint(CHECKPOINT_WAIT |
+							  (immediate ? CHECKPOINT_IMMEDIATE : 0) |
+							  (flush_all ? CHECKPOINT_FLUSH_ALL : 0) |

Some users might want to trigger a spread checkpoint but not wait for
it to finish, since it could take a long time? If that's a valid use case,
maybe we should add a WAIT option to let users choose whether to wait for
the checkpoint to complete or not?

Regards,

--
Fujii Masao
NTT DATA Japan Corporation

#10Christoph Berg
myon@debian.org
In reply to: Fujii Masao (#9)
1 attachment(s)
Re: CHECKPOINT unlogged data

Re: Fujii Masao

utility.c:946:4: warning: label followed by a declaration is a C23 extension [-Wc23-extensions]

Thanks, my compiler didn't throw that. { } block added in v3.

RequestCheckpoint(CHECKPOINT_WAIT |
+							  (immediate ? CHECKPOINT_IMMEDIATE : 0) |
+							  (flush_all ? CHECKPOINT_FLUSH_ALL : 0) |

Some users might want to trigger a spread checkpoint but not wait for
it to finish, since it could take a long time? If that's a valid use case,
maybe we should add a WAIT option to let users choose whether to wait for
the checkpoint to complete or not?

Do we want that? The checkpoint is only effective when it's finished,
and running `psql -c "checkpoint (wait false)"` might make people
shoot themselves into the foot.

Christoph

Attachments:

v3-0001-Add-mode-and-flush_all-options-to-checkpoint.patchtext/x-diff; charset=us-asciiDownload
From 2f94017b467b930038a5e67cf5a59743c8bc8275 Mon Sep 17 00:00:00 2001
From: Christoph Berg <myon@debian.org>
Date: Fri, 30 May 2025 17:58:35 +0200
Subject: [PATCH v3] Add mode and flush_all 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 IMMEDIATE and SPREAD.
IMMEDIATE is the spelling used by the log_checkpoints message; FAST is
accepted as alternative spelling, it is used by pg_basebackup
--checkpoint=fast.
---
 doc/src/sgml/ref/checkpoint.sgml    | 76 ++++++++++++++++++++++++-----
 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, 136 insertions(+), 28 deletions(-)

diff --git a/doc/src/sgml/ref/checkpoint.sgml b/doc/src/sgml/ref/checkpoint.sgml
index db011a47d04..3014f90f9cd 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 { IMMEDIATE | FAST | SPREAD }
+    FLUSH_ALL [ <replaceable class="parameter">boolean</replaceable> ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -31,20 +36,11 @@ 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
+   log.  All data files will be flushed to disk, except for relations marked <literal>UNLOGGED</literal>.  Refer to
    <xref linkend="wal-configuration"/> for more details about what happens
    during a checkpoint.
   </para>
 
-  <para>
-   The <command>CHECKPOINT</command> command forces an immediate
-   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.
-  </para>
-
   <para>
    If executed during recovery, the <command>CHECKPOINT</command> command
    will force a restartpoint (see <xref linkend="wal-configuration"/>)
@@ -56,6 +52,64 @@ CHECKPOINT
    the <xref linkend="predefined-role-pg-checkpoint"/>
    role can call <command>CHECKPOINT</command>.
   </para>
+
+  <para>
+   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>
+ </refsect1>
+
+ <refsect1>
+  <title>Parameters</title>
+
+  <variablelist>
+   <varlistentry>
+    <term><literal>MODE</literal></term>
+    <listitem>
+     <para>
+      The <command>CHECKPOINT</command> command forces an immediate
+      checkpoint by default when the command is issued, without waiting for a
+      regular checkpoint scheduled by the system. <literal>FAST</literal> is a
+      synonym for <literal>IMMEDIATE</literal>.
+     </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_ALL</literal></term>
+    <listitem>
+     <para>
+      Requests the checkpoint to also flush data of <literal>UNLOGGED</literal>
+      relations. Defaults to 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>
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 25fe3d58016..8bfdff76fac 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_IMMEDIATE | CHECKPOINT_WAIT |
-							  (RecoveryInProgress() ? 0 : CHECKPOINT_FORCE));
+			{
+				CheckPointStmt   *stmt = (CheckPointStmt *) parsetree;
+				ListCell   *lc;
+				bool		immediate = true;
+				bool		flush_all = 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, "immediate") == 0 || strcmp(mode, "fast") == 0)
+							immediate = true;
+						else if (strcmp(mode, "spread") == 0)
+							immediate = false;
+						else
+							ereport(ERROR,
+									(errcode(ERRCODE_SYNTAX_ERROR),
+									 errmsg("CHECKPOINT option \"%s\" argument \"%s\" is invalid", opt->defname, mode),
+									 errhint("valid arguments are \"IMMEDIATE\" (or \"FAST\") and \"SPREAD\""),
+									 parser_errposition(pstate, opt->location)));
+					}
+					else if (strcmp(opt->defname, "flush_all") == 0)
+						flush_all = defGetBoolean(opt);
+					else
+						ereport(ERROR,
+								(errcode(ERRCODE_SYNTAX_ERROR),
+								 errmsg("unrecognized CHECKPOINT option \"%s\"", opt->defname),
+								 errhint("valid options are \"IMMEDIATE\" and \"FLUSH_ALL\""),
+								 parser_errposition(pstate, opt->location)));
+				}
+
+				RequestCheckpoint(CHECKPOINT_WAIT |
+								  (immediate ? CHECKPOINT_IMMEDIATE : 0) |
+								  (flush_all ? CHECKPOINT_FLUSH_ALL : 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 ec65ab79fec..7757d576a64 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_ALL");
+	else if (Matches("CHECKPOINT", "(", "MODE"))
+		COMPLETE_WITH("IMMEDIATE", "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..d8b4bfa666e 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 immediate);
+CHECKPOINT (mode spread, flush_all);
 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..6141c6a69a9 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 immediate);
+CHECKPOINT (mode spread, flush_all);
 
 SELECT num_requested > :rqst_ckpts_before FROM pg_stat_checkpointer;
 SELECT wal_bytes > :wal_bytes_before FROM pg_stat_wal;
-- 
2.47.2

#11Nathan Bossart
nathandbossart@gmail.com
In reply to: Christoph Berg (#10)
Re: CHECKPOINT unlogged data

On Fri, Jun 06, 2025 at 04:26:56PM +0200, Christoph Berg wrote:

Re: Fujii Masao

Some users might want to trigger a spread checkpoint but not wait for
it to finish, since it could take a long time? If that's a valid use case,
maybe we should add a WAIT option to let users choose whether to wait for
the checkpoint to complete or not?

Do we want that? The checkpoint is only effective when it's finished,
and running `psql -c "checkpoint (wait false)"` might make people
shoot themselves into the foot.

*shrug*

I imagine the documentation will pretty clearly indicate that setting WAIT
to "false" will cause CHECKPOINT to not wait for it to finish.

+    <term><literal>MODE</literal></term>
+    <listitem>
+     <para>
+      The <command>CHECKPOINT</command> command forces an immediate
+      checkpoint by default when the command is issued, without waiting for a
+      regular checkpoint scheduled by the system. <literal>FAST</literal> is a
+      synonym for <literal>IMMEDIATE</literal>.
+     </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>

I don't understand why we need to add both FAST and IMMEDIATE.

+   <varlistentry>
+    <term><literal>FLUSH_ALL</literal></term>
+    <listitem>
+     <para>
+      Requests the checkpoint to also flush data of <literal>UNLOGGED</literal>
+      relations. Defaults to off.
+     </para>
+    </listitem>
+   </varlistentry>

Could we rename this to something like FLUSH_UNLOGGED or INCLUDE_UNLOGGED?
IMHO that's more descriptive.

My attempt at this patch back in 2020 included the following note, which
seems relevant here:

+   Note that the server may consolidate concurrently requested checkpoints or
+   restartpoints.  Such consolidated requests will contain a combined set of
+   options.  For example, if one session requested an immediate checkpoint and
+   another session requested a non-immediate checkpoint, the server may combine
+   these requests and perform one immediate checkpoint.

We might also want to make sure it's clear that CHECKPOINT does nothing if
there's been no database activity since the last one (or, in the case of a
restartpoint, if there hasn't been a checkpoint record).

--
nathan

#12Christoph Berg
myon@debian.org
In reply to: Nathan Bossart (#11)
Re: CHECKPOINT unlogged data

Re: Nathan Bossart

I imagine the documentation will pretty clearly indicate that setting WAIT
to "false" will cause CHECKPOINT to not wait for it to finish.

I can add it, it's easy enough...

I don't understand why we need to add both FAST and IMMEDIATE.

We have both:

=# checkpoint ;
2025-06-06 18:09:25.743 CEST [872379] LOG: checkpoint starting: immediate force wait

pg_basebackup --checkpoint=fast

Could we settle for one official name for that? Then we could use that
name in all contexts.

+ <term><literal>FLUSH_ALL</literal></term>

Could we rename this to something like FLUSH_UNLOGGED or INCLUDE_UNLOGGED?
IMHO that's more descriptive.

That's again coming from what the log message is saying:

=# checkpoint (flush_all);
2025-06-06 18:12:46.298 CEST [873436] LOG: checkpoint starting: immediate force wait flush-all

I think we should be consistent there.

#define CHECKPOINT_FLUSH_ALL 0x0010 /* Flush all pages, including those
* belonging to unlogged tables */

Maybe CHECKPOINT_FLUSH_UNLOGGED would be more explicit?

My attempt at this patch back in 2020 included the following note, which
seems relevant here:

+   Note that the server may consolidate concurrently requested checkpoints or
+   restartpoints.  Such consolidated requests will contain a combined set of
+   options.  For example, if one session requested an immediate checkpoint and
+   another session requested a non-immediate checkpoint, the server may combine
+   these requests and perform one immediate checkpoint.

The CHECKPOINT documentation links to `28.5. WAL Configuration`,
should this be mentioned there instead?

We might also want to make sure it's clear that CHECKPOINT does nothing if
there's been no database activity since the last one (or, in the case of a
restartpoint, if there hasn't been a checkpoint record).

That's taken care of by "force":

#define CHECKPOINT_FORCE 0x0008 /* Force even if no activity */

Christoph

#13Nathan Bossart
nathandbossart@gmail.com
In reply to: Christoph Berg (#12)
Re: CHECKPOINT unlogged data

On Fri, Jun 06, 2025 at 06:20:21PM +0200, Christoph Berg wrote:

Re: Nathan Bossart

I don't understand why we need to add both FAST and IMMEDIATE.

We have both:

=# checkpoint ;
2025-06-06 18:09:25.743 CEST [872379] LOG: checkpoint starting: immediate force wait

pg_basebackup --checkpoint=fast

Could we settle for one official name for that? Then we could use that
name in all contexts.

That seems like a good idea to me. I'm tempted to say that "fast" more
accurately describes what's happening than "immediate." "Immediate" sounds
like it happens instantaneously, but it's actually just happening "fast,"
i.e., as fast as possible.

+ <term><literal>FLUSH_ALL</literal></term>

Could we rename this to something like FLUSH_UNLOGGED or INCLUDE_UNLOGGED?
IMHO that's more descriptive.

That's again coming from what the log message is saying:

=# checkpoint (flush_all);
2025-06-06 18:12:46.298 CEST [873436] LOG: checkpoint starting: immediate force wait flush-all

I think we should be consistent there.

#define CHECKPOINT_FLUSH_ALL 0x0010 /* Flush all pages, including those
* belonging to unlogged tables */

Maybe CHECKPOINT_FLUSH_UNLOGGED would be more explicit?

WFM.

My attempt at this patch back in 2020 included the following note, which
seems relevant here:

+   Note that the server may consolidate concurrently requested checkpoints or
+   restartpoints.  Such consolidated requests will contain a combined set of
+   options.  For example, if one session requested an immediate checkpoint and
+   another session requested a non-immediate checkpoint, the server may combine
+   these requests and perform one immediate checkpoint.

The CHECKPOINT documentation links to `28.5. WAL Configuration`,
should this be mentioned there instead?

I thought it would make sense to put it closer to where these options are
described, since it'll be most evident for manually-initiated checkpoints.

We might also want to make sure it's clear that CHECKPOINT does nothing if
there's been no database activity since the last one (or, in the case of a
restartpoint, if there hasn't been a checkpoint record).

That's taken care of by "force":

#define CHECKPOINT_FORCE 0x0008 /* Force even if no activity */

Oh, I see that we always specify that for CHECKPOINT commands, except for
restartpoints. IIRC even if you do specify CHECKPOINT_FORCE for a
restartpoint, it'll have no effect. It's proably worth mentioning that
case, at least.

--
nathan

#14Christoph Berg
myon@debian.org
In reply to: Nathan Bossart (#13)
Re: CHECKPOINT unlogged data

Re: Nathan Bossart

That seems like a good idea to me. I'm tempted to say that "fast" more
accurately describes what's happening than "immediate." "Immediate" sounds
like it happens instantaneously, but it's actually just happening "fast,"
i.e., as fast as possible.

Ack.

#define CHECKPOINT_FLUSH_ALL 0x0010 /* Flush all pages, including those
* belonging to unlogged tables */

Maybe CHECKPOINT_FLUSH_UNLOGGED would be more explicit?

WFM.

Do we want to change the checkpoint log message (and the new options)
only, or include the CHECKPOINT_* flags? (I would guess there aren't
many external users of these flags, but mmmv.)

I thought it would make sense to put it closer to where these options are
described, since it'll be most evident for manually-initiated checkpoints.

Ack, I'll add that.

We might also want to make sure it's clear that CHECKPOINT does nothing if
there's been no database activity since the last one (or, in the case of a
restartpoint, if there hasn't been a checkpoint record).

That's taken care of by "force":

#define CHECKPOINT_FORCE 0x0008 /* Force even if no activity */

Oh, I see that we always specify that for CHECKPOINT commands, except for
restartpoints. IIRC even if you do specify CHECKPOINT_FORCE for a
restartpoint, it'll have no effect. It's proably worth mentioning that
case, at least.

Right, will do.

Christoph

#15Nathan Bossart
nathandbossart@gmail.com
In reply to: Christoph Berg (#14)
Re: CHECKPOINT unlogged data

On Wed, Jun 11, 2025 at 03:45:46PM +0200, Christoph Berg wrote:

Do we want to change the checkpoint log message (and the new options)
only, or include the CHECKPOINT_* flags? (I would guess there aren't
many external users of these flags, but mmmv.)

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.

--
nathan

#16Christoph Berg
myon@debian.org
In reply to: Nathan Bossart (#15)
2 attachment(s)
Re: CHECKPOINT unlogged data

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

Attachments:

v4-0001-Rename-checkpoint-options-immediate-and-flush-all.patchtext/x-diff; charset=us-asciiDownload
From d3194009b9c27c5741695b8149d00ffc56ff7f8c Mon Sep 17 00:00:00 2001
From: Christoph Berg <myon@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

v4-0002-Add-mode-and-flush_unlogged-options-to-CHECKPOINT.patchtext/x-diff; charset=us-asciiDownload
From 71e4253f2c383d92639fdbeb6983c9343825e8c6 Mon Sep 17 00:00:00 2001
From: Christoph Berg <myon@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

#17Nathan Bossart
nathandbossart@gmail.com
In reply to: Christoph Berg (#16)
Re: CHECKPOINT unlogged data

On Wed, Jun 11, 2025 at 05:53:15PM +0200, Christoph Berg wrote:

Ack, done in v4.

Thanks! The overall shape of these patches looks pretty good to me. I'll
aim to give them a deeper review in the near future.

--
nathan

#18Laurenz Albe
laurenz.albe@cybertec.at
In reply to: Christoph Berg (#16)
Re: CHECKPOINT unlogged data

On Wed, 2025-06-11 at 17:53 +0200, Christoph Berg wrote:

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

I had a look at it, and I have suggestions for the documentation.

--- a/doc/src/sgml/ref/checkpoint.sgml
+++ b/doc/src/sgml/ref/checkpoint.sgml
[...]
<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>

How about the following for added clarity:

Running an explicit <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"/>).
However, it can be useful to perform an explicit checkpoint immediately
before shutting down the server or performing an online file system backup,
if you want the checkpoint implicit in these operations to be as fast as
possible. In particular, <literal>UNLOGGED</literal> table data only get
flushed to disk during a shutdown checkpoint, so you should use the option
<literal>FLUSH_UNLOGGED</literal> for an explicit checkpoint right before a
shutdown.

<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>

Oh, interesting; I wasn't aware of that. That means that running CHECKPOINT
(FLUSH_UNLOGGED) is less useful than I thought, since there is often already
a spread checkpoint running. Would it be useful to recommend that you should
run the command twice when aiming for a fast shutdown?

@@ -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>

I find this somewhat confusing; how about

<para>
The default <literal>FAST</literal> mode causes the checkpoint to be
performed as fast as possible. 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>

+   <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>

How about:

<varlistentry>
<term><literal>FLUSH_UNLOGGED</literal></term>
<listitem>
<para>
Normally, data files marked as <literal>UNLOGGED</literal> are not
flushed to disk during a checkpoint. Enabling this option will also
flush <literal>UNLOGGED</literal> relations. This option is disabled
by default.
</para>
</listitem>
</varlistentry>

Yours,
Laurenz Albe

#19Christoph Berg
myon@debian.org
In reply to: Laurenz Albe (#18)
2 attachment(s)
Re: CHECKPOINT unlogged data

Re: Laurenz Albe

How about the following for added clarity:

Running an explicit <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"/>).
However, it can be useful to perform an explicit checkpoint immediately
before shutting down the server or performing an online file system backup,
if you want the checkpoint implicit in these operations to be as fast as
possible. In particular, <literal>UNLOGGED</literal> table data only get
flushed to disk during a shutdown checkpoint, so you should use the option
<literal>FLUSH_UNLOGGED</literal> for an explicit checkpoint right before a
shutdown.

Thanks for the suggestions.

I've taken this one, but replaced "be as fast as possible" by "have to
write out less data" to avoid confusion with the FAST mode.

Oh, interesting; I wasn't aware of that. That means that running CHECKPOINT
(FLUSH_UNLOGGED) is less useful than I thought, since there is often already
a spread checkpoint running. Would it be useful to recommend that you should
run the command twice when aiming for a fast shutdown?

I spent some time digging through the code, but I'm still not entirely
sure what's happening. There are several parts to it:

1) the list of buffers to flush is determined at the beginning of the
checkpoint, so running a 2nd FLUSH_UNLOGGED checkpoint will not make
the running checkpoint write these

2) running CHECKPOINT updates the checkpoint flags in shared memory so
I think the currently running checkpoint picks "MODE FAST" up and
speeds up. (But I'm not entirely sure, the call stack is quite deep
there.)

3) running CHECKPOINT (at least when waiting for it) seems to actually
start a new checkpoint, so FLUSH_UNLOGGED should still be effective.
(See the code arount "start_cv" in checkpointer.c)

Admittedly, adding these points together raises some question marks
about the flag handling, so I would welcome clarification by someone
more knowledgeable in this area.

I find this somewhat confusing; how about
How about:

Taken unmodified, thanks!

Christoph

Attachments:

v5-0001-Rename-checkpoint-options-immediate-and-flush-all.patchtext/x-diff; charset=us-asciiDownload
From 19062d3739b3217781631dd78ed6ad0ed0df3bc5 Mon Sep 17 00:00:00 2001
From: Christoph Berg <myon@debian.org>
Date: Wed, 11 Jun 2025 16:32:23 +0200
Subject: [PATCH v5 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 47ffc0a2307..4badd87515c 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
@@ -8947,7 +8947,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.).
  *
@@ -9077,11 +9077,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

v5-0002-Add-mode-and-flush_unlogged-options-to-CHECKPOINT.patchtext/x-diff; charset=us-asciiDownload
From 8f7975a96f9864b3d62da303f02b7edc6bac1de6 Mon Sep 17 00:00:00 2001
From: Christoph Berg <myon@debian.org>
Date: Fri, 30 May 2025 17:58:35 +0200
Subject: [PATCH v5 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    | 82 ++++++++++++++++++++++++-----
 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(+), 29 deletions(-)

diff --git a/doc/src/sgml/ref/checkpoint.sgml b/doc/src/sgml/ref/checkpoint.sgml
index 10a433e4757..2e95141db91 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,32 @@ 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
-   during a checkpoint.
+   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"/>).
+   However, it can be useful to perform an explicit checkpoint immediately
+   before shutting down the server or performing an online file system backup,
+   if you want the checkpoint implicit in these operations to have to write out
+   less data.  In particular, <literal>UNLOGGED</literal> table data is
+   normally only flushed to disk during a shutdown checkpoint, so you might use
+   the option <literal>FLUSH_UNLOGGED</literal> for an explicit checkpoint right
+   before a shutdown.
   </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 +71,51 @@ CHECKPOINT
   </para>
  </refsect1>
 
+ <refsect1>
+  <title>Parameters</title>
+
+  <variablelist>
+   <varlistentry>
+    <term><literal>MODE</literal></term>
+    <listitem>
+     <para>
+      The default <literal>FAST</literal> mode causes the checkpoint to be
+      performed as fast as possible.  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>
+      Normally, data files marked as <literal>UNLOGGED</literal> are not
+      flushed to disk during a checkpoint.  Enabling this option will also
+      flush <literal>UNLOGGED</literal> relations.  This option is disabled
+      by default.
+     </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 50f53159d58..78ee7aefd92 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -2033,6 +2033,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 2c0b4f28c14..0e61e8b66b8 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 ba12678d1cb..a5497e4a602 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

#20Nathan Bossart
nathandbossart@gmail.com
In reply to: Christoph Berg (#19)
Re: CHECKPOINT unlogged data

On Mon, Jun 16, 2025 at 04:36:59PM +0200, Christoph Berg wrote:

I spent some time digging through the code, but I'm still not entirely
sure what's happening. There are several parts to it:

1) the list of buffers to flush is determined at the beginning of the
checkpoint, so running a 2nd FLUSH_UNLOGGED checkpoint will not make
the running checkpoint write these

2) running CHECKPOINT updates the checkpoint flags in shared memory so
I think the currently running checkpoint picks "MODE FAST" up and
speeds up. (But I'm not entirely sure, the call stack is quite deep
there.)

3) running CHECKPOINT (at least when waiting for it) seems to actually
start a new checkpoint, so FLUSH_UNLOGGED should still be effective.
(See the code arount "start_cv" in checkpointer.c)

Admittedly, adding these points together raises some question marks
about the flag handling, so I would welcome clarification by someone
more knowledgeable in this area.

I think you've got it right. With CHECKPOINT_WAIT set, RequestCheckpoint()
will wait for a new checkpoint to start, at which point we know that the
new flags have been seen by the checkpointer. If an immediate checkpoint
is pending, CheckpointWriteDelay() will skip sleeping in the
currently-running one, so the current checkpoint will be "upgraded" to
immediate in some sense, but IIUC there will still be another immediate
checkpoint after it completes. But AFAICT it doesn't pick up
FLUSH_UNLOGGED until the next checkpoint begins.

Another thing to note is what I mentioned earlier:

+   Note that the server may consolidate concurrently requested checkpoints or
+   restartpoints.  Such consolidated requests will contain a combined set of
+   options.  For example, if one session requested an immediate checkpoint and
+   another session requested a non-immediate checkpoint, the server may combine
+   these requests and perform one immediate checkpoint.

--
nathan

#21Christoph Berg
myon@debian.org
In reply to: Nathan Bossart (#20)
2 attachment(s)
Re: CHECKPOINT unlogged data

Re: Nathan Bossart

I think you've got it right. With CHECKPOINT_WAIT set, RequestCheckpoint()
will wait for a new checkpoint to start, at which point we know that the
new flags have been seen by the checkpointer. If an immediate checkpoint
is pending, CheckpointWriteDelay() will skip sleeping in the
currently-running one, so the current checkpoint will be "upgraded" to
immediate in some sense, but IIUC there will still be another immediate
checkpoint after it completes. But AFAICT it doesn't pick up
FLUSH_UNLOGGED until the next checkpoint begins.

Another thing to note is what I mentioned earlier:

Thanks. I now have this:

<para>
If a checkpoint is already running when a <command>CHECKPOINT</command>
is issued, a new checkpoint is queued. The server will consolidate multiple
concurrently requested checkpoints or restartpoints and merge their options.
For example, if one session requests a fast checkpoint and another session
requests a spread checkpoint, the server combines these requests and
performs one fast checkpoint. Queing a fast checkpoint will also switch a
currently running spread checkpoint to run fast.
</para>

Christoph

Attachments:

v6-0001-Rename-checkpoint-options-immediate-and-flush-all.patchtext/x-diff; charset=us-asciiDownload
From 19062d3739b3217781631dd78ed6ad0ed0df3bc5 Mon Sep 17 00:00:00 2001
From: Christoph Berg <myon@debian.org>
Date: Wed, 11 Jun 2025 16:32:23 +0200
Subject: [PATCH v6 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 47ffc0a2307..4badd87515c 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
@@ -8947,7 +8947,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.).
  *
@@ -9077,11 +9077,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

v6-0002-Add-mode-and-flush_unlogged-options-to-CHECKPOINT.patchtext/x-diff; charset=us-asciiDownload
From 7cb8e8e19783ad45ce24d3ff87a23d051e097f5d Mon Sep 17 00:00:00 2001
From: Christoph Berg <myon@debian.org>
Date: Fri, 30 May 2025 17:58:35 +0200
Subject: [PATCH v6 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    | 89 +++++++++++++++++++++++++----
 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, 148 insertions(+), 29 deletions(-)

diff --git a/doc/src/sgml/ref/checkpoint.sgml b/doc/src/sgml/ref/checkpoint.sgml
index 10a433e4757..e1caa14aa1b 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,39 @@ 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
-   during a checkpoint.
+   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"/>).
+   However, it can be useful to perform an explicit checkpoint immediately
+   before shutting down the server or performing an online file system backup,
+   if you want the checkpoint implicit in these operations to have to write out
+   less data.  In particular, <literal>UNLOGGED</literal> table data is
+   normally only flushed to disk during a shutdown checkpoint, so you might use
+   the option <literal>FLUSH_UNLOGGED</literal> for an explicit checkpoint right
+   before a shutdown.
   </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.)
+  </para>
+
+  <para>
+   If a checkpoint is already running when a <command>CHECKPOINT</command>
+   is issued, a new checkpoint is queued.  The server will consolidate multiple
+   concurrently requested checkpoints or restartpoints and merge their options.
+   For example, if one session requests a fast checkpoint and another session
+   requests a spread checkpoint, the server combines these requests and
+   performs one fast checkpoint.  Queing a fast checkpoint will also switch a
+   currently running spread checkpoint to run fast.
   </para>
 
   <para>
@@ -58,6 +78,51 @@ CHECKPOINT
   </para>
  </refsect1>
 
+ <refsect1>
+  <title>Parameters</title>
+
+  <variablelist>
+   <varlistentry>
+    <term><literal>MODE</literal></term>
+    <listitem>
+     <para>
+      The default <literal>FAST</literal> mode causes the checkpoint to be
+      performed as fast as possible.  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>
+      Normally, data files marked as <literal>UNLOGGED</literal> are not
+      flushed to disk during a checkpoint.  Enabling this option will also
+      flush <literal>UNLOGGED</literal> relations.  This option is disabled
+      by default.
+     </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 50f53159d58..78ee7aefd92 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -2033,6 +2033,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 2c0b4f28c14..0e61e8b66b8 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 ba12678d1cb..a5497e4a602 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

#22Nathan Bossart
nathandbossart@gmail.com
In reply to: Christoph Berg (#21)
5 attachment(s)
Re: CHECKPOINT unlogged data

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

Attachments:

v7-0001-rename-CHECKPOINT_FLUSH_ALL-to-CHECKPOINT_FLUSH_U.patchtext/plain; charset=us-asciiDownload
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)

v7-0002-rename-CHECKPOINT_IMMEDIATE-to-CHECKPOINT_FAST.patchtext/plain; charset=us-asciiDownload
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)

v7-0003-add-option-list-to-checkpoint-command.patchtext/plain; charset=us-asciiDownload
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)

v7-0004-add-mode-option-to-checkpoint-command.patchtext/plain; charset=us-asciiDownload
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)

v7-0005-add-flush_unlogged-option-to-checkpoint-command.patchtext/plain; charset=us-asciiDownload
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)

#23Christoph Berg
myon@debian.org
In reply to: Nathan Bossart (#22)
Re: CHECKPOINT unlogged data

Re: Nathan Bossart

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.

Ack. I was pondering if the code was growing too big there, but didn't
want to change too many things at once.

* 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.

I would have thought this already happened here.

Thoughts?

Fine with me, thanks!

Christoph

#24Nathan Bossart
nathandbossart@gmail.com
In reply to: Christoph Berg (#23)
5 attachment(s)
Re: CHECKPOINT unlogged data

Here is what I have staged for commit, which I'm planning to do on Friday.

--
nathan

Attachments:

v8-0001-Rename-CHECKPOINT_FLUSH_ALL-to-CHECKPOINT_FLUSH_U.patchtext/plain; charset=us-asciiDownload
From ffb16241a57bcbe35445754e606e47a073eedf0d Mon Sep 17 00:00:00 2001
From: Nathan Bossart <nathan@postgresql.org>
Date: Wed, 9 Jul 2025 10:05:10 -0500
Subject: [PATCH v8 1/5] Rename CHECKPOINT_FLUSH_ALL to
 CHECKPOINT_FLUSH_UNLOGGED.

The new name more accurately indicates the effects of this flag on
a requested checkpoint.  Checkpoint-related log messages (i.e.,
those controlled by the log_checkpoints configuration parameter)
will now say "flush-unlogged" instead of "flush-all", too.  This is
preparatory work for a follow-up commit that will add a
FLUSH_UNLOGGED option to the CHECKPOINT command.

Author: Christoph Berg <myon@debian.org>
Discussion: https://postgr.es/m/aDnaKTEf-0dLiEfz%40msg.df7cb.de
---
 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 a8cc6402d62..329519e95f0 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 bd68d7e0ca9..0b4b23e02a2 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)

v8-0002-Rename-CHECKPOINT_IMMEDIATE-to-CHECKPOINT_FAST.patchtext/plain; charset=us-asciiDownload
From e7722a9320c3d10507784990bb1d14d397fe488c Mon Sep 17 00:00:00 2001
From: Nathan Bossart <nathan@postgresql.org>
Date: Wed, 9 Jul 2025 11:23:57 -0500
Subject: [PATCH v8 2/5] Rename CHECKPOINT_IMMEDIATE to CHECKPOINT_FAST.

The new name more accurately indicates the effects of this flag on
a requested checkpoint.  Checkpoint-related log messages (i.e.,
those controlled by the log_checkpoints configuration parameter)
will now say "fast" instead of "immediate", too.  And references to
"immediate" checkpoints in the documentation have been updated to
say "fast" instead.  This is preparatory work for a follow-up
commit that will add a MODE option to the CHECKPOINT command.

Author: Christoph Berg <myon@debian.org>
Discussion: https://postgr.es/m/aDnaKTEf-0dLiEfz%40msg.df7cb.de
---
 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 c28aa71f570..6b327d4fd81 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -28973,7 +28973,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 329519e95f0..add4e9e9364 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).
@@ -8946,9 +8946,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
@@ -9076,11 +9075,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 0b4b23e02a2..6afdd28dba6 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 aff8510755f..a628da4b145 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)

v8-0003-Add-option-list-to-CHECKPOINT-command.patchtext/plain; charset=us-asciiDownload
From b43491d909806eb6ae67f999243f79cd95f1235e Mon Sep 17 00:00:00 2001
From: Nathan Bossart <nathan@postgresql.org>
Date: Wed, 9 Jul 2025 13:13:01 -0500
Subject: [PATCH v8 3/5] Add option list to CHECKPOINT command.

This commit adds the boilerplate code for supporting a list of
options in CHECKPOINT commands.  No actual options are supported
yet, but follow-up commits will add support for MODE and
FLUSH_UNLOGGED.  While at it, this commit refactors the code for
executing CHECKPOINT commands to its own function since it's about
to become significantly longer.

Author: Christoph Berg <myon@debian.org>
Reviewed-by: Fujii Masao <masao.fujii@oss.nttdata.com>
Discussion: https://postgr.es/m/aDnaKTEf-0dLiEfz%40msg.df7cb.de
---
 doc/src/sgml/ref/checkpoint.sgml      | 11 +++++++++-
 src/backend/parser/gram.y             |  7 ++++++
 src/backend/postmaster/checkpointer.c | 31 +++++++++++++++++++++++++++
 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, 55 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/checkpoint.sgml b/doc/src/sgml/ref/checkpoint.sgml
index 10a433e4757..fad5e982d03 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,13 @@ CHECKPOINT
   </para>
  </refsect1>
 
+ <refsect1>
+  <title>Parameters</title>
+
+  <para>
+  </para>
+ </refsect1>
+
  <refsect1>
   <title>Compatibility</title>
 
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 70a0d832a11..73345bb3c70 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..dc01f2382f1 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,35 @@ 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 a628da4b145..4c1faf5575c 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 5ba45a0bcb3..089fe367d9f 100644
--- a/src/bin/psql/tab-complete.in.c
+++ b/src/bin/psql/tab-complete.in.c
@@ -3153,6 +3153,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 28e2e8dc0fd..86a236bd58b 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -4047,6 +4047,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)

v8-0004-Add-MODE-option-to-CHECKPOINT-command.patchtext/plain; charset=us-asciiDownload
From bfba87a017b3f7d98e44387838761980fe02708f Mon Sep 17 00:00:00 2001
From: Nathan Bossart <nathan@postgresql.org>
Date: Wed, 9 Jul 2025 13:52:14 -0500
Subject: [PATCH v8 4/5] Add MODE option to CHECKPOINT command.

This option may be set to FAST (the default) to request the
checkpoint be completed as fast as possible, or SPREAD to request
the checkpoint be spread over a longer interval (based on the
checkpoint-related configuration parameters).  Note that the server
may consolidate the options for concurrently requested checkpoints.
For example, if one session requests a "fast" checkpoint and
another requests a "spread" checkpoint, the server may perform one
"fast" checkpoint.

Author: Christoph Berg <myon@debian.org>
Reviewed-by: Andres Freund <andres@anarazel.de>
Reviewed-by: Fujii Masao <masao.fujii@oss.nttdata.com>
Reviewed-by: Laurenz Albe <laurenz.albe@cybertec.at>
Discussion: https://postgr.es/m/aDnaKTEf-0dLiEfz%40msg.df7cb.de
---
 doc/src/sgml/ref/checkpoint.sgml      | 34 ++++++++++++++++++++++++---
 src/backend/postmaster/checkpointer.c | 28 ++++++++++++++++++----
 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, 75 insertions(+), 10 deletions(-)

diff --git a/doc/src/sgml/ref/checkpoint.sgml b/doc/src/sgml/ref/checkpoint.sgml
index fad5e982d03..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,8 +74,25 @@ CHECKPOINT [ ( option [, ...] ) ]
  <refsect1>
   <title>Parameters</title>
 
-  <para>
-  </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 dc01f2382f1..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,11 +988,28 @@ 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))
 		ereport(ERROR,
@@ -1003,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 089fe367d9f..a7db04efd93 100644
--- a/src/bin/psql/tab-complete.in.c
+++ b/src/bin/psql/tab-complete.in.c
@@ -3156,6 +3156,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)

v8-0005-Add-FLUSH_UNLOGGED-option-to-CHECKPOINT-command.patchtext/plain; charset=us-asciiDownload
From 8d085c3c939d6f60b7c6821b550f989a3f8e5a51 Mon Sep 17 00:00:00 2001
From: Nathan Bossart <nathan@postgresql.org>
Date: Wed, 9 Jul 2025 14:19:21 -0500
Subject: [PATCH v8 5/5] Add FLUSH_UNLOGGED option to CHECKPOINT command.

This option, which is disabled by default, can be used to request
the checkpoint also flush data files for unlogged relations.  As
with the MODE option, the server may consolidate the options for
concurrently requested checkpoints.  For example, if one session
uses (FLUSH_UNLOGGED FALSE) and another uses (FLUSH_UNLOGGED TRUE),
the server may perform one checkpoint with FLUSH_UNLOGGED enabled.

Author: Christoph Berg <myon@debian.org>
Reviewed-by: Laurenz Albe <laurenz.albe@cybertec.at>
Discussion: https://postgr.es/m/aDnaKTEf-0dLiEfz%40msg.df7cb.de
---
 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 a7db04efd93..c7e6e8b16a5 100644
--- a/src/bin/psql/tab-complete.in.c
+++ b/src/bin/psql/tab-complete.in.c
@@ -3165,7 +3165,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)

#25Christoph Berg
myon@debian.org
In reply to: Nathan Bossart (#24)
Re: CHECKPOINT unlogged data

Re: Nathan Bossart

Here is what I have staged for commit, which I'm planning to do on Friday.

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"/>).

This is somewhat hard to parse, perhaps combine the two ()() into one:

Sets checkpoint mode to fast or spread (the default is spread,
see <xref linkend="backup-lowlevel-base-backup"/>).

(patch 4)

<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"/>).

Explaining what the option does when explicitly setting the default is
confusing. We should explain the opposite case instead:

The <command>CHECKPOINT</command> command issues a fast checkpoint
by default where writes run at full speed. When literal>MODE</literal>
is set to <literal>SPREAD</literal>, writes are instead limited
by the settings in <xref linkend="runtime-config-wal-checkpoints"/>.

(patch 5)

<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.

More precise:

Normally, <command>CHECKPOINT</command> does not flush dirty buffers of
unlogged relations. This option, which is disabled by default, enables
flushing unlogged relations to disk.

Christoph

#26Fujii Masao
masao.fujii@oss.nttdata.com
In reply to: Nathan Bossart (#24)
Re: CHECKPOINT unlogged data

On 2025/07/10 4:26, Nathan Bossart wrote:

Here is what I have staged for commit, which I'm planning to do on Friday.

Thanks for updating the patches!

Regarding the 0005 patch:

-			COMPLETE_WITH("MODE");
+			COMPLETE_WITH("MODE, FLUSH_UNLOGGED");

Shouldn't that be:

COMPLETE_WITH("MODE", "FLUSH_UNLOGGED");

IOW, the two options should be separate strings, so it needs
double quotes around each.

Regards,

--
Fujii Masao
NTT DATA Japan Corporation

#27Dilip Kumar
dilipbalaut@gmail.com
In reply to: Fujii Masao (#26)
Re: CHECKPOINT unlogged data

On Thu, Jul 10, 2025 at 9:31 AM Fujii Masao <masao.fujii@oss.nttdata.com> wrote:

On 2025/07/10 4:26, Nathan Bossart wrote:

Here is what I have staged for commit, which I'm planning to do on Friday.

Thanks for updating the patches!

Regarding the 0005 patch:

-                       COMPLETE_WITH("MODE");
+                       COMPLETE_WITH("MODE, FLUSH_UNLOGGED");

Shouldn't that be:

COMPLETE_WITH("MODE", "FLUSH_UNLOGGED");

IOW, the two options should be separate strings, so it needs
double quotes around each.

I agree that it makes more sense to treat them as 2 separate strings.

--
Regards,
Dilip Kumar
Google

#28Dilip Kumar
dilipbalaut@gmail.com
In reply to: Dilip Kumar (#27)
Re: CHECKPOINT unlogged data

On Thu, Jul 10, 2025 at 9:55 AM Dilip Kumar <dilipbalaut@gmail.com> wrote:

On Thu, Jul 10, 2025 at 9:31 AM Fujii Masao <masao.fujii@oss.nttdata.com> wrote:

On 2025/07/10 4:26, Nathan Bossart wrote:

Here is what I have staged for commit, which I'm planning to do on Friday.

Thanks for updating the patches!

Regarding the 0005 patch:

-                       COMPLETE_WITH("MODE");
+                       COMPLETE_WITH("MODE, FLUSH_UNLOGGED");

Shouldn't that be:

COMPLETE_WITH("MODE", "FLUSH_UNLOGGED");

IOW, the two options should be separate strings, so it needs
double quotes around each.

I agree that it makes more sense to treat them as 2 separate strings.

I was just playing around with the wrong mode seems error code is not
correct, see below example

postgres[906701]=# CHECKPOINT ( MODE wrong);

2025-07-10 05:00:41.644 UTC [906701] ERROR: unrecognized MODE option
"mode" at character 14

2025-07-10 05:00:41.644 UTC [906701] STATEMENT: CHECKPOINT ( MODE wrong);

ERROR: 42601: unrecognized MODE option "mode"

LINE 1: CHECKPOINT ( MODE wrong);

IMHO the error should be "unrecognized MODE option "wrong" not the "mode" ?

While looking at the code it seems problem is here instead if
'opt->defname' we should use 'mode' variable.

+ else if (strcmp(mode, "fast") != 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("unrecognized MODE option \"%s\"", opt->defname),
+ parser_errposition(pstate, opt->location)));

--
Regards,
Dilip Kumar
Google

#29Dilip Kumar
dilipbalaut@gmail.com
In reply to: Dilip Kumar (#28)
1 attachment(s)
Re: CHECKPOINT unlogged data

On Thu, Jul 10, 2025 at 10:34 AM Dilip Kumar <dilipbalaut@gmail.com> wrote:

On Thu, Jul 10, 2025 at 9:55 AM Dilip Kumar <dilipbalaut@gmail.com> wrote:

On Thu, Jul 10, 2025 at 9:31 AM Fujii Masao <masao.fujii@oss.nttdata.com> wrote:

On 2025/07/10 4:26, Nathan Bossart wrote:

Here is what I have staged for commit, which I'm planning to do on Friday.

Thanks for updating the patches!

Regarding the 0005 patch:

-                       COMPLETE_WITH("MODE");
+                       COMPLETE_WITH("MODE, FLUSH_UNLOGGED");

Shouldn't that be:

COMPLETE_WITH("MODE", "FLUSH_UNLOGGED");

IOW, the two options should be separate strings, so it needs
double quotes around each.

I agree that it makes more sense to treat them as 2 separate strings.

I was just playing around with the wrong mode seems error code is not
correct, see below example

postgres[906701]=# CHECKPOINT ( MODE wrong);

2025-07-10 05:00:41.644 UTC [906701] ERROR: unrecognized MODE option
"mode" at character 14

2025-07-10 05:00:41.644 UTC [906701] STATEMENT: CHECKPOINT ( MODE wrong);

ERROR: 42601: unrecognized MODE option "mode"

LINE 1: CHECKPOINT ( MODE wrong);

IMHO the error should be "unrecognized MODE option "wrong" not the "mode" ?

While looking at the code it seems problem is here instead if
'opt->defname' we should use 'mode' variable.

+ else if (strcmp(mode, "fast") != 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("unrecognized MODE option \"%s\"", opt->defname),
+ parser_errposition(pstate, opt->location)));

Attached fixup patch, fixes this issue as well as there is wrong
suggestion in tab completion, currently it suggest "CHECKPOINT (MODE,
FLUSH_UNLOGGED )" which is wrong, so this would be fixed what Fuji
suggested upthread to make them a 2 separate stings.

--
Regards,
Dilip Kumar
Google

Attachments:

fixup.patchapplication/octet-stream; name=fixup.patchDownload
diff --git a/src/backend/postmaster/checkpointer.c b/src/backend/postmaster/checkpointer.c
index a3e4e70ad66..2809e298a44 100644
--- a/src/backend/postmaster/checkpointer.c
+++ b/src/backend/postmaster/checkpointer.c
@@ -1002,7 +1002,7 @@ ExecCheckpoint(ParseState *pstate, CheckPointStmt *stmt)
 			else if (strcmp(mode, "fast") != 0)
 				ereport(ERROR,
 						(errcode(ERRCODE_SYNTAX_ERROR),
-						 errmsg("unrecognized MODE option \"%s\"", opt->defname),
+						 errmsg("unrecognized MODE option \"%s\"", mode),
 						 parser_errposition(pstate, opt->location)));
 		}
 		else if (strcmp(opt->defname, "flush_unlogged") == 0)
diff --git a/src/bin/psql/tab-complete.in.c b/src/bin/psql/tab-complete.in.c
index 500483121c8..69a87ab4118 100644
--- a/src/bin/psql/tab-complete.in.c
+++ b/src/bin/psql/tab-complete.in.c
@@ -3152,7 +3152,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, FLUSH_UNLOGGED");
+			COMPLETE_WITH("MODE", "FLUSH_UNLOGGED");
 		else if (TailMatches("MODE"))
 			COMPLETE_WITH("FAST", "SPREAD");
 	}
#30Nathan Bossart
nathandbossart@gmail.com
In reply to: Dilip Kumar (#29)
5 attachment(s)
Re: CHECKPOINT unlogged data

Thanks all for the feedback. Here is an updated patch set.

--
nathan

Attachments:

v9-0001-Rename-CHECKPOINT_FLUSH_ALL-to-CHECKPOINT_FLUSH_U.patchtext/plain; charset=us-asciiDownload
From d862592acfb6470e34da9e993ff942c48cbf4e2f Mon Sep 17 00:00:00 2001
From: Nathan Bossart <nathan@postgresql.org>
Date: Wed, 9 Jul 2025 10:05:10 -0500
Subject: [PATCH v9 1/5] Rename CHECKPOINT_FLUSH_ALL to
 CHECKPOINT_FLUSH_UNLOGGED.

The new name more accurately indicates the effects of this flag on
a requested checkpoint.  Checkpoint-related log messages (i.e.,
those controlled by the log_checkpoints configuration parameter)
will now say "flush-unlogged" instead of "flush-all", too.  This is
preparatory work for a follow-up commit that will add a
FLUSH_UNLOGGED option to the CHECKPOINT command.

Author: Christoph Berg <myon@debian.org>
Discussion: https://postgr.es/m/aDnaKTEf-0dLiEfz%40msg.df7cb.de
---
 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 a8cc6402d62..329519e95f0 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 bd68d7e0ca9..0b4b23e02a2 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)

v9-0002-Rename-CHECKPOINT_IMMEDIATE-to-CHECKPOINT_FAST.patchtext/plain; charset=us-asciiDownload
From 85a80fe3daac63f6d3bc0014c6aed9a154815253 Mon Sep 17 00:00:00 2001
From: Nathan Bossart <nathan@postgresql.org>
Date: Wed, 9 Jul 2025 11:23:57 -0500
Subject: [PATCH v9 2/5] Rename CHECKPOINT_IMMEDIATE to CHECKPOINT_FAST.

The new name more accurately indicates the effects of this flag on
a requested checkpoint.  Checkpoint-related log messages (i.e.,
those controlled by the log_checkpoints configuration parameter)
will now say "fast" instead of "immediate", too.  And references to
"immediate" checkpoints in the documentation have been updated to
say "fast" instead.  This is preparatory work for a follow-up
commit that will add a MODE option to the CHECKPOINT command.

Author: Christoph Berg <myon@debian.org>
Discussion: https://postgr.es/m/aDnaKTEf-0dLiEfz%40msg.df7cb.de
---
 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           |  3 ++-
 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, 40 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 c28aa71f570..6b327d4fd81 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -28973,7 +28973,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..fecee08b0a5 100644
--- a/doc/src/sgml/ref/pg_basebackup.sgml
+++ b/doc/src/sgml/ref/pg_basebackup.sgml
@@ -500,8 +500,9 @@ 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
         (see <xref linkend="backup-lowlevel-base-backup"/>).
+        The default is spread.
        </para>
       </listitem>
      </varlistentry>
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 329519e95f0..add4e9e9364 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).
@@ -8946,9 +8946,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
@@ -9076,11 +9075,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 0b4b23e02a2..6afdd28dba6 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 aff8510755f..a628da4b145 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)

v9-0003-Add-option-list-to-CHECKPOINT-command.patchtext/plain; charset=us-asciiDownload
From ea9662ffbef53e0d26b49065630eea02db3707ba Mon Sep 17 00:00:00 2001
From: Nathan Bossart <nathan@postgresql.org>
Date: Wed, 9 Jul 2025 13:13:01 -0500
Subject: [PATCH v9 3/5] Add option list to CHECKPOINT command.

This commit adds the boilerplate code for supporting a list of
options in CHECKPOINT commands.  No actual options are supported
yet, but follow-up commits will add support for MODE and
FLUSH_UNLOGGED.  While at it, this commit refactors the code for
executing CHECKPOINT commands to its own function since it's about
to become significantly longer.

Author: Christoph Berg <myon@debian.org>
Reviewed-by: Fujii Masao <masao.fujii@oss.nttdata.com>
Discussion: https://postgr.es/m/aDnaKTEf-0dLiEfz%40msg.df7cb.de
---
 doc/src/sgml/ref/checkpoint.sgml      | 11 +++++++++-
 src/backend/parser/gram.y             |  7 ++++++
 src/backend/postmaster/checkpointer.c | 31 +++++++++++++++++++++++++++
 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 ++
 src/test/regress/expected/stats.out   |  6 ++++++
 src/test/regress/sql/stats.sql        |  3 +++
 9 files changed, 64 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/checkpoint.sgml b/doc/src/sgml/ref/checkpoint.sgml
index 10a433e4757..fad5e982d03 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,13 @@ CHECKPOINT
   </para>
  </refsect1>
 
+ <refsect1>
+  <title>Parameters</title>
+
+  <para>
+  </para>
+ </refsect1>
+
  <refsect1>
   <title>Compatibility</title>
 
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 70a0d832a11..73345bb3c70 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..dc01f2382f1 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,35 @@ 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 a628da4b145..4c1faf5575c 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 5ba45a0bcb3..089fe367d9f 100644
--- a/src/bin/psql/tab-complete.in.c
+++ b/src/bin/psql/tab-complete.in.c
@@ -3153,6 +3153,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 28e2e8dc0fd..86a236bd58b 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -4047,6 +4047,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);
 
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 776f1ad0e53..9b865ae5f6c 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -926,6 +926,12 @@ 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.
+--
+-- While at it, test checkpoint options.
+CHECKPOINT (WRONG);
+ERROR:  unrecognized CHECKPOINT option "wrong"
+LINE 1: CHECKPOINT (WRONG);
+                    ^
 CHECKPOINT;
 CHECKPOINT;
 SELECT num_requested > :rqst_ckpts_before FROM pg_stat_checkpointer;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 232ab8db8fa..97b50926aa6 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -439,6 +439,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.
+--
+-- While at it, test checkpoint options.
+CHECKPOINT (WRONG);
 CHECKPOINT;
 CHECKPOINT;
 
-- 
2.39.5 (Apple Git-154)

v9-0004-Add-MODE-option-to-CHECKPOINT-command.patchtext/plain; charset=us-asciiDownload
From c7365281d57cfb3428ffba6ae1b7a1ada42e5ebc Mon Sep 17 00:00:00 2001
From: Nathan Bossart <nathan@postgresql.org>
Date: Thu, 10 Jul 2025 10:30:35 -0500
Subject: [PATCH v9 4/5] Add MODE option to CHECKPOINT command.

This option may be set to FAST (the default) to request the
checkpoint be completed as fast as possible, or SPREAD to request
the checkpoint be spread over a longer interval (based on the
checkpoint-related configuration parameters).  Note that the server
may consolidate the options for concurrently requested checkpoints.
For example, if one session requests a "fast" checkpoint and
another requests a "spread" checkpoint, the server may perform one
"fast" checkpoint.

Author: Christoph Berg <myon@debian.org>
Reviewed-by: Andres Freund <andres@anarazel.de>
Reviewed-by: Fujii Masao <masao.fujii@oss.nttdata.com>
Reviewed-by: Laurenz Albe <laurenz.albe@cybertec.at>
Reviewed-by: Dilip Kumar <dilipbalaut@gmail.com>
Discussion: https://postgr.es/m/aDnaKTEf-0dLiEfz%40msg.df7cb.de
---
 doc/src/sgml/ref/checkpoint.sgml      | 35 ++++++++++++++++++++++++---
 src/backend/postmaster/checkpointer.c | 28 +++++++++++++++++----
 src/bin/psql/tab-complete.in.c        | 13 ++++++++++
 src/test/regress/expected/stats.out   |  9 +++++--
 src/test/regress/sql/stats.sql        |  6 +++--
 5 files changed, 79 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/checkpoint.sgml b/doc/src/sgml/ref/checkpoint.sgml
index fad5e982d03..36a9e323f44 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,14 +41,24 @@ CHECKPOINT [ ( option [, ...] ) ]
   </para>
 
   <para>
-   The <command>CHECKPOINT</command> command forces a fast
+   By 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"/>).
+   To request the checkpoint be spread over a longer interval, set the
+   <literal>MODE</literal> option to <literal>SPREAD</literal>.
    <command>CHECKPOINT</command> is not intended for use during normal
    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,8 +75,25 @@ CHECKPOINT [ ( option [, ...] ) ]
  <refsect1>
   <title>Parameters</title>
 
-  <para>
-  </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 dc01f2382f1..9d77269a374 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,11 +988,28 @@ 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\"", mode),
+						 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))
 		ereport(ERROR,
@@ -1003,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 089fe367d9f..a7db04efd93 100644
--- a/src/bin/psql/tab-complete.in.c
+++ b/src/bin/psql/tab-complete.in.c
@@ -3156,6 +3156,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 9b865ae5f6c..b4df9ad5960 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -927,12 +927,17 @@ DROP TABLE test_stats_temp;
 -- of the checkpoint. But after a second checkpoint we'll see at least the
 -- results of the first.
 --
--- While at it, test checkpoint options.
+-- While at it, test checkpoint options.  Note that we don't test MODE SPREAD
+-- because it would prolong the test.
 CHECKPOINT (WRONG);
 ERROR:  unrecognized CHECKPOINT option "wrong"
 LINE 1: CHECKPOINT (WRONG);
                     ^
-CHECKPOINT;
+CHECKPOINT (MODE WRONG);
+ERROR:  unrecognized MODE option "wrong"
+LINE 1: CHECKPOINT (MODE WRONG);
+                    ^
+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 97b50926aa6..0868b250a64 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -440,9 +440,11 @@ DROP TABLE test_stats_temp;
 -- of the checkpoint. But after a second checkpoint we'll see at least the
 -- results of the first.
 --
--- While at it, test checkpoint options.
+-- While at it, test checkpoint options.  Note that we don't test MODE SPREAD
+-- because it would prolong the test.
 CHECKPOINT (WRONG);
-CHECKPOINT;
+CHECKPOINT (MODE WRONG);
+CHECKPOINT (MODE FAST);
 CHECKPOINT;
 
 SELECT num_requested > :rqst_ckpts_before FROM pg_stat_checkpointer;
-- 
2.39.5 (Apple Git-154)

v9-0005-Add-FLUSH_UNLOGGED-option-to-CHECKPOINT-command.patchtext/plain; charset=us-asciiDownload
From 0269d61a12e604bc7679d8724e0924a4be2e2cc1 Mon Sep 17 00:00:00 2001
From: Nathan Bossart <nathan@postgresql.org>
Date: Thu, 10 Jul 2025 10:47:55 -0500
Subject: [PATCH v9 5/5] Add FLUSH_UNLOGGED option to CHECKPOINT command.

This option, which is disabled by default, can be used to request
the checkpoint also flush data files for unlogged relations.  As
with the MODE option, the server may consolidate the options for
concurrently requested checkpoints.  For example, if one session
uses (FLUSH_UNLOGGED FALSE) and another uses (FLUSH_UNLOGGED TRUE),
the server may perform one checkpoint with FLUSH_UNLOGGED enabled.

Author: Christoph Berg <myon@debian.org>
Reviewed-by: Laurenz Albe <laurenz.albe@cybertec.at>
Reviewed-by: Fujii Masao <masao.fujii@oss.nttdata.com>
Reviewed-by: Dilip Kumar <dilipbalaut@gmail.com>
Discussion: https://postgr.es/m/aDnaKTEf-0dLiEfz%40msg.df7cb.de
---
 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 36a9e323f44..cd981cf2cab 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>
@@ -76,6 +77,17 @@ CHECKPOINT [ ( option [, ...] ) ]
   <title>Parameters</title>
 
   <variablelist>
+   <varlistentry>
+    <term><literal>FLUSH_UNLOGGED</literal></term>
+    <listitem>
+     <para>
+      Normally, <command>CHECKPOINT</command> does not flush dirty buffers of
+      unlogged relations.  This option, which is disabled by default, enables
+      flushing unlogged relations to disk.
+     </para>
+    </listitem>
+   </varlistentry>
+
    <varlistentry>
     <term><literal>MODE</literal></term>
     <listitem>
@@ -93,6 +105,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 9d77269a374..2809e298a44 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\"", mode),
 						 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 a7db04efd93..6872653c6c8 100644
--- a/src/bin/psql/tab-complete.in.c
+++ b/src/bin/psql/tab-complete.in.c
@@ -3165,7 +3165,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 b4df9ad5960..b9dd5317163 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -938,7 +938,7 @@ ERROR:  unrecognized MODE option "wrong"
 LINE 1: CHECKPOINT (MODE WRONG);
                     ^
 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 0868b250a64..3c6837c5c94 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -445,7 +445,7 @@ DROP TABLE test_stats_temp;
 CHECKPOINT (WRONG);
 CHECKPOINT (MODE WRONG);
 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)

#31Dilip Kumar
dilipbalaut@gmail.com
In reply to: Nathan Bossart (#30)
1 attachment(s)
Re: CHECKPOINT unlogged data

On Thu, Jul 10, 2025 at 9:33 PM Nathan Bossart <nathandbossart@gmail.com> wrote:

Thanks all for the feedback. Here is an updated patch set.

Thanks now, looks good to me. Additionally IMHO it would be good to
add tests with FLUSH_UNLOGGED TRUE and FLUSH_UNLOGGED FALSE as well, I
have added a simple 2 test for the same in attached file.

--
Regards,
Dilip Kumar
Google

Attachments:

extra_test.patchapplication/octet-stream; name=extra_test.patchDownload
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index b9dd5317163..815ae622da1 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -939,6 +939,8 @@ LINE 1: CHECKPOINT (MODE WRONG);
                     ^
 CHECKPOINT (MODE FAST);
 CHECKPOINT (FLUSH_UNLOGGED);
+CHECKPOINT (FLUSH_UNLOGGED TRUE);
+CHECKPOINT (FLUSH_UNLOGGED FALSE);
 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 3c6837c5c94..9d206aa901f 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -446,6 +446,8 @@ CHECKPOINT (WRONG);
 CHECKPOINT (MODE WRONG);
 CHECKPOINT (MODE FAST);
 CHECKPOINT (FLUSH_UNLOGGED);
+CHECKPOINT (FLUSH_UNLOGGED TRUE);
+CHECKPOINT (FLUSH_UNLOGGED FALSE);
 
 SELECT num_requested > :rqst_ckpts_before FROM pg_stat_checkpointer;
 SELECT wal_bytes > :wal_bytes_before FROM pg_stat_wal;
#32Nathan Bossart
nathandbossart@gmail.com
In reply to: Dilip Kumar (#31)
Re: CHECKPOINT unlogged data

On Fri, Jul 11, 2025 at 08:43:05AM +0530, Dilip Kumar wrote:

Thanks now, looks good to me.

Thanks for reviewing.

Additionally IMHO it would be good to add tests with FLUSH_UNLOGGED TRUE
and FLUSH_UNLOGGED FALSE as well, I have added a simple 2 test for the
same in attached file.

I'll add something like this before committing. In the future, please do
not post small add-on patches like this because it confuses cfbot. If you
must, I think appending an unknown extension like ".nocfbot" to the file
name is sufficient to prevent cfbot from picking it up.

--
nathan

#33Dilip Kumar
dilipbalaut@gmail.com
In reply to: Nathan Bossart (#32)
Re: CHECKPOINT unlogged data

On Fri, 11 Jul 2025 at 8:27 PM, Nathan Bossart <nathandbossart@gmail.com>
wrote:

On Fri, Jul 11, 2025 at 08:43:05AM +0530, Dilip Kumar wrote:

Thanks now, looks good to me.

Thanks for reviewing.

Additionally IMHO it would be good to add tests with FLUSH_UNLOGGED TRUE
and FLUSH_UNLOGGED FALSE as well, I have added a simple 2 test for the
same in attached file.

I'll add something like this before committing. In the future, please do
not post small add-on patches like this because it confuses cfbot. If you
must, I think appending an unknown extension like ".nocfbot" to the file
name is sufficient to prevent cfbot from picking it up.

Yeah right, I somehow missed that part.. will take care in future. Thanks.

Show quoted text
#34Nathan Bossart
nathandbossart@gmail.com
In reply to: Dilip Kumar (#33)
Re: CHECKPOINT unlogged data

Committed.

--
nathan