My idea is to store the information in guc_tables.c in a .dat file
similar to the catalog data in src/include/catalog/, and generated
guc_tables.c from that. I want to make it easier to edit that
information, and I want to be able to make changes to the downstream
data structures more easily. (Essentially, those are the same reasons
as for the original adoption of the .dat format.)
An important project is to adapt the GUC system to a multithreaded
server. The leading idea is to convert most global variables to
thread-local storage. But this doesn't work with the current global
structs, because they can't contain a pointer to a thread-local
variable. Some workarounds and changes exist in WIP threading branches,
but they all require at least some mechanical changes to the global
tables. If these tables could be generated automatically, it would be
easier to try out different things.
More generally, maybe the current format of a global struct that points
to many global variables is not the right one anymore. Maybe the global
variables should be packaged into a struct, or several structs. Or
maybe the whole thing could just be a hash table and you retrieve values
when you need them. Who knows. But this would make it easier to
experiment and make changes.
Another possible benefit would be that we could generate the
postgresql.conf.sample file. This is also very tedious to edit and
maintain, and sometimes people want to make larger changes, and this is
very difficult. And then we might not need tests like 003_check_guc.pl
that check the consistency of the sample file.
So here is an initial POC patch. I have written a script to convert a
new src/backend/utils/misc/guc_parameters.dat to what would be
guc_tables.c, but in the patch it's guc_tables_new.c. The
guc_parameters.dat in the patch is populated only with a few entries
that cover most of the different types and variants and possible
settings, so we can see what it would look like. Eventually, this would
require a big conversion.
My next goal would be to make this work so that most of guc_tables.c is
generated, and nothing else changes beyond that. But before I do all
the remaining tedious work, I wanted to check what people think.From 31e00a9282e154c62197e1bbceeb328e0e088110 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <pe...@eisentraut.org>
Date: Mon, 11 Aug 2025 07:57:02 +0200
Subject: [PATCH v0] WIP: Generate GUC tables from .dat file
---
src/backend/utils/misc/gen_guc_tables.pl | 79 +++++++++++++++++++++++
src/backend/utils/misc/guc_parameters.dat | 75 +++++++++++++++++++++
src/backend/utils/misc/meson.build | 11 ++++
3 files changed, 165 insertions(+)
create mode 100644 src/backend/utils/misc/gen_guc_tables.pl
create mode 100644 src/backend/utils/misc/guc_parameters.dat
diff --git a/src/backend/utils/misc/gen_guc_tables.pl
b/src/backend/utils/misc/gen_guc_tables.pl
new file mode 100644
index 00000000000..9bd1c24ac50
--- /dev/null
+++ b/src/backend/utils/misc/gen_guc_tables.pl
@@ -0,0 +1,79 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings FATAL => 'all';
+
+use FindBin;
+use lib "$FindBin::RealBin/../../catalog";
+use Catalog;
+
+# debug
+use Data::Dumper;
+
+my $input_fname = $ARGV[0];
+my $output_fname = $ARGV[1];
+
+open my $ifh, '<', $input_fname or die;
+open my $ofh, '>', $output_fname or die;
+
+my $parse = Catalog::ParseData($input_fname);
+
+sub escape
+{
+ my ($s) = @_;
+
+ $s =~ s/"/\\"/g;
+
+ return $s;
+}
+
+sub print_one_table
+{
+ my ($type) = @_;
+ my $Type = ucfirst $type;
+
+ print $ofh "struct config_${type} ConfigureNames${Type}[] =\n"
+ . "{\n";
+
+ foreach my $entry (@{$parse})
+ {
+ next if $entry->{type} ne $type;
+
+ print $ofh "#ifdef " . $entry->{ifdef} . "\n" if
$entry->{ifdef};
+ print $ofh "\t{\n";
+ print $ofh "\t\t{\n";
+ print $ofh qq{\t\t\t"} . $entry->{name} . qq{",\n};
+ print $ofh qq{\t\t\t} . $entry->{context} . qq{,\n};
+ print $ofh qq{\t\t\t} . $entry->{group} . qq{,\n};
+ print $ofh qq{\t\t\tgettext_noop("} .
escape($entry->{short_desc}) . qq{"),\n};
+ if ($entry->{long_desc})
+ {
+ print $ofh qq{\t\t\tgettext_noop("} .
escape($entry->{long_desc}) . qq{"),\n};
+ }
+ else
+ {
+ print $ofh qq{\t\t\tNULL,\n};
+ }
+ print $ofh qq{\t\t\t} . $entry->{flags} . qq{,\n} if
$entry->{flags};
+ print $ofh "\t\t},\n";
+ print $ofh qq{\t\t&} . $entry->{variable} . qq {,\n};
+ print $ofh qq{\t\t} . $entry->{boot_val} . qq {,\n};
+ print $ofh qq{\t\t} . $entry->{min} . qq {,\n} if
$entry->{type} eq 'int' || $entry->{type} eq 'real';
+ print $ofh qq{\t\t} . $entry->{max} . qq {,\n} if
$entry->{type} eq 'int' || $entry->{type} eq 'real';
+ print $ofh qq{\t\t} . $entry->{options} . qq {,\n} if
$entry->{type} eq 'enum';
+ print $ofh qq{\t\t} . ($entry->{check_hook} || 'NULL') . qq
{,\n};
+ print $ofh qq{\t\t} . ($entry->{assign_hook} || 'NULL') . qq
{,\n};
+ print $ofh qq{\t\t} . ($entry->{show_hook} || 'NULL') . qq
{,\n};
+ print $ofh "\t},\n";
+ print $ofh "#endif\n" if $entry->{ifdef};
+ }
+
+ print $ofh "\t/* End-of-list marker */\n"
+ . "\t{0}\n"
+ ."};\n\n";
+}
+
+foreach my $type (qw(bool int real string enum))
+{
+ print_one_table($type);
+}
diff --git a/src/backend/utils/misc/guc_parameters.dat
b/src/backend/utils/misc/guc_parameters.dat
new file mode 100644
index 00000000000..beba6c9d05a
--- /dev/null
+++ b/src/backend/utils/misc/guc_parameters.dat
@@ -0,0 +1,75 @@
+[
+
+{ name => 'enable_seqscan', type => 'bool', context => 'PGC_USERSET', group =>
'QUERY_TUNING_METHOD',
+ short_desc => 'Enables the planner\'s use of sequential-scan plans.',
+ flags => 'GUC_EXPLAIN',
+ variable => 'enable_seqscan',
+ boot_val => 'true',
+},
+
+{ name => 'enable_indexscan', type => 'bool', context => 'PGC_USERSET', group
=> 'QUERY_TUNING_METHOD',
+ short_desc => 'Enables the planner\'s use of index-scan plans.',
+ flags => 'GUC_EXPLAIN',
+ variable => 'enable_indexscan',
+ boot_val => 'true',
+},
+
+{ name => 'enable_partition_pruning', type => 'bool', context =>
'PGC_USERSET', group => 'QUERY_TUNING_METHOD',
+ short_desc => 'Enables plan-time and execution-time partition pruning.',
+ long_desc => 'Allows the query planner and executor to compare partition
bounds to conditions in the query to determine which partitions must be
scanned.',
+ flags => 'GUC_EXPLAIN',
+ variable => 'enable_partition_pruning',
+ boot_val => 'true',
+},
+
+{ name => 'ssl', type => 'bool', context => 'PGC_SIGHUP', group =>
'CONN_AUTH_SSL',
+ short_desc => 'Enables SSL connections.',
+ variable => 'EnableSSL',
+ boot_val => 'false',
+ check_hook => 'check_ssl',
+},
+
+# this is undocumented because not exposed in a standard build
+{ name => 'optimize_bounded_sort', type => 'bool', context => 'PGC_USERSET',
group => 'QUERY_TUNING_METHOD',
+ short_desc => 'Enables bounded sorting using heap sort.',
+ flags => 'GUC_NOT_IN_SAMPLE | GUC_EXPLAIN',
+ variable => 'optimize_bounded_sort',
+ boot_val => 'true',
+ ifdef => 'DEBUG_BOUNDED_SORT',
+},
+
+{ name => 'wal_receiver_timeout', type => 'int', context => 'PGC_SIGHUP',
group => 'REPLICATION_STANDBY',
+ short_desc => 'Sets the maximum wait time to receive data from the sending
server.',
+ long_desc => '0 disables the timeout.',
+ flags => 'GUC_UNIT_MS',
+ variable => 'wal_receiver_timeout',
+ boot_val => '60 * 1000',
+ min => '0',
+ max => 'INT_MAX',
+},
+
+{ name => 'seq_page_cost', type => 'real', context => 'PGC_USERSET', group =>
'QUERY_TUNING_COST',
+ short_desc => 'Sets the planner\'s estimate of the cost of a sequentially
fetched disk page.',
+ flags => 'GUC_EXPLAIN',
+ variable => 'seq_page_cost',
+ boot_val => 'DEFAULT_SEQ_PAGE_COST',
+ min => '0',
+ max => 'DBL_MAX',
+},
+
+{ name => 'archive_command', type => 'string', context => 'PGC_SIGHUP', group
=> 'WAL_ARCHIVING',
+ short_desc => 'Sets the shell command that will be called to archive a WAL
file.',
+ long_desc => 'An empty string means use "archive_library".',
+ variable => 'XLogArchiveCommand',
+ boot_val => '""',
+ show_hook => 'show_archive_command',
+},
+
+{ name => 'bytea_output', type => 'enum', context => 'PGC_USERSET', group =>
'CLIENT_CONN_STATEMENT',
+ short_desc => 'Sets the output format for bytea.',
+ variable => 'bytea_output',
+ boot_val => 'BYTEA_OUTPUT_HEX',
+ options => 'bytea_output_options',
+},
+
+]
diff --git a/src/backend/utils/misc/meson.build
b/src/backend/utils/misc/meson.build
index 9e389a00d05..a34512ec9c1 100644
--- a/src/backend/utils/misc/meson.build
+++ b/src/backend/utils/misc/meson.build
@@ -37,3 +37,14 @@ backend_link_with += static_library('guc-file',
install_data('postgresql.conf.sample',
install_dir: dir_data,
)
+
+
+# WIP
+custom_target(
+ 'guc_tables_new.c',
+ input: files('guc_parameters.dat'),
+ output: ['guc_tables_new.c'],
+ depend_files: catalog_pm,
+ command: [perl, files('gen_guc_tables.pl'), '@INPUT@', '@OUTPUT@'],
+ install: false, # for now
+)
--
2.50.1