agora inbox for pgsql-hackers@postgresql.org  
help / color / mirror / Atom feed
[PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
178+ messages / 1 participants
[nested] [flat]

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--






^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--





^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--





^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--





^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--






^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--






^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--






^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--






^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--





^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--






^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--





^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--





^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--





^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--





^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--






^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--





^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--






^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--






^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--






^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--





^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--






^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--





^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--






^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--






^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--





^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--






^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--





^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--






^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--





^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--






^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--





^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--





^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--





^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--






^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--





^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--





^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--





^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--






^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--





^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--





^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--






^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--





^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--





^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--






^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--






^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--






^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--






^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--






^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--






^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--





^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--





^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--





^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--






^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--





^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--






^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--





^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--





^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--






^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--






^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--





^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--





^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--





^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--





^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--





^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--





^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--





^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--





^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--






^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--





^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--






^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--






^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--






^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--






^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--






^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--





^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--






^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--





^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--






^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--





^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--





^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--






^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--





^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--





^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--





^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--





^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--






^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--





^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--






^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--





^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--






^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--






^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--






^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--





^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--






^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--






^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--





^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--






^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--





^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--






^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--






^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--






^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--






^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--





^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--





^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--






^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--





^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--





^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--






^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--





^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--





^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--






^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--






^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--






^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--





^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--






^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--






^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--






^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--





^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--






^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--





^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--





^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--






^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--





^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--






^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--






^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--






^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--






^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--






^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--





^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--






^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--





^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--






^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--






^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--





^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--






^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--





^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--





^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--






^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--






^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--






^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--






^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--





^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--





^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--






^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--






^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--






^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--






^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--






^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--





^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--






^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--






^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--






^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--





^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--






^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--






^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--






^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--





^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--





^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--





^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--






^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--






^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--






^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--





^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--






^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--






^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--





^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--






^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--





^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--





^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--





^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--






^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--






^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--






^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--





^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--





^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--





^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--






^ permalink  raw  reply  [nested|flat] 178+ messages in thread

* [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
@ 2026-09-04 21:47 Nathan Bossart <nathan@postgresql.org>
  0 siblings, 0 replies; 178+ messages in thread

From: Nathan Bossart @ 2026-09-04 21:47 UTC (permalink / raw)

The in-tree getopt_long() moves each non-option to the end of argv
as soon as it finds one, which puts a non-option that preceded an
option right where the option's argument lookup expects to find it.
For example, "vacuumdb postgres --jobs" takes "postgres" as the
number of jobs instead of complaining that --jobs is missing its
argument.  To fix, stop the argument lookups at the start of the
moved non-options, which we already track to know when to stop
scanning.

Oversight in commit 411b720343.

Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
Backpatch-through: 17
---
 src/bin/scripts/t/100_vacuumdb.pl |  4 ++++
 src/port/getopt_long.c            | 14 +++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index 7c4e35a6717..b78fa2a38de 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -240,6 +240,10 @@ $node->command_fails_like(
 	[ 'vacuumdb', '--all', 'postgres' ],
 	qr/cannot vacuum all databases and a specific one at the same time/,
 	'cannot use option --all and a dbname as argument at the same time');
+$node->command_fails_like(
+	[ 'vacuumdb', 'postgres', '--jobs' ],
+	qr/requires an argument/,
+	'option missing its argument after a non-option');
 
 $node->safe_psql(
 	'postgres', q|
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 0a9a50189f1..f2edadb59d5 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -66,6 +66,9 @@ getopt_long(int argc, char *const argv[],
 	static int	nonopt_start = -1;
 	static bool force_nonopt = false;
 
+	if (nonopt_start == -1)
+		nonopt_start = argc;
+
 	if (!*place)
 	{							/* update scanning pointer */
 		char	  **args = (char **) argv;
@@ -75,7 +78,7 @@ retry:
 		/*
 		 * If we are out of arguments or only non-options remain, return -1.
 		 */
-		if (optind >= argc || optind == nonopt_start)
+		if (optind >= nonopt_start)
 		{
 			place = EMSG;
 			nonopt_start = -1;
@@ -99,10 +102,7 @@ retry:
 				args[i] = args[i + 1];
 			args[argc - 1] = place;
 
-			if (nonopt_start == -1)
-				nonopt_start = argc - 1;
-			else
-				nonopt_start--;
+			nonopt_start--;
 
 			goto retry;
 		}
@@ -139,7 +139,7 @@ retry:
 							optarg = place + namelen + 1;
 						else if (has_arg == optional_argument)
 							optarg = NULL;
-						else if (optind < argc - 1)
+						else if (optind < nonopt_start - 1)
 						{
 							optind++;
 							optarg = argv[optind];
@@ -222,7 +222,7 @@ retry:
 	{							/* need an argument */
 		if (*place)				/* no white space */
 			optarg = place;
-		else if (argc <= ++optind)
+		else if (nonopt_start <= ++optind)
 		{						/* no arg */
 			place = EMSG;
 			if (*optstring == ':')
-- 
2.55.0


--Zv9Xg0nwk4TTIGFg--






^ permalink  raw  reply  [nested|flat] 178+ messages in thread


end of thread, other threads:[~2026-09-04 21:47 UTC | newest]

Thread overview: 178+ messages (download: mbox mbox.gz follow: Atom feed)
-- links below jump to the message on this page --
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>
2026-09-04 21:47 [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long(). Nathan Bossart <nathan@postgresql.org>

This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox