diff --git a/doc/src/sgml/ref/cluster.sgml b/doc/src/sgml/ref/cluster.sgml
index 339990f..d6c1d84 100644
--- a/doc/src/sgml/ref/cluster.sgml
+++ b/doc/src/sgml/ref/cluster.sgml
@@ -21,8 +21,8 @@ PostgreSQL documentation
 
  <refsynopsisdiv>
 <synopsis>
-CLUSTER [VERBOSE] <replaceable class="PARAMETER">table_name</replaceable> [ USING <replaceable class="PARAMETER">index_name</replaceable> ]
-CLUSTER [VERBOSE]
+CLUSTER [FREEZE] [VERBOSE] <replaceable class="PARAMETER">table_name</replaceable> [ USING <replaceable class="PARAMETER">index_name</replaceable> ]
+CLUSTER [FREEZE] [VERBOSE]
 </synopsis>
  </refsynopsisdiv>
 
@@ -100,6 +100,19 @@ CLUSTER [VERBOSE]
    </varlistentry>
 
    <varlistentry>
+    <term><literal>FREEZE</literal></term>
+    <listitem>
+     <para>
+      Selects aggressive <quote>freezing</quote> of tuples.
+      Specifying <literal>FREEZE</literal> is equivalent to performing
+      <command>CLUSTER</command> with the
+      <xref linkend="guc-vacuum-freeze-min-age"> parameter
+      set to zero.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
     <term><literal>VERBOSE</literal></term>
     <listitem>
      <para>
diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c
index f6a5bfe..aa4b85a 100644
--- a/src/backend/commands/cluster.c
+++ b/src/backend/commands/cluster.c
@@ -177,7 +177,7 @@ cluster(ClusterStmt *stmt, bool isTopLevel)
 		heap_close(rel, NoLock);
 
 		/* Do the job */
-		cluster_rel(tableOid, indexOid, false, stmt->verbose, -1, -1);
+		cluster_rel(tableOid, indexOid, false, stmt->verbose, stmt->freeze_min_age, stmt->freeze_table_age);
 	}
 	else
 	{
@@ -227,7 +227,7 @@ cluster(ClusterStmt *stmt, bool isTopLevel)
 			/* functions in indexes may want a snapshot set */
 			PushActiveSnapshot(GetTransactionSnapshot());
 			cluster_rel(rvtc->tableOid, rvtc->indexOid, true, stmt->verbose,
-						-1, -1);
+						stmt->freeze_min_age, stmt->freeze_table_age);
 			PopActiveSnapshot();
 			CommitTransactionCommand();
 		}
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 65f3b98..16dd7a8 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -2711,6 +2711,8 @@ _copyClusterStmt(const ClusterStmt *from)
 	ClusterStmt *newnode = makeNode(ClusterStmt);
 
 	COPY_NODE_FIELD(relation);
+	COPY_SCALAR_FIELD(freeze_min_age);
+	COPY_SCALAR_FIELD(freeze_table_age);
 	COPY_STRING_FIELD(indexname);
 	COPY_SCALAR_FIELD(verbose);
 
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 4c9b05e..94e9ab6 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -1072,6 +1072,8 @@ static bool
 _equalClusterStmt(const ClusterStmt *a, const ClusterStmt *b)
 {
 	COMPARE_NODE_FIELD(relation);
+	COMPARE_SCALAR_FIELD(freeze_min_age);
+	COMPARE_SCALAR_FIELD(freeze_table_age);
 	COMPARE_STRING_FIELD(indexname);
 	COMPARE_SCALAR_FIELD(verbose);
 
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 363c603..d9504ba 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -8456,36 +8456,42 @@ CreateConversionStmt:
 /*****************************************************************************
  *
  *		QUERY:
- *				CLUSTER [VERBOSE] <qualified_name> [ USING <index_name> ]
- *				CLUSTER [VERBOSE]
- *				CLUSTER [VERBOSE] <index_name> ON <qualified_name> (for pre-8.3)
+ *				CLUSTER [FREEZE] [VERBOSE] <qualified_name> [ USING <index_name> ]
+ *				CLUSTER [FREEZE] [VERBOSE]
+ *				CLUSTER [FREEZE] [VERBOSE] <index_name> ON <qualified_name> (for pre-8.3)
  *
  *****************************************************************************/
 
 ClusterStmt:
-			CLUSTER opt_verbose qualified_name cluster_index_specification
+			CLUSTER opt_freeze opt_verbose qualified_name cluster_index_specification
 				{
 					ClusterStmt *n = makeNode(ClusterStmt);
-					n->relation = $3;
-					n->indexname = $4;
-					n->verbose = $2;
+					n->relation = $4;
+					n->freeze_min_age = $2 ? 0 : -1;
+					n->freeze_table_age = $2 ? 0 : -1;
+					n->indexname = $5;
+					n->verbose = $3;
 					$$ = (Node*)n;
 				}
-			| CLUSTER opt_verbose
+			| CLUSTER opt_freeze opt_verbose
 				{
 					ClusterStmt *n = makeNode(ClusterStmt);
 					n->relation = NULL;
+					n->freeze_min_age = $2 ? 0 : -1;
+					n->freeze_table_age = $2 ? 0 : -1;
 					n->indexname = NULL;
-					n->verbose = $2;
+					n->verbose = $3;
 					$$ = (Node*)n;
 				}
 			/* kept for pre-8.3 compatibility */
-			| CLUSTER opt_verbose index_name ON qualified_name
+			| CLUSTER opt_freeze opt_verbose index_name ON qualified_name
 				{
 					ClusterStmt *n = makeNode(ClusterStmt);
-					n->relation = $5;
-					n->indexname = $3;
-					n->verbose = $2;
+					n->relation = $6;
+					n->freeze_min_age = $2 ? 0 : -1;
+					n->freeze_table_age = $2 ? 0 : -1;
+					n->indexname = $4;
+					n->verbose = $3;
 					$$ = (Node*)n;
 				}
 		;
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index e5235cb..d5b43a0 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -2441,6 +2441,8 @@ typedef struct DropdbStmt
 typedef struct ClusterStmt
 {
 	NodeTag		type;
+	int			freeze_min_age; /* min freeze age, or -1 to use default */
+	int			freeze_table_age;		/* age at which to scan whole table */
 	RangeVar   *relation;		/* relation being indexed, or NULL if all */
 	char	   *indexname;		/* original index defined */
 	bool		verbose;		/* print progress info */
