diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 0ee6974f1c..8754f2f89b 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -23859,6 +23859,43 @@ SELECT currval(pg_get_serial_sequence('sometable', 'id'));
+
+
+
+ pg_settings_get_flags
+
+ pg_settings_get_flags ( guc text )
+ text[]
+
+
+ Returns an array of the flags associated with the given GUC, or
+ NULL if it does not exist. The result is
+ an empty array if the GUC exists but there are no flags to show.
+ Only the most useful flags are exposed, as of the following:
+
+
+ EXPLAIN: parameters included in
+ EXPLAIN (SETTINGS) commands.
+
+
+ NO_SHOW_ALL: parameters excluded from
+ SHOW ALL commands.
+
+
+ NO_RESET_ALL: parameters excluded from
+ RESET ALL commands.
+
+
+ NOT_IN_SAMPLE: parameters not included in
+ postgresql.conf by default.
+
+
+ RUNTIME_COMPUTED: runtime-computed parameters.
+
+
+
+
+
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 4c94f09c64..b3fd42e0f1 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -9634,6 +9634,45 @@ GetConfigOptionByName(const char *name, const char **varname, bool missing_ok)
return _ShowOption(record, true);
}
+/*
+ * Return some of the flags associated to the specified GUC in the shape of
+ * a text array, and NULL if it does not exist. An empty array is returned
+ * if the GUC exists without any meaningful flags to show.
+ */
+Datum
+pg_settings_get_flags(PG_FUNCTION_ARGS)
+{
+#define MAX_GUC_FLAGS 5
+ char *varname = TextDatumGetCString(PG_GETARG_DATUM(0));
+ struct config_generic *record;
+ int cnt = 0;
+ Datum flags[MAX_GUC_FLAGS];
+ ArrayType *a;
+
+ record = find_option(varname, false, true, ERROR);
+
+ /* return NULL if no such variable */
+ if (record == NULL)
+ PG_RETURN_NULL();
+
+ if (record->flags & GUC_EXPLAIN)
+ flags[cnt++] = CStringGetTextDatum("EXPLAIN");
+ if (record->flags & GUC_NO_RESET_ALL)
+ flags[cnt++] = CStringGetTextDatum("NO_RESET_ALL");
+ if (record->flags & GUC_NO_SHOW_ALL)
+ flags[cnt++] = CStringGetTextDatum("NO_SHOW_ALL");
+ if (record->flags & GUC_NOT_IN_SAMPLE)
+ flags[cnt++] = CStringGetTextDatum("NOT_IN_SAMPLE");
+ if (record->flags & GUC_RUNTIME_COMPUTED)
+ flags[cnt++] = CStringGetTextDatum("RUNTIME_COMPUTED");
+
+ Assert(cnt <= MAX_GUC_FLAGS);
+
+ /* Returns the record as Datum */
+ a = construct_array(flags, cnt, TEXTOID, -1, false, TYPALIGN_INT);
+ PG_RETURN_ARRAYTYPE_P(a);
+}
+
/*
* Return GUC variable value by variable number; optionally return canonical
* form of name. Return value is palloc'd.
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index 656b6c8f00..6487bf9c0a 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -53,6 +53,6 @@
*/
/* yyyymmddN */
-#define CATALOG_VERSION_NO 202201271
+#define CATALOG_VERSION_NO 202201311
#endif
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 0859dc81ca..7024dbe10a 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6096,6 +6096,11 @@
proargmodes => '{o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o}',
proargnames => '{name,setting,unit,category,short_desc,extra_desc,context,vartype,source,min_val,max_val,enumvals,boot_val,reset_val,sourcefile,sourceline,pending_restart}',
prosrc => 'show_all_settings' },
+
+{ oid => '8921', descr => 'return flags for specified GUC',
+ proname => 'pg_settings_get_flags', provolatile => 's', prorettype => '_text',
+ proargtypes => 'text', prosrc => 'pg_settings_get_flags' },
+
{ oid => '3329', descr => 'show config file settings',
proname => 'pg_show_all_file_settings', prorows => '1000', proretset => 't',
provolatile => 'v', prorettype => 'record', proargtypes => '',
diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out
index 59da91ff04..75b6bfbf11 100644
--- a/src/test/regress/expected/guc.out
+++ b/src/test/regress/expected/guc.out
@@ -813,3 +813,86 @@ set default_with_oids to f;
-- Should not allow to set it to true.
set default_with_oids to t;
ERROR: tables declared WITH OIDS are not supported
+-- Test GUC categories and flag patterns
+SELECT pg_settings_get_flags(NULL);
+ pg_settings_get_flags
+-----------------------
+
+(1 row)
+
+SELECT pg_settings_get_flags('does_not_exist');
+ pg_settings_get_flags
+-----------------------
+
+(1 row)
+
+CREATE TABLE tab_settings_flags AS SELECT name, category,
+ 'EXPLAIN' = ANY(flags) AS explain,
+ 'NO_RESET_ALL' = ANY(flags) AS no_reset_all,
+ 'NO_SHOW_ALL' = ANY(flags) AS no_show_all,
+ 'NOT_IN_SAMPLE' = ANY(flags) AS not_in_sample,
+ 'RUNTIME_COMPUTED' = ANY(flags) AS runtime_computed
+ FROM pg_show_all_settings() AS psas,
+ pg_settings_get_flags(psas.name) AS flags;
+-- Developer GUCs should be flagged with GUC_NOT_IN_SAMPLE:
+SELECT name FROM tab_settings_flags
+ WHERE category = 'Developer Options' AND NOT not_in_sample
+ ORDER BY 1;
+ name
+------
+(0 rows)
+
+-- Most query-tuning GUCs are flagged as valid for EXPLAIN.
+-- default_statistics_target is an exception.
+SELECT name FROM tab_settings_flags
+ WHERE category ~ '^Query Tuning' AND NOT explain
+ ORDER BY 1;
+ name
+---------------------------
+ default_statistics_target
+(1 row)
+
+-- Runtime-computed GUCs should be part of the preset category.
+SELECT name FROM tab_settings_flags
+ WHERE NOT category = 'Preset Options' AND runtime_computed
+ ORDER BY 1;
+ name
+------
+(0 rows)
+
+-- Preset GUCs are flagged as NOT_IN_SAMPLE.
+SELECT name FROM tab_settings_flags
+ WHERE category = 'Preset Options' AND NOT not_in_sample
+ ORDER BY 1;
+ name
+------
+(0 rows)
+
+-- NO_SHOW_ALL implies NO_RESET_ALL, and vice-versa.
+SELECT name FROM tab_settings_flags
+ WHERE no_show_all AND NOT no_reset_all
+ ORDER BY 1;
+ name
+------
+(0 rows)
+
+-- Exceptions are transaction_*.
+SELECT name FROM tab_settings_flags
+ WHERE NOT no_show_all AND no_reset_all
+ ORDER BY 1;
+ name
+------------------------
+ transaction_deferrable
+ transaction_isolation
+ transaction_read_only
+(3 rows)
+
+-- NO_SHOW_ALL implies NOT_IN_SAMPLE.
+SELECT name FROM tab_settings_flags
+ WHERE no_show_all AND NOT not_in_sample
+ ORDER BY 1;
+ name
+------
+(0 rows)
+
+DROP TABLE tab_settings_flags;
diff --git a/src/test/regress/sql/guc.sql b/src/test/regress/sql/guc.sql
index c39c11388d..3e2819449c 100644
--- a/src/test/regress/sql/guc.sql
+++ b/src/test/regress/sql/guc.sql
@@ -311,3 +311,46 @@ reset check_function_bodies;
set default_with_oids to f;
-- Should not allow to set it to true.
set default_with_oids to t;
+
+-- Test GUC categories and flag patterns
+SELECT pg_settings_get_flags(NULL);
+SELECT pg_settings_get_flags('does_not_exist');
+CREATE TABLE tab_settings_flags AS SELECT name, category,
+ 'EXPLAIN' = ANY(flags) AS explain,
+ 'NO_RESET_ALL' = ANY(flags) AS no_reset_all,
+ 'NO_SHOW_ALL' = ANY(flags) AS no_show_all,
+ 'NOT_IN_SAMPLE' = ANY(flags) AS not_in_sample,
+ 'RUNTIME_COMPUTED' = ANY(flags) AS runtime_computed
+ FROM pg_show_all_settings() AS psas,
+ pg_settings_get_flags(psas.name) AS flags;
+
+-- Developer GUCs should be flagged with GUC_NOT_IN_SAMPLE:
+SELECT name FROM tab_settings_flags
+ WHERE category = 'Developer Options' AND NOT not_in_sample
+ ORDER BY 1;
+-- Most query-tuning GUCs are flagged as valid for EXPLAIN.
+-- default_statistics_target is an exception.
+SELECT name FROM tab_settings_flags
+ WHERE category ~ '^Query Tuning' AND NOT explain
+ ORDER BY 1;
+-- Runtime-computed GUCs should be part of the preset category.
+SELECT name FROM tab_settings_flags
+ WHERE NOT category = 'Preset Options' AND runtime_computed
+ ORDER BY 1;
+-- Preset GUCs are flagged as NOT_IN_SAMPLE.
+SELECT name FROM tab_settings_flags
+ WHERE category = 'Preset Options' AND NOT not_in_sample
+ ORDER BY 1;
+-- NO_SHOW_ALL implies NO_RESET_ALL, and vice-versa.
+SELECT name FROM tab_settings_flags
+ WHERE no_show_all AND NOT no_reset_all
+ ORDER BY 1;
+-- Exceptions are transaction_*.
+SELECT name FROM tab_settings_flags
+ WHERE NOT no_show_all AND no_reset_all
+ ORDER BY 1;
+-- NO_SHOW_ALL implies NOT_IN_SAMPLE.
+SELECT name FROM tab_settings_flags
+ WHERE no_show_all AND NOT not_in_sample
+ ORDER BY 1;
+DROP TABLE tab_settings_flags;