v16-0001-Add-FDW-functions-for-importing-optimizer-statis.patch

text/x-patch

Filename: v16-0001-Add-FDW-functions-for-importing-optimizer-statis.patch
Type: text/x-patch
Part: 0
Message: Re: Import Statistics in postgres_fdw before resorting to sampling.

Patch

Same data as JSON: GET /api/v1/attachments/:id/patch the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes. API reference →
Format: format-patch
Series: patch v16-0001
Subject: Add FDW functions for importing optimizer statistics.
File+
src/backend/commands/analyze.c 48 20
src/include/foreign/fdwapi.h 8 0
From 3aff9ca0887944fac79f484c82c1354634d43a70 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 v16 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 eeed91be266..a11067ed31e 100644
--- a/src/backend/commands/analyze.c
+++ b/src/backend/commands/analyze.c
@@ -114,6 +114,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)
@@ -196,27 +199,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)
 	{
@@ -248,10 +240,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: 3b4c2b9db25ee1c51e5133da529668a37aa02fc3
-- 
2.53.0