From aff24e106b88de903f4a1b563485a7adee9438ad Mon Sep 17 00:00:00 2001 From: Corey Huinker Date: Sat, 17 Jan 2026 16:39:45 -0500 Subject: [PATCH v14 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 53adac9139b..f175c0d8ed7 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: 0123ce131fca454009439dfa3b2266d1d40737d7 -- 2.53.0