On Thu, Feb 22, 2018 at 9:24 PM, Tom Lane <t...@sss.pgh.pa.us> wrote:
> Magnus Hagander <mag...@hagander.net> 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;