1
0
mirror of https://github.com/postgres/postgres.git synced 2025-06-23 14:01:44 +03:00

Provide a better error message for misplaced dispatch options.

Before this patch, misplacing a special must-be-first option for
dispatching to a subprogram (e.g., postgres -D . --single) would
fail with an error like

	FATAL:  --single requires a value

This patch adjusts this error to more accurately complain that the
special option wasn't listed first.  The aforementioned error
message now looks like

	FATAL:  --single must be first argument

The dispatch option parsing code has been refactored for use
wherever ParseLongOption() is called.  Beyond the obvious advantage
of avoiding code duplication, this should prevent similar problems
when new dispatch options are added.  Note that we assume that none
of the dispatch option names match another valid command-line
argument, such as the name of a configuration parameter.

Ideally, we'd remove this must-be-first requirement for these
options, but after some investigation, we decided that wasn't worth
the added complexity and behavior changes.

Author: Nathan Bossart, Greg Sabino Mullane
Reviewed-by: Greg Sabino Mullane, Peter Eisentraut, Álvaro Herrera, Tom Lane
Discussion: https://postgr.es/m/CAKAnmmJkZtZAiSryho%3DgYpbvC7H-HNjEDAh16F3SoC9LPu8rqQ%40mail.gmail.com
This commit is contained in:
Nathan Bossart
2024-12-04 15:04:15 -06:00
parent 24c1c63387
commit 76fd342496
6 changed files with 133 additions and 16 deletions

View File

@ -224,8 +224,21 @@ BootstrapModeMain(int argc, char *argv[], bool check_only)
case 'B': case 'B':
SetConfigOption("shared_buffers", optarg, PGC_POSTMASTER, PGC_S_ARGV); SetConfigOption("shared_buffers", optarg, PGC_POSTMASTER, PGC_S_ARGV);
break; break;
case 'c':
case '-': case '-':
/*
* Error if the user misplaced a special must-be-first option
* for dispatching to a subprogram. parse_dispatch_option()
* returns DISPATCH_POSTMASTER if it doesn't find a match, so
* error for anything else.
*/
if (parse_dispatch_option(optarg) != DISPATCH_POSTMASTER)
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("--%s must be first argument", optarg)));
/* FALLTHROUGH */
case 'c':
{ {
char *name, char *name,
*value; *value;

View File

@ -43,6 +43,19 @@
const char *progname; const char *progname;
static bool reached_main = false; static bool reached_main = false;
/* names of special must-be-first options for dispatching to subprograms */
static const char *const DispatchOptionNames[] =
{
[DISPATCH_CHECK] = "check",
[DISPATCH_BOOT] = "boot",
[DISPATCH_FORKCHILD] = "forkchild",
[DISPATCH_DESCRIBE_CONFIG] = "describe-config",
[DISPATCH_SINGLE] = "single",
/* DISPATCH_POSTMASTER has no name */
};
StaticAssertDecl(lengthof(DispatchOptionNames) == DISPATCH_POSTMASTER,
"array length mismatch");
static void startup_hacks(const char *progname); static void startup_hacks(const char *progname);
static void init_locale(const char *categoryname, int category, const char *locale); static void init_locale(const char *categoryname, int category, const char *locale);
@ -57,6 +70,7 @@ int
main(int argc, char *argv[]) main(int argc, char *argv[])
{ {
bool do_check_root = true; bool do_check_root = true;
DispatchOption dispatch_option = DISPATCH_POSTMASTER;
reached_main = true; reached_main = true;
@ -179,26 +193,72 @@ main(int argc, char *argv[])
* Dispatch to one of various subprograms depending on first argument. * Dispatch to one of various subprograms depending on first argument.
*/ */
if (argc > 1 && strcmp(argv[1], "--check") == 0) if (argc > 1 && argv[1][0] == '-' && argv[1][1] == '-')
BootstrapModeMain(argc, argv, true); dispatch_option = parse_dispatch_option(&argv[1][2]);
else if (argc > 1 && strcmp(argv[1], "--boot") == 0)
BootstrapModeMain(argc, argv, false); switch (dispatch_option)
{
case DISPATCH_CHECK:
BootstrapModeMain(argc, argv, true);
break;
case DISPATCH_BOOT:
BootstrapModeMain(argc, argv, false);
break;
case DISPATCH_FORKCHILD:
#ifdef EXEC_BACKEND #ifdef EXEC_BACKEND
else if (argc > 1 && strncmp(argv[1], "--forkchild", 11) == 0) SubPostmasterMain(argc, argv);
SubPostmasterMain(argc, argv); #else
Assert(false); /* should never happen */
#endif #endif
else if (argc > 1 && strcmp(argv[1], "--describe-config") == 0) break;
GucInfoMain(); case DISPATCH_DESCRIBE_CONFIG:
else if (argc > 1 && strcmp(argv[1], "--single") == 0) GucInfoMain();
PostgresSingleUserMain(argc, argv, break;
strdup(get_user_name_or_exit(progname))); case DISPATCH_SINGLE:
else PostgresSingleUserMain(argc, argv,
PostmasterMain(argc, argv); strdup(get_user_name_or_exit(progname)));
break;
case DISPATCH_POSTMASTER:
PostmasterMain(argc, argv);
break;
}
/* the functions above should not return */ /* the functions above should not return */
abort(); abort();
} }
/*
* Returns the matching DispatchOption value for the given option name. If no
* match is found, DISPATCH_POSTMASTER is returned.
*/
DispatchOption
parse_dispatch_option(const char *name)
{
for (int i = 0; i < lengthof(DispatchOptionNames); i++)
{
/*
* Unlike the other dispatch options, "forkchild" takes an argument,
* so we just look for the prefix for that one. For non-EXEC_BACKEND
* builds, we never want to return DISPATCH_FORKCHILD, so skip over it
* in that case.
*/
if (i == DISPATCH_FORKCHILD)
{
#ifdef EXEC_BACKEND
if (strncmp(DispatchOptionNames[DISPATCH_FORKCHILD], name,
strlen(DispatchOptionNames[DISPATCH_FORKCHILD])) == 0)
return DISPATCH_FORKCHILD;
#endif
continue;
}
if (strcmp(DispatchOptionNames[i], name) == 0)
return (DispatchOption) i;
}
/* no match means this is a postmaster */
return DISPATCH_POSTMASTER;
}
/* /*
* Place platform-specific startup hacks here. This is the right * Place platform-specific startup hacks here. This is the right

View File

@ -589,8 +589,21 @@ PostmasterMain(int argc, char *argv[])
output_config_variable = strdup(optarg); output_config_variable = strdup(optarg);
break; break;
case 'c':
case '-': case '-':
/*
* Error if the user misplaced a special must-be-first option
* for dispatching to a subprogram. parse_dispatch_option()
* returns DISPATCH_POSTMASTER if it doesn't find a match, so
* error for anything else.
*/
if (parse_dispatch_option(optarg) != DISPATCH_POSTMASTER)
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("--%s must be first argument", optarg)));
/* FALLTHROUGH */
case 'c':
{ {
char *name, char *name,
*value; *value;

View File

@ -3947,8 +3947,21 @@ process_postgres_switches(int argc, char *argv[], GucContext ctx,
/* ignored for consistency with the postmaster */ /* ignored for consistency with the postmaster */
break; break;
case 'c':
case '-': case '-':
/*
* Error if the user misplaced a special must-be-first option
* for dispatching to a subprogram. parse_dispatch_option()
* returns DISPATCH_POSTMASTER if it doesn't find a match, so
* error for anything else.
*/
if (parse_dispatch_option(optarg) != DISPATCH_POSTMASTER)
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("--%s must be first argument", optarg)));
/* FALLTHROUGH */
case 'c':
{ {
char *name, char *name,
*value; *value;

View File

@ -138,4 +138,21 @@ extern PMChild *FindPostmasterChildByPid(int pid);
*/ */
#define MAX_BACKENDS 0x3FFFF #define MAX_BACKENDS 0x3FFFF
/*
* These values correspond to the special must-be-first options for dispatching
* to various subprograms. parse_dispatch_option() can be used to convert an
* option name to one of these values.
*/
typedef enum DispatchOption
{
DISPATCH_CHECK,
DISPATCH_BOOT,
DISPATCH_FORKCHILD,
DISPATCH_DESCRIBE_CONFIG,
DISPATCH_SINGLE,
DISPATCH_POSTMASTER, /* must be last */
} DispatchOption;
extern DispatchOption parse_dispatch_option(const char *name);
#endif /* _POSTMASTER_H */ #endif /* _POSTMASTER_H */

View File

@ -619,6 +619,7 @@ DirectoryMethodFile
DisableTimeoutParams DisableTimeoutParams
DiscardMode DiscardMode
DiscardStmt DiscardStmt
DispatchOption
DistanceValue DistanceValue
DistinctExpr DistinctExpr
DoState DoState