From 3e44c8a441f34117b3e6369fadfcbc94c7ae537d Mon Sep 17 00:00:00 2001 From: Etsuro Fujita Date: Mon, 14 Sep 2026 19:00:01 +0900 Subject: [PATCH] postgres_fdw: Disable attribute statistics import from pre-9.1 servers. To import attribute statistics, postgres_fdw sent a query wity COLLATE "C" to remote servers without checking their version. Since COLLATE is only supported since v9.1, this could break the import against older servers; fix by modifying postgres_fdw to check their version and if older, fall back to sampling. (This commit still allows importing only relation statistics from such an older server.) Also, expand the documentation to mention that attribute statistics are stored into the local server without applying the foreign table columns' n_distinct option. Oversight in commit 28972b6fc. Reported-by: Fujii Masao Reported-by: Osama Abdul Qader Author: Etsuro Fujita Reviewed-by: Corey Huinker Discussion: https://postgr.es/m/CAHGQGwH8%2BuDTwfohpck9v_9L3nwSWACbmC36JYRyqHd5xQN_OA%40mail.gmail.com Backpatch-through: 19 --- contrib/postgres_fdw/postgres_fdw.c | 35 +++++++++++++++++++---------- doc/src/sgml/postgres-fdw.sgml | 11 ++++----- 2 files changed, 29 insertions(+), 17 deletions(-) diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c index 3f0b5bbe9ed..61e4e6fb2d8 100644 --- a/contrib/postgres_fdw/postgres_fdw.c +++ b/contrib/postgres_fdw/postgres_fdw.c @@ -570,6 +570,7 @@ static bool fetch_remote_statistics(Relation relation, const char *local_schemaname, const char *local_relname, ForeignTable *table, + ForeignServer *server, RemoteStatsResults *remstats, RemoteAttributeMapping **p_remattrmap, int *p_attrcnt); @@ -5533,7 +5534,7 @@ postgresImportForeignStatistics(Relation relation, List *va_cols, int elevel) starttime = GetCurrentTimestamp(); ok = fetch_remote_statistics(relation, va_cols, - schemaname, relname, table, + schemaname, relname, table, server, &remstats, &remattrmap, &attrcnt); if (ok) @@ -5566,6 +5567,7 @@ fetch_remote_statistics(Relation relation, const char *local_schemaname, const char *local_relname, ForeignTable *table, + ForeignServer *server, RemoteStatsResults *remstats, RemoteAttributeMapping **p_remattrmap, int *p_attrcnt) @@ -5682,6 +5684,19 @@ fetch_remote_statistics(Relation relation, /* Try to get attribute stats if needed. */ if (attrcnt > 0) { + /* + * The fetch_attstats query sends COLLATE "C" to the remote + * server; if it hasn't got it, fallback to sampling. + */ + if (server_version_num < 90100) + { + ereport(WARNING, + errmsg("could not import statistics for foreign table \"%s.%s\" --- foreign server \"%s\" is too old to support attribute statistics import", + local_schemaname, local_relname, + server->servername)); + goto fetch_cleanup; + } + /* Fetch attribute stats. */ remstats->att = attstats = fetch_attstats(conn, server_version_num, @@ -5757,6 +5772,9 @@ fetch_attstats(PGconn *conn, int server_version_num, StringInfoData sql; PGresult *res; + /* The caller guarantees the remote server is v9.1 or later. */ + Assert(server_version_num >= 90100); + initStringInfo(&sql); appendStringInfoString(&sql, "SELECT DISTINCT ON (attname COLLATE \"C\") attname," @@ -5800,18 +5818,11 @@ fetch_attstats(PGconn *conn, int server_version_num, column_list); /* - * inherited is supported since Postgres 9.0 - * - * Note that this is okay because for now, we support the case where the - * remote table is partitioned, but not the case where it is inherited - * (see fetch_remote_statistics()). + * inherited and COLLATE are supported since Postgres 9.0 and 9.1, + * respectively. */ - if (server_version_num >= 90000) - appendStringInfoString(&sql, - " ORDER BY attname COLLATE \"C\", inherited DESC"); - else - appendStringInfoString(&sql, - " ORDER BY attname COLLATE \"C\""); + appendStringInfoString(&sql, + " ORDER BY attname COLLATE \"C\", inherited DESC"); res = pgfdw_exec_query(conn, sql.data, NULL); if (PQresultStatus(res) != PGRES_TUPLES_OK) diff --git a/doc/src/sgml/postgres-fdw.sgml b/doc/src/sgml/postgres-fdw.sgml index 02c2edb2c97..a71acc9b610 100644 --- a/doc/src/sgml/postgres-fdw.sgml +++ b/doc/src/sgml/postgres-fdw.sgml @@ -370,14 +370,15 @@ OPTIONS (ADD password_required 'false'); This option, which can be specified for a foreign table or a foreign server, determines if ANALYZE on a foreign table - will instead attempt to fetch the existing statistics for the foreign - table on the remote server, and import those statistics directly to - the local server. If the attempt failed, statistics are collected by - row sampling on the foreign table. + will instead attempt to fetch the existing statistics for the remote + table, and import those statistics directly to the local server + (without applying the foreign table columns' + n_distinct option). If the attempt failed, + statistics are collected by row sampling on the foreign table. This option is only useful if the remote table is one that can have regular statistics (tables, not inherited, and materialized views). When using this option, it is the user's responsibility - to ensure that the existing statistics for the foreign + to ensure that the existing statistics for the remote table are up-to-date. The default is false.