agora inbox for pgsql-hackers@postgresql.org
help / color / mirror / Atom feed[PATCH] Fix getopt_long() argument handling and add tests
5+ messages / 3 participants
[nested] [flat]
* [PATCH] Fix getopt_long() argument handling and add tests
@ 2026-08-29 16:57 Sehrope Sarkuni <sehrope@jackdb.com>
2026-09-04 21:58 ` Re: [PATCH] Fix getopt_long() argument handling and add tests Nathan Bossart <nathandbossart@gmail.com>
0 siblings, 1 reply; 5+ messages in thread
From: Sehrope Sarkuni @ 2026-08-29 16:57 UTC (permalink / raw)
To: pgsql-hackers; +Cc: Nathan Bossart <nathandbossart@gmail.com>
Hi hackers,
While reworking the pg_waldump TAP test [1] I ran "pg_waldump --stats
--limit 5" and it failed on Windows CI with 'could not locate WAL file
"5"'. For a long option with optional_argument and no "=", the port
getopt_long() increments optind in the missing-argument branch and
again at the end of the long option path, so the next option is
skipped and its value becomes a positional argument. The same branch
returns BADARG when optstring starts with a colon although nothing is
missing.
glibc handles both correctly, so this only happens in CI on Windows, for
--stats in pg_waldump and --install-missing in pg_amcheck when another
option follows. psql's --help is also optional_argument but exits
before anything after it matters.
0001 gives optional_argument its own branch that sets optarg to NULL
and falls through to the common optind increment. The remaining else
is then only the missing required argument case.
0002 adds src/test/modules/test_getopt_long, since nothing in
check-world exercises this file on Linux or macOS. A small harness
takes an optstring, a long option list and the arguments, and prints
one line per getopt_long() return plus the leftover non-options.
src/port/getopt_long.c is compiled into it directly so it is tested
everywhere. A second copy linked without it runs the same cases
against the system getopt_long().
The TAP test is a text table of named cases, each with its arguments
and expected output:
== long optional, separate not consumed
args: --gamma foo
--gamma
-- foo
== short missing arg, silent
optstring: :ab:
args: -b
:
--
It covers short options attached, separate and bundled, missing and
unknown options with and without the leading colon, long required
arguments in each form, long optional arguments first, middle and
last, the flag pointer form, "--", a lone "-" and non-option
reordering. Port and system output match on every case on Linux and
macOS. Only the error message wording differs and the test accepts
either.
0003 fixes a second problem the tests turned up. The port moves each
non-option to the end of argv as soon as it sees it, so by the time a
following option looks for its argument, a non-option that came before
it on the command line is already sitting after it and gets taken:
"pg_amcheck mydb --jobs" parses as --jobs=mydb rather than reporting
the missing argument. The system getopt_long() reports the missing
argument. The fix bounds the argument search by the start of the
moved block. 0004 adds the test cases for it, kept separate in case
0003 is backpatched without the test module.
This behavior comes from the non-option reordering added in 411b720343.
CC'ing Nathan in case I'm missing an intended aspect of that
implementation.
Without 0001 and 0003 the port binary fails 16 cases, the 12 optional
argument ones that 0001 fixes and the four from 0004 that 0003 fixes.
The system binary passes throughout on Linux. With both patches, all
244 checks pass.
Both fixes seem worth backpatching. The test module itself probably
doesn't need to be.
AI review also flagged a couple of possible portability issues with
the system getopt_long() comparison on BSD, particularly around
differences in error messages. I don't have a BSD system to test on,
so will see what the buildfarm says.
[1] https://www.postgresql.org/message-id/CAH7T-araSEdsNpxiKaMOW8kr_ZHhekCLHx5yzm2ksq4FdgULbA%40mail.gma...
Regards,
-- Sehrope Sarkuni
Founder & CEO | JackDB, Inc. | https://www.jackdb.com/
Attachments:
[text/x-patch] 0001-Fix-optional-argument-handling-in-port-getopt_long.patch (1.6K, ../../CAH7T-arxDuVCSkorO=k7+M-_JV0JFzMpN_EtKMyD2K0RDqZ2OA@mail.gmail.com/2-0001-Fix-optional-argument-handling-in-port-getopt_long.patch)
download | inline diff:
From daaa4883cc523984e567cfb5323bc6e4f032f401 Mon Sep 17 00:00:00 2001
From: Sehrope Sarkuni <sehrope@jackdb.com>
Date: Sat, 29 Aug 2026 00:34:18 +0000
Subject: [PATCH 1/4] Fix optional argument handling in port getopt_long()
A long option with an optional argument and no "=" advanced optind
twice, skipping the next argument, and returned BADARG when optstring
starts with a colon even though nothing is missing.
---
src/port/getopt_long.c | 20 ++++++--------------
1 file changed, 6 insertions(+), 14 deletions(-)
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 2e869fed58b..7489a0206ee 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -137,31 +137,23 @@ retry:
{
if (place[namelen] == '=')
optarg = place + namelen + 1;
- else if (optind < argc - 1 &&
- has_arg == required_argument)
+ else if (has_arg == optional_argument)
+ optarg = NULL;
+ else if (optind < argc - 1)
{
optind++;
optarg = argv[optind];
}
else
{
+ /* required argument missing */
optind++;
- if (optstring[0] == ':')
- {
- place = EMSG;
- return BADARG;
- }
-
- if (opterr && has_arg == required_argument)
+ if (opterr && optstring[0] != ':')
fprintf(stderr,
"%s: option requires an argument -- %s\n",
argv[0], place);
-
place = EMSG;
-
- if (has_arg == required_argument)
- return BADCH;
- optarg = NULL;
+ return optstring[0] == ':' ? BADARG : BADCH;
}
}
else
--
2.17.1
[text/x-patch] 0004-Test-getopt_long-with-a-non-option-before-a-missing-.patch (1.4K, ../../CAH7T-arxDuVCSkorO=k7+M-_JV0JFzMpN_EtKMyD2K0RDqZ2OA@mail.gmail.com/3-0004-Test-getopt_long-with-a-non-option-before-a-missing-.patch)
download | inline diff:
From 7525ace3a35ddf08e3dd021004adec1dea77ef67 Mon Sep 17 00:00:00 2001
From: Sehrope Sarkuni <sehrope@jackdb.com>
Date: Sat, 29 Aug 2026 13:55:44 +0000
Subject: [PATCH 4/4] Test getopt_long() with a non-option before a missing
argument
---
.../test_getopt_long/t/001_getopt_long.pl | 24 +++++++++++++++++++
1 file changed, 24 insertions(+)
diff --git a/src/test/modules/test_getopt_long/t/001_getopt_long.pl b/src/test/modules/test_getopt_long/t/001_getopt_long.pl
index 85c49d7d678..b407967a148 100644
--- a/src/test/modules/test_getopt_long/t/001_getopt_long.pl
+++ b/src/test/modules/test_getopt_long/t/001_getopt_long.pl
@@ -75,6 +75,18 @@ args: -ab
--
stderr: requires an argument -- '?b'?
+== short missing arg, non-option before
+args: foo -b
+?
+-- foo
+stderr: requires an argument -- '?b'?
+
+== short missing arg, lone dash before
+args: - -b
+?
+-- -
+stderr: requires an argument -- '?b'?
+
== short unknown
args: -x
?
@@ -184,6 +196,18 @@ args: -a --beta
--
stderr: requires an argument -- beta|[`']--beta' requires an argument
+== long required, missing, non-option before
+args: foo --beta
+?
+-- foo
+stderr: requires an argument -- beta|[`']--beta' requires an argument
+
+== long required, missing, lone dash before
+args: - --beta
+?
+-- -
+stderr: requires an argument -- beta|[`']--beta' requires an argument
+
== long optional, alone
args: --gamma
--gamma
--
2.17.1
[text/x-patch] 0002-Add-test_getopt_long-module.patch (16.1K, ../../CAH7T-arxDuVCSkorO=k7+M-_JV0JFzMpN_EtKMyD2K0RDqZ2OA@mail.gmail.com/4-0002-Add-test_getopt_long-module.patch)
download | inline diff:
From acb641037186b254567d2b352340772f455d2c17 Mon Sep 17 00:00:00 2001
From: Sehrope Sarkuni <sehrope@jackdb.com>
Date: Sat, 29 Aug 2026 13:34:07 +0000
Subject: [PATCH 2/4] Add test_getopt_long module
TAP tests for the port getopt_long(). The harness compiles
src/port/getopt_long.c in directly since libpgport uses the system
implementation on most platforms. A second copy linked without it runs
the same cases against the system getopt_long() for comparison.
---
src/test/modules/Makefile | 1 +
src/test/modules/meson.build | 1 +
src/test/modules/test_getopt_long/Makefile | 39 ++
src/test/modules/test_getopt_long/README | 18 +
src/test/modules/test_getopt_long/meson.build | 48 +++
.../test_getopt_long/t/001_getopt_long.pl | 372 ++++++++++++++++++
.../test_getopt_long/test_getopt_long.c | 149 +++++++
7 files changed, 628 insertions(+)
create mode 100644 src/test/modules/test_getopt_long/Makefile
create mode 100644 src/test/modules/test_getopt_long/README
create mode 100644 src/test/modules/test_getopt_long/meson.build
create mode 100644 src/test/modules/test_getopt_long/t/001_getopt_long.pl
create mode 100644 src/test/modules/test_getopt_long/test_getopt_long.c
diff --git a/src/test/modules/Makefile b/src/test/modules/Makefile
index bb88b3058ed..c85e8aff866 100644
--- a/src/test/modules/Makefile
+++ b/src/test/modules/Makefile
@@ -31,6 +31,7 @@ SUBDIRS = \
test_dsm_registry \
test_escape \
test_extensions \
+ test_getopt_long \
test_ginpostinglist \
test_int128 \
test_integerset \
diff --git a/src/test/modules/meson.build b/src/test/modules/meson.build
index ce09e00531d..3af2b1473e2 100644
--- a/src/test/modules/meson.build
+++ b/src/test/modules/meson.build
@@ -32,6 +32,7 @@ subdir('test_dsa')
subdir('test_dsm_registry')
subdir('test_escape')
subdir('test_extensions')
+subdir('test_getopt_long')
subdir('test_ginpostinglist')
subdir('test_int128')
subdir('test_integerset')
diff --git a/src/test/modules/test_getopt_long/Makefile b/src/test/modules/test_getopt_long/Makefile
new file mode 100644
index 00000000000..eb53b2c2231
--- /dev/null
+++ b/src/test/modules/test_getopt_long/Makefile
@@ -0,0 +1,39 @@
+# src/test/modules/test_getopt_long/Makefile
+
+PGFILEDESC = "standalone getopt_long tester"
+PGAPPICON = win32
+
+TAP_TESTS = 1
+
+OBJS = test_getopt_long.o $(WIN32RES)
+
+# getopt_long.o is not in OBJS, or --with-llvm would look for a
+# getopt_long.c here to build getopt_long.bc from.
+EXTRA_CLEAN = test_getopt_long$(X) test_getopt_long_system$(X) getopt_long.o
+
+ifdef USE_PGXS
+PG_CONFIG = pg_config
+PGXS := $(shell $(PG_CONFIG) --pgxs)
+include $(PGXS)
+else
+subdir = src/test/modules/test_getopt_long
+top_builddir = ../../../..
+include $(top_builddir)/src/Makefile.global
+include $(top_srcdir)/contrib/contrib-global.mk
+endif
+
+all: test_getopt_long$(X) test_getopt_long_system$(X)
+
+%.o: $(top_srcdir)/$(subdir)/%.c
+
+# The port getopt_long() is compiled in directly rather than taken from
+# libpgport, which uses the system implementation where one exists.
+getopt_long.o: $(top_srcdir)/src/port/getopt_long.c
+ $(CC) $(CFLAGS) $(CPPFLAGS) -c $< -o $@
+
+test_getopt_long$(X): test_getopt_long.o getopt_long.o $(WIN32RES)
+ $(CC) $(CFLAGS) $^ $(PG_LIBS_INTERNAL) $(LDFLAGS) $(LDFLAGS_EX) $(PG_LIBS) $(LIBS) -o $@
+
+# linked against the system getopt_long(), where there is one
+test_getopt_long_system$(X): test_getopt_long.o $(WIN32RES)
+ $(CC) $(CFLAGS) $^ $(PG_LIBS_INTERNAL) $(LDFLAGS) $(LDFLAGS_EX) $(PG_LIBS) $(LIBS) -o $@
diff --git a/src/test/modules/test_getopt_long/README b/src/test/modules/test_getopt_long/README
new file mode 100644
index 00000000000..f1fffcdfc90
--- /dev/null
+++ b/src/test/modules/test_getopt_long/README
@@ -0,0 +1,18 @@
+Module `test_getopt_long`
+=========================
+
+This module tests the getopt_long() implementation in src/port/getopt_long.c.
+Most platforms provide their own getopt_long() and libpgport does not include
+the port version there, so the test program compiles the port source in
+directly and thus exercises it everywhere. `test_getopt_long_system` is the
+same program without it, so the tests also run against the system
+getopt_long() and check the two agree.
+
+`test_getopt_long OPTSTRING LONGOPTS [ARG ...]` parses ARGs with the given
+short option string and long option list and prints one line per
+getopt_long() return, followed by a line listing the remaining non-option
+arguments. See the header comment in test_getopt_long.c for the format. The
+TAP test compares that output against the expected sequence for a range of
+argument layouts: attached and separate values, optional arguments in first,
+middle and last position, missing arguments, "--", a bare "-", and
+non-option reordering.
diff --git a/src/test/modules/test_getopt_long/meson.build b/src/test/modules/test_getopt_long/meson.build
new file mode 100644
index 00000000000..aa2e533b119
--- /dev/null
+++ b/src/test/modules/test_getopt_long/meson.build
@@ -0,0 +1,48 @@
+# Copyright (c) 2026, PostgreSQL Global Development Group
+
+# The port getopt_long() is compiled in directly rather than taken from
+# libpgport, which uses the system implementation where one exists.
+test_getopt_long_sources = files(
+ 'test_getopt_long.c',
+ '../../../port/getopt_long.c',
+)
+
+if host_system == 'windows'
+ test_getopt_long_sources += rc_bin_gen.process(win32ver_rc, extra_args: [
+ '--NAME', 'test_getopt_long',
+ '--FILEDESC', 'standalone getopt_long tester',
+ ])
+endif
+
+test_getopt_long = executable('test_getopt_long',
+ test_getopt_long_sources,
+ dependencies: [frontend_code],
+ kwargs: default_bin_args + {
+ 'install': false,
+ },
+)
+
+# Same program linked against the system getopt_long(), where there is one,
+# to check the port version behaves the same.
+test_getopt_long_system = executable('test_getopt_long_system',
+ files('test_getopt_long.c'),
+ dependencies: [frontend_code],
+ kwargs: default_bin_args + {
+ 'install': false,
+ },
+)
+
+tests += {
+ 'name': 'test_getopt_long',
+ 'sd': meson.current_source_dir(),
+ 'bd': meson.current_build_dir(),
+ 'tap': {
+ 'tests': [
+ 't/001_getopt_long.pl',
+ ],
+ 'deps': [
+ test_getopt_long,
+ test_getopt_long_system,
+ ],
+ },
+}
diff --git a/src/test/modules/test_getopt_long/t/001_getopt_long.pl b/src/test/modules/test_getopt_long/t/001_getopt_long.pl
new file mode 100644
index 00000000000..85c49d7d678
--- /dev/null
+++ b/src/test/modules/test_getopt_long/t/001_getopt_long.pl
@@ -0,0 +1,372 @@
+
+# Copyright (c) 2026, PostgreSQL Global Development Group
+
+# Test the port implementation of getopt_long().
+
+use strict;
+use warnings FATAL => 'all';
+
+use PostgreSQL::Test::Utils;
+use Test::More;
+
+my $longopts = 'alpha,beta:,gamma::,*verbose,alphabet';
+
+# Each case is a paragraph: a "== title" line, optional "optstring:" and
+# "args:" lines (default "ab:" and none), then the expected output lines.
+# An optional final "stderr: REGEX" line gives the expected error message;
+# without it stderr must be empty. The system getopt_long() words messages
+# differently, so the regexes accept both forms.
+my $cases = <<'EOF';
+== no arguments
+--
+
+== only non-options
+args: x y
+-- x y
+
+== short no arg
+args: -a
+-a
+--
+
+== short attached arg
+args: -bfoo
+-b=foo
+--
+
+== short separate arg
+args: -b foo
+-b=foo
+--
+
+== short bundled
+args: -ab foo
+-a
+-b=foo
+--
+
+== short bundled, attached arg
+args: -abfoo
+-a
+-b=foo
+--
+
+== short arg looks like option
+args: -b -a
+-b=-a
+--
+
+== short missing arg
+args: -b
+?
+--
+stderr: requires an argument -- '?b'?
+
+== short missing arg, silent
+optstring: :ab:
+args: -b
+:
+--
+
+== short missing arg at end of bundle
+args: -ab
+-a
+?
+--
+stderr: requires an argument -- '?b'?
+
+== short unknown
+args: -x
+?
+--
+stderr: (illegal|invalid) option -- '?x'?
+
+== short unknown, silent
+optstring: :ab:
+args: -x
+?
+--
+
+== short unknown in bundle
+args: -axa foo
+-a
+?
+-a
+-- foo
+stderr: (illegal|invalid) option -- '?x'?
+
+== long no arg
+args: --alpha
+--alpha
+--
+
+== long no arg then non-option
+args: --alpha foo
+--alpha
+-- foo
+
+== long no arg, longer name also defined
+args: --alphabet
+--alphabet
+--
+
+== long unknown
+args: --nope
+?
+--
+stderr: illegal option -- nope|unrecognized option [`']--nope'
+
+== long unknown, silent
+optstring: :ab:
+args: --nope
+?
+--
+
+== long unknown then option
+args: --nope -a
+?
+-a
+--
+stderr: illegal option -- nope|unrecognized option [`']--nope'
+
+== long required, equals
+args: --beta=foo
+--beta=foo
+--
+
+== long required, separate
+args: --beta foo
+--beta=foo
+--
+
+== long required, empty
+args: --beta=
+--beta=
+--
+
+== long required, value contains equals
+args: --beta=a=b
+--beta=a=b
+--
+
+== long required, double dash as value
+args: --beta --
+--beta=--
+--
+
+== long required, separate then more
+args: --beta foo -a bar
+--beta=foo
+-a
+-- bar
+
+== long required, value looks like option
+args: --beta --alpha
+--beta=--alpha
+--
+
+== long required, missing
+args: --beta
+?
+--
+stderr: requires an argument -- beta|[`']--beta' requires an argument
+
+== long required, missing, silent
+optstring: :ab:
+args: --beta
+:
+--
+
+== long required, missing after others
+args: -a --beta
+-a
+?
+--
+stderr: requires an argument -- beta|[`']--beta' requires an argument
+
+== long optional, alone
+args: --gamma
+--gamma
+--
+
+== long optional, alone, silent
+optstring: :ab:
+args: --gamma
+--gamma
+--
+
+== long optional, equals
+args: --gamma=foo
+--gamma=foo
+--
+
+== long optional, empty
+args: --gamma=
+--gamma=
+--
+
+== long optional, separate not consumed
+args: --gamma foo
+--gamma
+-- foo
+
+== long optional, separate not consumed, silent
+optstring: :ab:
+args: --gamma foo
+--gamma
+-- foo
+
+== long optional, first
+args: --gamma -a foo
+--gamma
+-a
+-- foo
+
+== long optional, first with equals
+args: --gamma=x -a foo
+--gamma=x
+-a
+-- foo
+
+== long optional, middle
+args: -a --gamma -b foo
+-a
+--gamma
+-b=foo
+--
+
+== long optional, middle before non-option
+args: -a --gamma foo -b bar
+-a
+--gamma
+-b=bar
+-- foo
+
+== long optional between non-options
+args: foo --gamma bar -a
+--gamma
+-a
+-- foo bar
+
+== long optional, last
+args: -a -b foo --gamma
+-a
+-b=foo
+--gamma
+--
+
+== long optional, last, silent
+optstring: :ab:
+args: -a --gamma
+-a
+--gamma
+--
+
+== long optional followed by long
+args: --gamma --alpha
+--gamma
+--alpha
+--
+
+== long optional followed by long required
+args: --gamma --beta foo
+--gamma
+--beta=foo
+--
+
+== long optional followed by lone dash
+args: --gamma -
+--gamma
+-- -
+
+== long optional repeated
+args: --gamma --gamma=1 --gamma
+--gamma
+--gamma=1
+--gamma
+--
+
+== long optional then double dash
+args: --gamma -- foo
+--gamma
+-- foo
+
+== long flag
+args: --verbose
+flag:--verbose
+--
+
+== long flag among others
+args: -a --verbose --gamma
+-a
+flag:--verbose
+--gamma
+--
+
+== double dash ends options
+args: -a -- -b foo
+-a
+-- -b foo
+
+== double dash first
+args: -- -a
+-- -a
+
+== double dash last
+args: -a --
+-a
+--
+
+== double dash repeated
+args: -a -- -- foo
+-a
+-- -- foo
+
+== non-options reordered to end
+args: foo -a bar -b baz qux
+-a
+-b=baz
+-- foo bar qux
+
+== lone dash is a non-option
+args: -a - -b x
+-a
+-b=x
+-- -
+
+== non-options then double dash
+args: foo -a -- -b bar
+-a
+-- foo -b bar
+EOF
+
+# test_getopt_long_system uses the system getopt_long() where there is one;
+# its outputs must match, only the error message wording differs.
+foreach my $exe ('test_getopt_long', 'test_getopt_long_system')
+{
+ foreach my $case (split /\n\n/, $cases)
+ {
+ my @lines = split /\n/, $case;
+ my ($title) = shift(@lines) =~ /^== (.*)/;
+ my $optstring = 'ab:';
+ $optstring = $1 if $lines[0] =~ /^optstring: (.*)/ and shift @lines;
+ my @args;
+ @args = split ' ', $1 if $lines[0] =~ /^args: (.*)/ and shift @lines;
+ my $stderr_re;
+ $stderr_re = $1 if $lines[-1] =~ /^stderr: (.*)/ and pop @lines;
+
+ my ($stdout, $stderr) =
+ run_command([ $exe, $optstring, $longopts, @args ]);
+ is($stdout, join("\n", @lines), "$exe $title: output");
+ if (defined $stderr_re)
+ {
+ like($stderr, qr/$stderr_re/, "$exe $title: stderr");
+ }
+ else
+ {
+ is($stderr, '', "$exe $title: no stderr");
+ }
+ }
+}
+
+done_testing();
diff --git a/src/test/modules/test_getopt_long/test_getopt_long.c b/src/test/modules/test_getopt_long/test_getopt_long.c
new file mode 100644
index 00000000000..9027e55eb52
--- /dev/null
+++ b/src/test/modules/test_getopt_long/test_getopt_long.c
@@ -0,0 +1,149 @@
+/*-------------------------------------------------------------------------
+ *
+ * test_getopt_long.c
+ * Test program for the src/port implementation of getopt_long()
+ *
+ * Copyright (c) 2026, PostgreSQL Global Development Group
+ *
+ * IDENTIFICATION
+ * src/test/modules/test_getopt_long/test_getopt_long.c
+ *
+ * Usage: test_getopt_long OPTSTRING LONGOPTS [ARG ...]
+ *
+ * LONGOPTS is a comma separated list of long option names, each optionally
+ * followed by ":" (required argument) or "::" (optional argument), and
+ * optionally prefixed with "*" to make getopt_long() set a flag variable
+ * instead of returning a value. The remaining ARGs are parsed with
+ * getopt_long() and each return is printed on its own line:
+ *
+ * -x short option x
+ * -x=VALUE short option x with argument VALUE
+ * --name long option name
+ * --name=VALUE long option name with argument VALUE
+ * flag:--name long option name, delivered via its flag pointer
+ * ? BADCH (unknown option or missing argument)
+ * : BADARG (missing argument, optstring starts with ':')
+ *
+ * After getopt_long() returns -1, a final line "--" lists the remaining
+ * (non-option) arguments, space separated.
+ *
+ * src/port/getopt_long.c is compiled into this program directly so the
+ * port implementation is tested even on platforms where libpgport would
+ * normally use the system's getopt_long(). test_getopt_long_system is the
+ * same program linked without it, for comparison against the system one.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres_fe.h"
+
+#include "getopt_long.h"
+
+#define MAX_LONGOPTS 32
+
+/* long options without a flag return this plus their index */
+#define LONGOPT_BASE 1000
+
+static struct option longopts[MAX_LONGOPTS + 1];
+static char *longnames[MAX_LONGOPTS];
+static int flagval;
+
+static void
+parse_longopts(char *spec)
+{
+ int n = 0;
+ char *tok;
+
+ for (tok = strtok(spec, ","); tok != NULL; tok = strtok(NULL, ","))
+ {
+ struct option *opt = &longopts[n];
+ char *colon;
+
+ if (n >= MAX_LONGOPTS)
+ {
+ fprintf(stderr, "too many long options\n");
+ exit(1);
+ }
+
+ if (tok[0] == '*')
+ {
+ tok++;
+ opt->flag = &flagval;
+ opt->val = n + 1;
+ }
+ else
+ {
+ opt->flag = NULL;
+ opt->val = LONGOPT_BASE + n;
+ }
+
+ colon = strchr(tok, ':');
+ if (colon == NULL)
+ opt->has_arg = no_argument;
+ else if (colon[1] == ':')
+ opt->has_arg = optional_argument;
+ else
+ opt->has_arg = required_argument;
+ if (colon != NULL)
+ *colon = '\0';
+
+ longnames[n] = tok;
+ opt->name = tok;
+ n++;
+ }
+
+ longopts[n].name = NULL;
+}
+
+int
+main(int argc, char **argv)
+{
+ const char *optstring;
+ char **args;
+ int nargs;
+ int c;
+
+ if (argc < 3)
+ {
+ fprintf(stderr, "Usage: %s OPTSTRING LONGOPTS [ARG ...]\n", argv[0]);
+ exit(1);
+ }
+
+ optstring = argv[1];
+ parse_longopts(argv[2]);
+
+ /* build the argv that getopt_long() will see, and may reorder */
+ nargs = argc - 2;
+ args = palloc((nargs + 1) * sizeof(char *));
+ args[0] = argv[0];
+ for (int i = 1; i < nargs; i++)
+ args[i] = argv[i + 2];
+ args[nargs] = NULL;
+
+ while ((c = getopt_long(nargs, args, optstring, longopts, NULL)) != -1)
+ {
+ if (c == 0)
+ {
+ printf("flag:--%s\n", longnames[flagval - 1]);
+ continue;
+ }
+
+ if (c >= LONGOPT_BASE)
+ printf("--%s", longnames[c - LONGOPT_BASE]);
+ else if (c == '?' || c == ':')
+ printf("%c", c);
+ else
+ printf("-%c", c);
+
+ if (optarg != NULL)
+ printf("=%s", optarg);
+ printf("\n");
+ }
+
+ printf("--");
+ for (int i = optind; i < nargs; i++)
+ printf(" %s", args[i]);
+ printf("\n");
+
+ return 0;
+}
--
2.17.1
[text/x-patch] 0003-Do-not-take-a-moved-non-option-as-an-option-argument.patch (1.8K, ../../CAH7T-arxDuVCSkorO=k7+M-_JV0JFzMpN_EtKMyD2K0RDqZ2OA@mail.gmail.com/5-0003-Do-not-take-a-moved-non-option-as-an-option-argument.patch)
download | inline diff:
From e6be21d4b1070e94dcab0d92a590186676823b71 Mon Sep 17 00:00:00 2001
From: Sehrope Sarkuni <sehrope@jackdb.com>
Date: Sat, 29 Aug 2026 13:55:44 +0000
Subject: [PATCH 3/4] Do not take a moved non-option as an option argument in
port getopt_long()
Non-options are moved to the end of argv as they are found, so one
that preceded an option was already behind it when the option looked
for its argument. "foo -b" parsed as -b with argument foo instead of
reporting the missing argument.
---
src/port/getopt_long.c | 14 ++++++++++++--
1 file changed, 12 insertions(+), 2 deletions(-)
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 7489a0206ee..269762280d8 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -65,6 +65,7 @@ getopt_long(int argc, char *const argv[],
const char *oli; /* option letter list index */
static int nonopt_start = -1;
static bool force_nonopt = false;
+ int argend;
if (!*place)
{ /* update scanning pointer */
@@ -135,11 +136,18 @@ retry:
if (has_arg != no_argument)
{
+ /*
+ * Non-options already moved to the back of argv
+ * preceded this option, so they cannot be its
+ * argument.
+ */
+ argend = (nonopt_start == -1) ? argc : nonopt_start;
+
if (place[namelen] == '=')
optarg = place + namelen + 1;
else if (has_arg == optional_argument)
optarg = NULL;
- else if (optind < argc - 1)
+ else if (optind < argend - 1)
{
optind++;
optarg = argv[optind];
@@ -213,9 +221,11 @@ retry:
}
else
{ /* need an argument */
+ argend = (nonopt_start == -1) ? argc : nonopt_start;
+
if (*place) /* no white space */
optarg = place;
- else if (argc <= ++optind)
+ else if (argend <= ++optind)
{ /* no arg */
place = EMSG;
if (*optstring == ':')
--
2.17.1
^ permalink raw reply [nested|flat] 5+ messages in thread
* Re: [PATCH] Fix getopt_long() argument handling and add tests
2026-08-29 16:57 [PATCH] Fix getopt_long() argument handling and add tests Sehrope Sarkuni <sehrope@jackdb.com>
@ 2026-09-04 21:58 ` Nathan Bossart <nathandbossart@gmail.com>
2026-09-05 12:04 ` Re: [PATCH] Fix getopt_long() argument handling and add tests Sehrope Sarkuni <sehrope@jackdb.com>
2026-09-10 10:56 ` Re: [PATCH] Fix getopt_long() argument handling and add tests solai v <solai.cdac@gmail.com>
0 siblings, 2 replies; 5+ messages in thread
From: Nathan Bossart @ 2026-09-04 21:58 UTC (permalink / raw)
To: Sehrope Sarkuni <sehrope@jackdb.com>; +Cc: pgsql-hackers
On Sat, Aug 29, 2026 at 12:57:00PM -0400, Sehrope Sarkuni wrote:
> While reworking the pg_waldump TAP test [1] I ran "pg_waldump --stats
> --limit 5" and it failed on Windows CI with 'could not locate WAL file
> "5"'. For a long option with optional_argument and no "=", the port
> getopt_long() increments optind in the missing-argument branch and
> again at the end of the long option path, so the next option is
> skipped and its value becomes a positional argument. The same branch
> returns BADARG when optstring starts with a colon although nothing is
> missing.
>
> [...]
>
> 0003 fixes a second problem the tests turned up. The port moves each
> non-option to the end of argv as soon as it sees it, so by the time a
> following option looks for its argument, a non-option that came before
> it on the command line is already sitting after it and gets taken:
> "pg_amcheck mydb --jobs" parses as --jobs=mydb rather than reporting
> the missing argument. The system getopt_long() reports the missing
> argument. The fix bounds the argument search by the start of the
> moved block. 0004 adds the test cases for it, kept separate in case
> 0003 is backpatched without the test module.
Nice finds. I've attached what I have staged for commit. I've tried to
simplify the diffs a bit, and I also got rid of the new test module in
favor of adding cases to existing test files. I don't think we need to
bother back-patching 0001; the bug went undiscovered for ~23 years. I can
try back-patching 0002 to v17, though.
--
nathan
From f04a179b4f24f79f54ce81c6060f45c86a803c34 Mon Sep 17 00:00:00 2001
From: Nathan Bossart <nathan@postgresql.org>
Date: Fri, 4 Sep 2026 16:37:01 -0500
Subject: [PATCH v2 1/2] Fix optional-argument handling in in-tree
getopt_long().
A long option with an optional argument that is given without "="
advances optind twice, so the following argument is skipped. For
example, "pg_waldump --stats --limit 5" complains that it cannot
locate WAL file "5". The same path also returns BADARG when
optstring starts with a colon, even though nothing is missing. To
fix, handle optional arguments before the missing-argument code,
which then only needs to deal with required arguments.
This is a bug fix and could be back-patched, but since this issue
went unnoticed for 23 years, I'm not going to bother.
Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
---
src/bin/pg_waldump/t/001_basic.pl | 3 +--
src/port/getopt_long.c | 11 +++++------
2 files changed, 6 insertions(+), 8 deletions(-)
diff --git a/src/bin/pg_waldump/t/001_basic.pl b/src/bin/pg_waldump/t/001_basic.pl
index 8beac19eaff..7b33efc6299 100644
--- a/src/bin/pg_waldump/t/001_basic.pl
+++ b/src/bin/pg_waldump/t/001_basic.pl
@@ -340,11 +340,10 @@ sub test_pg_waldump
my ($stdout, $stderr);
my $result = IPC::Run::run [
- 'pg_waldump',
+ 'pg_waldump', @opts,
'--start' => $startlsn,
'--end' => $endlsn,
'--path' => $path,
- @opts
],
'>' => \$stdout,
'2>' => \$stderr;
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 2e869fed58b..0a9a50189f1 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -137,8 +137,9 @@ retry:
{
if (place[namelen] == '=')
optarg = place + namelen + 1;
- else if (optind < argc - 1 &&
- has_arg == required_argument)
+ else if (has_arg == optional_argument)
+ optarg = NULL;
+ else if (optind < argc - 1)
{
optind++;
optarg = argv[optind];
@@ -152,16 +153,14 @@ retry:
return BADARG;
}
- if (opterr && has_arg == required_argument)
+ if (opterr)
fprintf(stderr,
"%s: option requires an argument -- %s\n",
argv[0], place);
place = EMSG;
- if (has_arg == required_argument)
- return BADCH;
- optarg = NULL;
+ return BADCH;
}
}
else
--
2.55.0
From b0fcd1a01b01e9e15d76dcadfae13f33f9ab8bb1 Mon Sep 17 00:00:00 2001
From: Nathan Bossart <nathan@postgresql.org>
Date: Fri, 4 Sep 2026 16:47:53 -0500
Subject: [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
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
Attachments:
[text/plain] v2-0001-Fix-optional-argument-handling-in-in-tree-getopt_.patch (2.4K, ../../aps-8xDMYZfVQ5rQ@nathan/2-v2-0001-Fix-optional-argument-handling-in-in-tree-getopt_.patch)
download | inline diff:
From f04a179b4f24f79f54ce81c6060f45c86a803c34 Mon Sep 17 00:00:00 2001
From: Nathan Bossart <nathan@postgresql.org>
Date: Fri, 4 Sep 2026 16:37:01 -0500
Subject: [PATCH v2 1/2] Fix optional-argument handling in in-tree
getopt_long().
A long option with an optional argument that is given without "="
advances optind twice, so the following argument is skipped. For
example, "pg_waldump --stats --limit 5" complains that it cannot
locate WAL file "5". The same path also returns BADARG when
optstring starts with a colon, even though nothing is missing. To
fix, handle optional arguments before the missing-argument code,
which then only needs to deal with required arguments.
This is a bug fix and could be back-patched, but since this issue
went unnoticed for 23 years, I'm not going to bother.
Author: Sehrope Sarkuni <sehrope@jackdb.com>
Discussion: https://postgr.es/m/CAH7T-arxDuVCSkorO%3Dk7%2BM-_JV0JFzMpN_EtKMyD2K0RDqZ2OA%40mail.gmail.com
---
src/bin/pg_waldump/t/001_basic.pl | 3 +--
src/port/getopt_long.c | 11 +++++------
2 files changed, 6 insertions(+), 8 deletions(-)
diff --git a/src/bin/pg_waldump/t/001_basic.pl b/src/bin/pg_waldump/t/001_basic.pl
index 8beac19eaff..7b33efc6299 100644
--- a/src/bin/pg_waldump/t/001_basic.pl
+++ b/src/bin/pg_waldump/t/001_basic.pl
@@ -340,11 +340,10 @@ sub test_pg_waldump
my ($stdout, $stderr);
my $result = IPC::Run::run [
- 'pg_waldump',
+ 'pg_waldump', @opts,
'--start' => $startlsn,
'--end' => $endlsn,
'--path' => $path,
- @opts
],
'>' => \$stdout,
'2>' => \$stderr;
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index 2e869fed58b..0a9a50189f1 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -137,8 +137,9 @@ retry:
{
if (place[namelen] == '=')
optarg = place + namelen + 1;
- else if (optind < argc - 1 &&
- has_arg == required_argument)
+ else if (has_arg == optional_argument)
+ optarg = NULL;
+ else if (optind < argc - 1)
{
optind++;
optarg = argv[optind];
@@ -152,16 +153,14 @@ retry:
return BADARG;
}
- if (opterr && has_arg == required_argument)
+ if (opterr)
fprintf(stderr,
"%s: option requires an argument -- %s\n",
argv[0], place);
place = EMSG;
- if (has_arg == required_argument)
- return BADCH;
- optarg = NULL;
+ return BADCH;
}
}
else
--
2.55.0
[text/plain] v2-0002-Fix-option-argument-lookup-in-in-tree-getopt_long.patch (3.0K, ../../aps-8xDMYZfVQ5rQ@nathan/3-v2-0002-Fix-option-argument-lookup-in-in-tree-getopt_long.patch)
download | inline diff:
From b0fcd1a01b01e9e15d76dcadfae13f33f9ab8bb1 Mon Sep 17 00:00:00 2001
From: Nathan Bossart <nathan@postgresql.org>
Date: Fri, 4 Sep 2026 16:47:53 -0500
Subject: [PATCH v2 2/2] Fix option argument lookup in in-tree getopt_long().
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
^ permalink raw reply [nested|flat] 5+ messages in thread
* Re: [PATCH] Fix getopt_long() argument handling and add tests
2026-08-29 16:57 [PATCH] Fix getopt_long() argument handling and add tests Sehrope Sarkuni <sehrope@jackdb.com>
2026-09-04 21:58 ` Re: [PATCH] Fix getopt_long() argument handling and add tests Nathan Bossart <nathandbossart@gmail.com>
@ 2026-09-05 12:04 ` Sehrope Sarkuni <sehrope@jackdb.com>
1 sibling, 0 replies; 5+ messages in thread
From: Sehrope Sarkuni @ 2026-09-05 12:04 UTC (permalink / raw)
To: Nathan Bossart <nathandbossart@gmail.com>; +Cc: pgsql-hackers
On Fri, Sep 4, 2026 at 5:58 PM Nathan Bossart <nathandbossart@gmail.com> wrote:
> Nice finds. I've attached what I have staged for commit.
Thanks for the review!
I tried it out locally and also layered the new test module on top of
your patches. Everything passes.
> I've tried to simplify the diffs a bit,
Yes, I like your 0002 better. Easier to reason about the state machine
by setting nonopt_start once in the beginning instead of the ternaries.
> and I also got rid of the new test module in favor of adding cases to
> existing test files. I don't think we need to bother back-patching
> 0001; the bug went undiscovered for ~23 years.
Sounds good. It was useful for verifying the change but it's a bit
overkill to include.
Regards,
-- Sehrope Sarkuni
Founder & CEO | JackDB, Inc. | https://www.jackdb.com/
^ permalink raw reply [nested|flat] 5+ messages in thread
* Re: [PATCH] Fix getopt_long() argument handling and add tests
2026-08-29 16:57 [PATCH] Fix getopt_long() argument handling and add tests Sehrope Sarkuni <sehrope@jackdb.com>
2026-09-04 21:58 ` Re: [PATCH] Fix getopt_long() argument handling and add tests Nathan Bossart <nathandbossart@gmail.com>
@ 2026-09-10 10:56 ` solai v <solai.cdac@gmail.com>
2026-09-11 20:25 ` Re: [PATCH] Fix getopt_long() argument handling and add tests Nathan Bossart <nathandbossart@gmail.com>
1 sibling, 1 reply; 5+ messages in thread
From: solai v @ 2026-09-10 10:56 UTC (permalink / raw)
To: Nathan Bossart <nathandbossart@gmail.com>; +Cc: Sehrope Sarkuni <sehrope@jackdb.com>; pgsql-hackers
Hi all,
On Thu, Sep 10, 2026 at 11:46 AM Nathan Bossart
<nathandbossart@gmail.com> wrote:
>
> On Sat, Aug 29, 2026 at 12:57:00PM -0400, Sehrope Sarkuni wrote:
> > While reworking the pg_waldump TAP test [1] I ran "pg_waldump --stats
> > --limit 5" and it failed on Windows CI with 'could not locate WAL file
> > "5"'. For a long option with optional_argument and no "=", the port
> > getopt_long() increments optind in the missing-argument branch and
> > again at the end of the long option path, so the next option is
> > skipped and its value becomes a positional argument. The same branch
> > returns BADARG when optstring starts with a colon although nothing is
> > missing.
> >
> > [...]
> >
> > 0003 fixes a second problem the tests turned up. The port moves each
> > non-option to the end of argv as soon as it sees it, so by the time a
> > following option looks for its argument, a non-option that came before
> > it on the command line is already sitting after it and gets taken:
> > "pg_amcheck mydb --jobs" parses as --jobs=mydb rather than reporting
> > the missing argument. The system getopt_long() reports the missing
> > argument. The fix bounds the argument search by the start of the
> > moved block. 0004 adds the test cases for it, kept separate in case
> > 0003 is backpatched without the test module.
>
> Nice finds. I've attached what I have staged for commit. I've tried to
> simplify the diffs a bit, and I also got rid of the new test module in
> favor of adding cases to existing test files. I don't think we need to
> bother back-patching 0001; the bug went undiscovered for ~23 years. I can
> try back-patching 0002 to v17, though.
>
I tested both v2 patches on the latest master and was able to
reproduce both reported issues with the in-tree getopt_long()
implementation before applying the patches with the following
observations:
--stats --limit 5 incorrectly skipped --limit and left 5 as a
positional argument.
mydb --jobs incorrectly consumed mydb as the argument to --jobs.
After applying both patches, both cases behaved correctly. And I ran
the affected TAP tests and everything got passed. Also I reviewed the
changes in src/port/getopt_long.c and the corresponding test
additions. I did not find any issues with the v2 patches.
The patch looks good to me.
Regards,
Solai
^ permalink raw reply [nested|flat] 5+ messages in thread
* Re: [PATCH] Fix getopt_long() argument handling and add tests
2026-08-29 16:57 [PATCH] Fix getopt_long() argument handling and add tests Sehrope Sarkuni <sehrope@jackdb.com>
2026-09-04 21:58 ` Re: [PATCH] Fix getopt_long() argument handling and add tests Nathan Bossart <nathandbossart@gmail.com>
2026-09-10 10:56 ` Re: [PATCH] Fix getopt_long() argument handling and add tests solai v <solai.cdac@gmail.com>
@ 2026-09-11 20:25 ` Nathan Bossart <nathandbossart@gmail.com>
0 siblings, 0 replies; 5+ messages in thread
From: Nathan Bossart @ 2026-09-11 20:25 UTC (permalink / raw)
To: solai v <solai.cdac@gmail.com>; +Cc: Sehrope Sarkuni <sehrope@jackdb.com>; pgsql-hackers
On Thu, Sep 10, 2026 at 04:26:05PM +0530, solai v wrote:
> After applying both patches, both cases behaved correctly. And I ran
> the affected TAP tests and everything got passed. Also I reviewed the
> changes in src/port/getopt_long.c and the corresponding test
> additions. I did not find any issues with the v2 patches.
> The patch looks good to me.
Thanks for reviewing. Committed.
--
nathan
^ permalink raw reply [nested|flat] 5+ messages in thread
end of thread, other threads:[~2026-09-11 20:25 UTC | newest]
Thread overview: 5+ messages (download: mbox mbox.gz follow: Atom feed)
-- links below jump to the message on this page --
2026-08-29 16:57 [PATCH] Fix getopt_long() argument handling and add tests Sehrope Sarkuni <sehrope@jackdb.com>
2026-09-04 21:58 ` Nathan Bossart <nathandbossart@gmail.com>
2026-09-05 12:04 ` Sehrope Sarkuni <sehrope@jackdb.com>
2026-09-10 10:56 ` solai v <solai.cdac@gmail.com>
2026-09-11 20:25 ` Nathan Bossart <nathandbossart@gmail.com>
This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox