From 7b695148065be8d3456ebb0af92cf3f93fc6ae10 Mon Sep 17 00:00:00 2001
From: Corey Huinker <corey.huinker@gmail.com>
Date: Sat, 17 Jan 2026 16:39:45 -0500
Subject: [PATCH v10 1/3] Add FDW functions for importing optimizer statistics.

Add the the function StatisticsAreImportable which is used as a
lightweight preliminary scan to determine if the remote table could
potentially have statistics that could be imported instead of fetching a
row sample from the remote table, which can be expensive.

Also add the function ImportStatistics which attempts to actually fetch
those statistics from the remote server, and if successful import them
into the local statistics catalog.
---
 src/include/foreign/fdwapi.h   |  8 ++++
 src/backend/commands/analyze.c | 68 ++++++++++++++++++++++++----------
 2 files changed, 56 insertions(+), 20 deletions(-)

diff --git a/src/include/foreign/fdwapi.h b/src/include/foreign/fdwapi.h
index 96b6f692d2a..59beeae4f45 100644
--- a/src/include/foreign/fdwapi.h
+++ b/src/include/foreign/fdwapi.h
@@ -157,6 +157,12 @@ typedef bool (*AnalyzeForeignTable_function) (Relation relation,
 											  AcquireSampleRowsFunc *func,
 											  BlockNumber *totalpages);
 
+typedef bool (*StatisticsAreImportable_function)  (Relation relation);
+
+typedef bool (*ImportStatistics_function) (Relation relation,
+										   List *va_cols,
+										   int elevel);
+
 typedef List *(*ImportForeignSchema_function) (ImportForeignSchemaStmt *stmt,
 											   Oid serverOid);
 
@@ -255,6 +261,8 @@ typedef struct FdwRoutine
 
 	/* Support functions for ANALYZE */
 	AnalyzeForeignTable_function AnalyzeForeignTable;
+	StatisticsAreImportable_function StatisticsAreImportable;
+	ImportStatistics_function ImportStatistics;
 
 	/* Support functions for IMPORT FOREIGN SCHEMA */
 	ImportForeignSchema_function ImportForeignSchema;
diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c
index a483424152c..c84367461fb 100644
--- a/src/backend/commands/analyze.c
+++ b/src/backend/commands/analyze.c
@@ -113,6 +113,9 @@ analyze_rel(Oid relid, RangeVar *relation,
 	int			elevel;
 	AcquireSampleRowsFunc acquirefunc = NULL;
 	BlockNumber relpages = 0;
+	FdwRoutine *fdwroutine = NULL;
+	bool		can_import_stats = false;
+	bool		stats_imported = false;
 
 	/* Select logging level */
 	if (params.options & VACOPT_VERBOSE)
@@ -195,27 +198,16 @@ analyze_rel(Oid relid, RangeVar *relation,
 	else if (onerel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
 	{
 		/*
-		 * For a foreign table, call the FDW's hook function to see whether it
-		 * supports analysis.
+		 * For a foreign table, call the FDW's hook functions to see whether
+		 * it supports statistics import or analysis.
 		 */
-		FdwRoutine *fdwroutine;
-		bool		ok = false;
 
 		fdwroutine = GetFdwRoutineForRelation(onerel, false);
 
-		if (fdwroutine->AnalyzeForeignTable != NULL)
-			ok = fdwroutine->AnalyzeForeignTable(onerel,
-												 &acquirefunc,
-												 &relpages);
-
-		if (!ok)
-		{
-			ereport(WARNING,
-					(errmsg("skipping \"%s\" --- cannot analyze this foreign table",
-							RelationGetRelationName(onerel))));
-			relation_close(onerel, ShareUpdateExclusiveLock);
-			return;
-		}
+		if (fdwroutine->ImportStatistics != NULL &&
+			fdwroutine->StatisticsAreImportable != NULL &&
+			fdwroutine->StatisticsAreImportable(onerel))
+			can_import_stats = true;
 	}
 	else if (onerel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
 	{
@@ -247,10 +239,46 @@ analyze_rel(Oid relid, RangeVar *relation,
 									 PROGRESS_ANALYZE_STARTED_BY_MANUAL);
 
 	/*
-	 * Do the normal non-recursive ANALYZE.  We can skip this for partitioned
-	 * tables, which don't contain any rows.
+	 * If this table can import statistics, attempt to do so.
 	 */
-	if (onerel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
+	if (can_import_stats)
+	{
+		Assert(onerel->rd_rel->relkind == RELKIND_FOREIGN_TABLE);
+		Assert(fdwroutine != NULL);
+		Assert(fdwroutine->ImportStatistics != NULL);
+		stats_imported = fdwroutine->ImportStatistics(onerel, va_cols, elevel);
+	}
+
+	/*
+	 * Foreign tables that were not able to import stats will resort to
+	 * regular sampling.
+	 */
+	if ((onerel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
+		&& !stats_imported)
+	{
+		bool		ok = false;
+
+		if (fdwroutine->AnalyzeForeignTable != NULL)
+			ok = fdwroutine->AnalyzeForeignTable(onerel,
+												 &acquirefunc,
+												 &relpages);
+
+		if (!ok)
+		{
+			ereport(WARNING,
+					errmsg("skipping \"%s\" -- cannot analyze this foreign table.",
+						   RelationGetRelationName(onerel)));
+			relation_close(onerel, ShareUpdateExclusiveLock);
+			return;
+		}
+	}
+
+	/*
+	 * Do the normal non-recursive ANALYZE.  We can skip this for partitioned
+	 * tables and foreign tables that successfully imported statistics.
+	 */
+	if ((onerel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
+		&& !stats_imported)
 		do_analyze_rel(onerel, params, va_cols, acquirefunc,
 					   relpages, false, in_outer_xact, elevel);
 

base-commit: 6831cd9e3b082d7b830c3196742dd49e3540c49b
-- 
2.52.0

