From 290bb8afe6b47adb8b080688bd97a710af351cb1 Mon Sep 17 00:00:00 2001 From: Michael Paquier Date: Thu, 6 Aug 2026 16:46:03 +0900 Subject: [PATCH] Tighten proto_version parsing in pgoutput This is similar to 58ff4a0a0867, but this time for the parameter "proto_version", when given as a parameter to pgoutput for logical replication. strtoul() lacked a check for an empty string, leading to an inconsistent behavior depending on the platform. The author has originally proposed a TAP test to check this empty value pattern. I have added a cheaper SQL query, instead, test_decoding including a test area where this is possible. Author: Tristan Partin Discussion: https://postgr.es/m/DKBS2Z9CGARC.2T07O6TJYSE8B@partin.io --- contrib/test_decoding/expected/replorigin.out | 4 ++++ contrib/test_decoding/sql/replorigin.sql | 3 +++ src/backend/replication/pgoutput/pgoutput.c | 5 +++-- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/contrib/test_decoding/expected/replorigin.out b/contrib/test_decoding/expected/replorigin.out index 29a9630c900..14b91e12c10 100644 --- a/contrib/test_decoding/expected/replorigin.out +++ b/contrib/test_decoding/expected/replorigin.out @@ -292,6 +292,10 @@ SELECT pg_replication_origin_session_setup('regress_test_decoding: regression_sl (1 row) INSERT INTO target_tbl(data) VALUES ('test data'); +-- invalid proto_version +SELECT pg_logical_slot_peek_binary_changes('regression_slot', NULL, NULL, 'proto_version', '', 'publication_names', 'pub', 'origin', 'none'); +ERROR: invalid proto_version +CONTEXT: slot "regression_slot", output plugin "pgoutput", in the startup callback -- The replayed change will be filtered. SELECT count(*) = 0 FROM pg_logical_slot_peek_binary_changes('regression_slot', NULL, NULL, 'proto_version', '4', 'publication_names', 'pub', 'origin', 'none'); ?column? diff --git a/contrib/test_decoding/sql/replorigin.sql b/contrib/test_decoding/sql/replorigin.sql index 17f2b888238..56f20ec8ea6 100644 --- a/contrib/test_decoding/sql/replorigin.sql +++ b/contrib/test_decoding/sql/replorigin.sql @@ -138,6 +138,9 @@ SELECT pg_replication_origin_session_setup('regress_test_decoding: regression_sl INSERT INTO target_tbl(data) VALUES ('test data'); +-- invalid proto_version +SELECT pg_logical_slot_peek_binary_changes('regression_slot', NULL, NULL, 'proto_version', '', 'publication_names', 'pub', 'origin', 'none'); + -- The replayed change will be filtered. SELECT count(*) = 0 FROM pg_logical_slot_peek_binary_changes('regression_slot', NULL, NULL, 'proto_version', '4', 'publication_names', 'pub', 'origin', 'none'); diff --git a/src/backend/replication/pgoutput/pgoutput.c b/src/backend/replication/pgoutput/pgoutput.c index 4ecfcbff7ab..0afdb1432ca 100644 --- a/src/backend/replication/pgoutput/pgoutput.c +++ b/src/backend/replication/pgoutput/pgoutput.c @@ -316,6 +316,7 @@ parse_output_parameters(List *options, PGOutputData *data) { unsigned long parsed; char *endptr; + const char *val = strVal(defel->arg); if (protocol_version_given) ereport(ERROR, @@ -324,8 +325,8 @@ parse_output_parameters(List *options, PGOutputData *data) protocol_version_given = true; errno = 0; - parsed = strtoul(strVal(defel->arg), &endptr, 10); - if (errno != 0 || *endptr != '\0') + parsed = strtoul(val, &endptr, 10); + if (endptr == val || errno != 0 || *endptr != '\0') ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("invalid proto_version")));