From 34c86514b262e808b7837f994c0da76acbf79a54 Mon Sep 17 00:00:00 2001
From: Rafia Sabih <rafia.sabih@cybertec.at>
Date: Mon, 20 Jul 2026 14:44:32 +0530
Subject: [PATCH v7] Emit debug message for batch_size reduced

In case batch_size is reduced to keep the parameter within
limits of libpq, emit a debug message.
Also emit a warning message when batch_size is set to a value
over the limit.
---
 .../postgres_fdw/expected/postgres_fdw.out    | 46 +++++++++++++++++++
 contrib/postgres_fdw/option.c                 |  5 ++
 contrib/postgres_fdw/postgres_fdw.c           |  8 ++++
 contrib/postgres_fdw/sql/postgres_fdw.sql     | 46 +++++++++++++++++++
 4 files changed, 105 insertions(+)

diff --git a/contrib/postgres_fdw/expected/postgres_fdw.out b/contrib/postgres_fdw/expected/postgres_fdw.out
index 9303de98b62..237c26136ae 100644
--- a/contrib/postgres_fdw/expected/postgres_fdw.out
+++ b/contrib/postgres_fdw/expected/postgres_fdw.out
@@ -11684,6 +11684,52 @@ DROP TRIGGER ftable_rowcount_trigger ON ltable;
 DROP TABLE ltable;
 DROP TABLE parent;
 DROP FUNCTION ftable_rowcount_trigf;
+-- Verify that DEBUG1 is emitted when batch_size is reduced to stay within
+-- the libpq 65535-parameter limit.
+SET client_min_messages = DEBUG1;
+CREATE TABLE batch_table ( x int, y int );
+CREATE FOREIGN TABLE ftable ( x int, y int )
+	SERVER loopback
+	OPTIONS (table_name 'batch_table', batch_size '33000');
+INSERT INTO ftable(x) VALUES (1);
+DEBUG:  batch_size reduced from 33000 to 32767 to stay within the libpq 65535-parameter limit
+-- Clean up
+DROP FOREIGN TABLE ftable;
+DROP TABLE batch_table;
+-- Verify that a WARNING is emitted when batch_size is set above the
+-- libpq 65535-parameter limit.
+CREATE TABLE batch_warn_table ( x int );
+CREATE FOREIGN TABLE ftable_warn ( x int )
+  SERVER loopback
+  OPTIONS (table_name 'batch_warn_table', batch_size '65536');
+WARNING:  batch_size of 65536 exceeds protocol limit of 65535
+DETAIL:  The batch_size for a query will be reduced to protocol limit divided by the number of columns in the query.
+INSERT INTO ftable_warn VALUES (1);
+DEBUG:  batch_size reduced from 65536 to 65535 to stay within the libpq 65535-parameter limit
+-- Clean up
+DROP FOREIGN TABLE ftable_warn;
+DROP TABLE batch_warn_table;
+-- Verify that no WARNING is emitted when batch_size is within the
+-- libpq 65535-parameter limit.
+-- check for single column
+CREATE TABLE batch_nowarn_table ( x int );
+CREATE FOREIGN TABLE ftable_no_warn ( x int )
+  SERVER loopback
+  OPTIONS (table_name 'batch_nowarn_table', batch_size '65535');
+INSERT INTO ftable_no_warn VALUES (1);
+-- Clean up
+DROP FOREIGN TABLE ftable_no_warn;
+DROP TABLE batch_nowarn_table;
+-- check for more than one columns
+CREATE TABLE batch_table ( x int, y int );
+CREATE FOREIGN TABLE ftable ( x int, y int )
+	SERVER loopback
+	OPTIONS (table_name 'batch_table', batch_size '32767');
+INSERT INTO ftable(x) VALUES (1);
+RESET client_min_messages;
+-- Clean up
+DROP FOREIGN TABLE ftable;
+DROP TABLE batch_table;
 -- ===================================================================
 -- test asynchronous execution
 -- ===================================================================
diff --git a/contrib/postgres_fdw/option.c b/contrib/postgres_fdw/option.c
index 79b16c3f318..b58300e6efe 100644
--- a/contrib/postgres_fdw/option.c
+++ b/contrib/postgres_fdw/option.c
@@ -179,6 +179,11 @@ postgres_fdw_validator(PG_FUNCTION_ARGS)
 						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
 						 errmsg("\"%s\" must be an integer value greater than zero",
 								def->defname)));
+			if (strcmp(def->defname, "batch_size") == 0 &&
+				int_val > PQ_QUERY_PARAM_MAX_LIMIT)
+				ereport(WARNING,
+						errmsg("batch_size of %d exceeds protocol limit of %d", int_val, PQ_QUERY_PARAM_MAX_LIMIT),
+						errdetail("The batch_size for a query will be reduced to protocol limit divided by the number of columns in the query."));
 		}
 		else if (strcmp(def->defname, "password_required") == 0)
 		{
diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index 8b660a6c02c..12b752629f3 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -2182,7 +2182,15 @@ postgresGetForeignModifyBatchSize(ResultRelInfo *resultRelInfo)
 	 * don't exceed this limit by using the maximum batch_size possible.
 	 */
 	if (fmstate && fmstate->p_nums > 0)
+	{
+		int			configured = batch_size;
+
 		batch_size = Min(batch_size, PQ_QUERY_PARAM_MAX_LIMIT / fmstate->p_nums);
+		if (batch_size < configured)
+			elog(DEBUG1, "batch_size reduced from %d to %d "
+				 "to stay within the libpq %d-parameter limit",
+				 configured, batch_size, PQ_QUERY_PARAM_MAX_LIMIT);
+	}
 
 	return batch_size;
 }
diff --git a/contrib/postgres_fdw/sql/postgres_fdw.sql b/contrib/postgres_fdw/sql/postgres_fdw.sql
index e7019952173..29f22f65e37 100644
--- a/contrib/postgres_fdw/sql/postgres_fdw.sql
+++ b/contrib/postgres_fdw/sql/postgres_fdw.sql
@@ -4027,6 +4027,52 @@ DROP TABLE ltable;
 DROP TABLE parent;
 DROP FUNCTION ftable_rowcount_trigf;
 
+-- Verify that DEBUG1 is emitted when batch_size is reduced to stay within
+-- the libpq 65535-parameter limit.
+SET client_min_messages = DEBUG1;
+CREATE TABLE batch_table ( x int, y int );
+CREATE FOREIGN TABLE ftable ( x int, y int )
+	SERVER loopback
+	OPTIONS (table_name 'batch_table', batch_size '33000');
+INSERT INTO ftable(x) VALUES (1);
+
+-- Clean up
+DROP FOREIGN TABLE ftable;
+DROP TABLE batch_table;
+
+-- Verify that a WARNING is emitted when batch_size is set above the
+-- libpq 65535-parameter limit.
+CREATE TABLE batch_warn_table ( x int );
+CREATE FOREIGN TABLE ftable_warn ( x int )
+  SERVER loopback
+  OPTIONS (table_name 'batch_warn_table', batch_size '65536');
+INSERT INTO ftable_warn VALUES (1);
+-- Clean up
+DROP FOREIGN TABLE ftable_warn;
+DROP TABLE batch_warn_table;
+
+-- Verify that no WARNING is emitted when batch_size is within the
+-- libpq 65535-parameter limit.
+-- check for single column
+CREATE TABLE batch_nowarn_table ( x int );
+CREATE FOREIGN TABLE ftable_no_warn ( x int )
+  SERVER loopback
+  OPTIONS (table_name 'batch_nowarn_table', batch_size '65535');
+INSERT INTO ftable_no_warn VALUES (1);
+-- Clean up
+DROP FOREIGN TABLE ftable_no_warn;
+DROP TABLE batch_nowarn_table;
+
+-- check for more than one columns
+CREATE TABLE batch_table ( x int, y int );
+CREATE FOREIGN TABLE ftable ( x int, y int )
+	SERVER loopback
+	OPTIONS (table_name 'batch_table', batch_size '32767');
+INSERT INTO ftable(x) VALUES (1);
+RESET client_min_messages;
+-- Clean up
+DROP FOREIGN TABLE ftable;
+DROP TABLE batch_table;
 -- ===================================================================
 -- test asynchronous execution
 -- ===================================================================
-- 
2.39.5 (Apple Git-154)

