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;

Reply via email to