From 1d2a58ea79956e8aafd4d7d481bdef1e63b5caab Mon Sep 17 00:00:00 2001 From: Corey Huinker Date: Sat, 17 Jan 2026 16:39:45 -0500 Subject: [PATCH v13 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: 88327092ff06c48676d2a603420089bf493770f3 -- 2.53.0