From: Etsuro Fujita Date: Thu, 14 May 2026 08:05:00 +0000 (+0900) Subject: postgres_fdw: Fix deparsing of remote column names in stats import. X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=5107398e6d5ecad96f3d1c0efcfc9aa02b9cdff9;p=thirdparty%2Fpostgresql.git postgres_fdw: Fix deparsing of remote column names in stats import. build_remattrmap() deparses a list of remote column names for a query that retrieves attribute stats for them from the remote server. Previously, it did so by using the array-literal syntax with each column name individually quoted by quote_identifier(), causing the query to fail on the remote server with a syntax error or no results when that column name included a single quote or backslash, as quote_identifier() doesn't escape those characters, making the query invalid or incorrect. Fix by switching from the array-literal syntax to the ARRAY constructor syntax with each column name individually quoted by deparseStringLiteral(). Oversight in commit 28972b6fc. Reported-by: Satya Narlapuram Reported-by: Ayush Tiwari Author: Ayush Tiwari Reviewed-by: Alex Guo Reviewed-by: Zhenwei Shang Reviewed-by: Etsuro Fujita Discussion: https://postgr.es/m/CAHg%2BQDc9%3DWtYi%3DJW6QUL6ASOJc6PcGPTuxoMkhnkQ7oi7j5atg%40mail.gmail.com Discussion: https://postgr.es/m/CAJTYsWWGhVDFjr%2BsmdYdU-Q_TT9YMzXA4QcLCr7rizDOyrEEow%40mail.gmail.com --- diff --git a/contrib/postgres_fdw/expected/postgres_fdw.out b/contrib/postgres_fdw/expected/postgres_fdw.out index aaffcf31271..e90289e4ab1 100644 --- a/contrib/postgres_fdw/expected/postgres_fdw.out +++ b/contrib/postgres_fdw/expected/postgres_fdw.out @@ -12917,11 +12917,23 @@ CREATE FOREIGN TABLE simport_fview (c1 int, c2 text) ALTER FOREIGN TABLE simport_fview OPTIONS (ADD restore_stats 'true'); ANALYZE simport_fview; -- should fail WARNING: could not import statistics for foreign table "public.simport_fview" --- remote table "public.simport_view" is of relkind "v" which cannot have statistics +-- This tests build_remattrmap()'s deparsing of column names that include +-- single quotes or backslashes +CREATE TABLE dtest_table ("col'quote" int, "col\backslash" int); +CREATE FOREIGN TABLE dtest_ftable ("col'quote" int, "col\backslash" int) + SERVER loopback OPTIONS (table_name 'dtest_table', restore_stats 'true'); +INSERT INTO dtest_table SELECT g, g FROM generate_series(1, 10) g; +ANALYZE dtest_table; +ANALYZE VERBOSE dtest_ftable; -- should work +INFO: importing statistics for foreign table "public.dtest_ftable" +INFO: finished importing statistics for foreign table "public.dtest_ftable" -- cleanup DROP FOREIGN TABLE simport_ftable; DROP FOREIGN TABLE simport_fview; DROP VIEW simport_view; DROP TABLE simport_table; +DROP FOREIGN TABLE dtest_ftable; +DROP TABLE dtest_table; -- =================================================================== -- test for postgres_fdw_get_connections function with check_conn = true -- =================================================================== diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c index 0657618eda3..85be46eb2a2 100644 --- a/contrib/postgres_fdw/postgres_fdw.c +++ b/contrib/postgres_fdw/postgres_fdw.c @@ -5886,7 +5886,7 @@ fetch_attstats(PGconn *conn, int server_version_num, " AND tablename = "); deparseStringLiteral(&sql, remote_relname); appendStringInfo(&sql, - " AND attname = ANY('%s'::text[])", + " AND attname = ANY(%s)", column_list); /* inherited is supported since Postgres 9.0 */ @@ -5921,7 +5921,7 @@ build_remattrmap(Relation relation, List *va_cols, remattrmap = palloc_array(RemoteAttributeMapping, tupdesc->natts); initStringInfo(column_list); - appendStringInfoChar(column_list, '{'); + appendStringInfoString(column_list, "ARRAY["); for (int i = 0; i < tupdesc->natts; i++) { Form_pg_attribute attr = TupleDescAttr(tupdesc, i); @@ -5954,7 +5954,7 @@ build_remattrmap(Relation relation, List *va_cols, if (attrcnt > 0) appendStringInfoString(column_list, ", "); - appendStringInfoString(column_list, quote_identifier(remote_attname)); + deparseStringLiteral(column_list, remote_attname); remattrmap[attrcnt].local_attnum = attnum; strncpy(remattrmap[attrcnt].local_attname, attname, NAMEDATALEN); @@ -5962,7 +5962,7 @@ build_remattrmap(Relation relation, List *va_cols, remattrmap[attrcnt].res_index = -1; attrcnt++; } - appendStringInfoChar(column_list, '}'); + appendStringInfoChar(column_list, ']'); /* Sort mapping by remote attribute name if needed. */ if (attrcnt > 1) diff --git a/contrib/postgres_fdw/sql/postgres_fdw.sql b/contrib/postgres_fdw/sql/postgres_fdw.sql index 267d3c1a7e7..dfc58beb0d2 100644 --- a/contrib/postgres_fdw/sql/postgres_fdw.sql +++ b/contrib/postgres_fdw/sql/postgres_fdw.sql @@ -4573,11 +4573,24 @@ ALTER FOREIGN TABLE simport_fview OPTIONS (ADD restore_stats 'true'); ANALYZE simport_fview; -- should fail +-- This tests build_remattrmap()'s deparsing of column names that include +-- single quotes or backslashes +CREATE TABLE dtest_table ("col'quote" int, "col\backslash" int); +CREATE FOREIGN TABLE dtest_ftable ("col'quote" int, "col\backslash" int) + SERVER loopback OPTIONS (table_name 'dtest_table', restore_stats 'true'); + +INSERT INTO dtest_table SELECT g, g FROM generate_series(1, 10) g; +ANALYZE dtest_table; + +ANALYZE VERBOSE dtest_ftable; -- should work + -- cleanup DROP FOREIGN TABLE simport_ftable; DROP FOREIGN TABLE simport_fview; DROP VIEW simport_view; DROP TABLE simport_table; +DROP FOREIGN TABLE dtest_ftable; +DROP TABLE dtest_table; -- =================================================================== -- test for postgres_fdw_get_connections function with check_conn = true