On Thu, Feb 22, 2018 at 9:24 PM, Tom Lane <[email protected]> wrote:
> Magnus Hagander <[email protected]> writes:
> > I hacked up an attempt to do this. It does seem to work in the very
> simple
> > case, but it does requiring changing the order in InitPostgres() to load
> > the startup packet before validating those.
>
> I doubt that's safe. It requires, to name just one thing, an assumption
> that no processing done in process_startup_options has any need to know
> the database encoding, which is established by CheckMyDatabase. Thus
> for instance, if any GUC settings carried in the startup packet include
> non-ASCII characters, the wrong things will happen.
>
> You could possibly make it work with more aggressive refactoring, but
> I remain of the opinion that this is a fundamentally bad idea anyhow.
> A GUC of this kind is just ripe for abuse, and I don't think it's solving
> any problem we really need solved.
>
Here's another attempt at moving this one forward. Basically this adds a
new GucSource being GUC_S_CLIENT_EARLY. It now runs through the parameters
once before CheckMyDatabase, with source set to GUC_S_CLIENT_EARLY. In this
source, *only* parameters that are flagged as GUC_ALLOW_EARLY will be set,
any other parameters are ignored (without error). For now, only the
ignore_connection_restriction is allowed at this stage. Then it runs
CheckMyDatabase(), and after that it runs through all the parameters again,
now with the GUC_S_CLIENT source as usual, which will now process all
other variables.
--
Magnus Hagander
Me: https://www.hagander.net/ <http://www.hagander.net/>
Work: https://www.redpill-linpro.com/ <http://www.redpill-linpro.com/>
diff --git a/src/backend/postmaster/checksumhelper.c b/src/backend/postmaster/checksumhelper.c
index 44535f9976..997bffa416 100644
--- a/src/backend/postmaster/checksumhelper.c
+++ b/src/backend/postmaster/checksumhelper.c
@@ -586,6 +588,8 @@ ChecksumHelperWorkerMain(Datum arg)
ereport(DEBUG1,
(errmsg("Checksum worker starting for database oid %d", dboid)));
+ SetConfigOption("ignore_connection_restriction", "true", PGC_SU_BACKEND, PGC_S_OVERRIDE);
+
BackgroundWorkerInitializeConnectionByOid(dboid, InvalidOid);
/*
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 6dc2095b9a..f08669ddb3 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -3358,7 +3358,7 @@ get_stats_option_name(const char *arg)
*/
void
process_postgres_switches(int argc, char *argv[], GucContext ctx,
- const char **dbname)
+ const char **dbname, bool early_processing)
{
bool secure = (ctx == PGC_POSTMASTER);
int errs = 0;
@@ -3376,6 +3376,10 @@ process_postgres_switches(int argc, char *argv[], GucContext ctx,
argc--;
}
}
+ else if (early_processing)
+ {
+ gucsource = PGC_S_CLIENT_EARLY;
+ }
else
{
gucsource = PGC_S_CLIENT; /* switches came from client */
@@ -3641,7 +3645,7 @@ PostgresMain(int argc, char *argv[],
/*
* Parse command-line options.
*/
- process_postgres_switches(argc, argv, PGC_POSTMASTER, &dbname);
+ process_postgres_switches(argc, argv, PGC_POSTMASTER, &dbname, false);
/* Must have gotten a database name, or have a default (the username) */
if (dbname == NULL)
diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c
index 484628987f..2b00cdebdb 100644
--- a/src/backend/utils/init/postinit.c
+++ b/src/backend/utils/init/postinit.c
@@ -73,7 +73,7 @@ static void StatementTimeoutHandler(void);
static void LockTimeoutHandler(void);
static void IdleInTransactionSessionTimeoutHandler(void);
static bool ThereIsAtLeastOneRole(void);
-static void process_startup_options(Port *port, bool am_superuser);
+static void process_startup_options(Port *port, bool am_superuser, bool early_processing);
static void process_settings(Oid databaseid, Oid roleid);
@@ -326,7 +326,7 @@ CheckMyDatabase(const char *name, bool am_superuser)
/*
* Check that the database is currently allowing connections.
*/
- if (!dbform->datallowconn)
+ if (!dbform->datallowconn && !IgnoreDatAllowConn)
ereport(FATAL,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("database \"%s\" is not currently accepting connections",
@@ -811,7 +811,7 @@ InitPostgres(const char *in_dbname, Oid dboid, const char *username,
{
/* process any options passed in the startup packet */
if (MyProcPort != NULL)
- process_startup_options(MyProcPort, am_superuser);
+ process_startup_options(MyProcPort, am_superuser, false);
/* Apply PostAuthDelay as soon as we've read all options */
if (PostAuthDelay > 0)
@@ -999,6 +999,10 @@ InitPostgres(const char *in_dbname, Oid dboid, const char *username,
/* set up ACL framework (so CheckMyDatabase can check permissions) */
initialize_acl();
+ /* Process "early" GUCs in startup packet */
+ if (MyProcPort != NULL)
+ process_startup_options(MyProcPort, am_superuser, true);
+
/*
* Re-read the pg_database row for our database, check permissions and set
* up database-specific GUC settings. We can't do this until all the
@@ -1014,7 +1018,7 @@ InitPostgres(const char *in_dbname, Oid dboid, const char *username,
* because we didn't know if client is a superuser.
*/
if (MyProcPort != NULL)
- process_startup_options(MyProcPort, am_superuser);
+ process_startup_options(MyProcPort, am_superuser, false);
/* Process pg_db_role_setting options */
process_settings(MyDatabaseId, GetSessionUserId());
@@ -1051,7 +1055,7 @@ InitPostgres(const char *in_dbname, Oid dboid, const char *username,
* settings passed in the startup packet.
*/
static void
-process_startup_options(Port *port, bool am_superuser)
+process_startup_options(Port *port, bool am_superuser, bool early_processing)
{
GucContext gucctx;
ListCell *gucopts;
@@ -1086,7 +1090,7 @@ process_startup_options(Port *port, bool am_superuser)
Assert(ac < maxac);
- (void) process_postgres_switches(ac, av, gucctx, NULL);
+ (void) process_postgres_switches(ac, av, gucctx, NULL, early_processing);
}
/*
@@ -1105,10 +1109,11 @@ process_startup_options(Port *port, bool am_superuser)
value = lfirst(gucopts);
gucopts = lnext(gucopts);
- SetConfigOption(name, value, gucctx, PGC_S_CLIENT);
+ SetConfigOption(name, value, gucctx, early_processing ? PGC_S_CLIENT_EARLY : PGC_S_CLIENT);
}
}
+
/*
* Load GUC settings from pg_db_role_setting.
*
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 039b63bb05..1653b6a63d 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -461,6 +461,7 @@ bool row_security;
bool check_function_bodies = true;
bool default_with_oids = false;
bool session_auth_is_superuser;
+bool IgnoreDatAllowConn;
int log_min_error_statement = ERROR;
int log_min_messages = WARNING;
@@ -1648,6 +1649,19 @@ static struct config_bool ConfigureNamesBool[] =
},
{
+ {"ignore_connection_restriction", PGC_SU_BACKEND, DEVELOPER_OPTIONS,
+ gettext_noop("Ignores the datallowconn restriction on database."),
+ NULL,
+ GUC_NO_SHOW_ALL | GUC_NO_RESET_ALL | GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE | GUC_ALLOW_EARLY
+
+ },
+ &IgnoreDatAllowConn,
+ false,
+ NULL, NULL, NULL
+ },
+
+
+ {
{"lo_compat_privileges", PGC_SUSET, COMPAT_OPTIONS_PREVIOUS,
gettext_noop("Enables backward compatibility mode for privilege checks on large objects."),
gettext_noop("Skips privilege checks when reading or modifying large objects, "
@@ -6086,6 +6100,11 @@ set_config_option(const char *name, const char *value,
name)));
return 0;
}
+ /* Ignore, but not reject, if we are in early processing but param not allowed */
+ if (source == PGC_S_CLIENT_EARLY &&
+ (record->flags & GUC_ALLOW_EARLY) == 0)
+ return 0;
+
/* FALL THRU to process the same as PGC_BACKEND */
case PGC_BACKEND:
if (context == PGC_SIGHUP)
diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h
index a4574cd533..666508cfaf 100644
--- a/src/include/miscadmin.h
+++ b/src/include/miscadmin.h
@@ -424,6 +424,7 @@ extern void BaseInit(void);
/* in utils/init/miscinit.c */
extern bool IgnoreSystemIndexes;
+extern bool IgnoreDatAllowConn;
extern PGDLLIMPORT bool process_shared_preload_libraries_in_progress;
extern char *session_preload_libraries_string;
extern char *shared_preload_libraries_string;
diff --git a/src/include/tcop/tcopprot.h b/src/include/tcop/tcopprot.h
index 63b4e4864d..3443485bab 100644
--- a/src/include/tcop/tcopprot.h
+++ b/src/include/tcop/tcopprot.h
@@ -75,7 +75,8 @@ extern void ProcessClientReadInterrupt(bool blocked);
extern void ProcessClientWriteInterrupt(bool blocked);
extern void process_postgres_switches(int argc, char *argv[],
- GucContext ctx, const char **dbname);
+ GucContext ctx, const char **dbname,
+ bool early_processing);
extern void PostgresMain(int argc, char *argv[],
const char *dbname,
const char *username) pg_attribute_noreturn();
diff --git a/src/include/utils/guc.h b/src/include/utils/guc.h
index 2e03640c0b..b2bad5d455 100644
--- a/src/include/utils/guc.h
+++ b/src/include/utils/guc.h
@@ -117,7 +117,8 @@ typedef enum
PGC_S_OVERRIDE, /* special case to forcibly set default */
PGC_S_INTERACTIVE, /* dividing line for error reporting */
PGC_S_TEST, /* test per-database or per-user setting */
- PGC_S_SESSION /* SET command */
+ PGC_S_SESSION, /* SET command */
+ PGC_S_CLIENT_EARLY /* from client before encoding etc set */
} GucSource;
/*
@@ -229,6 +230,8 @@ typedef enum
#define GUC_UNIT (GUC_UNIT_MEMORY | GUC_UNIT_TIME)
+#define GUC_ALLOW_EARLY 0x100000 /* allow applying GUC before setting encodings etc */
+
/* GUC vars that are actually declared in guc.c, rather than elsewhere */
extern bool log_duration;