From 9dddc5e43e033babe22209a835c208ef574a2b8c Mon Sep 17 00:00:00 2001 From: Anton Chumak Date: Mon, 15 Sep 2025 17:28:50 +0700 Subject: [PATCH 2/2] added guc options for test purposes Added test composite GUC options replica, shared_preload_libraries, log_level_names. Each option represents one of the main composite types: structure, dynamic array, and static array, respectively. Also, for example, the ssn option and the test_guc extension with their own test options were added. Regression and TAP tests were performed for the test options. --- contrib/test_guc/Makefile | 18 ++ contrib/test_guc/test_guc--1.0.sql | 0 contrib/test_guc/test_guc.c | 118 ++++++++++ contrib/test_guc/test_guc.control | 5 + src/backend/replication/slot.c | 3 + src/backend/replication/syncrep.c | 3 + src/backend/utils/error/elog.c | 2 + src/backend/utils/init/miscinit.c | 3 + src/backend/utils/misc/guc.c | 1 - src/backend/utils/misc/guc_parameters.dat | 28 +++ src/backend/utils/misc/guc_tables.c | 8 + src/backend/utils/misc/postgresql.conf.sample | 5 + src/include/miscadmin.h | 9 + src/include/replication/slot.h | 10 + src/include/replication/syncrep.h | 15 ++ src/include/utils/elog.h | 2 + src/include/utils/guc.h | 1 + src/test/modules/test_misc/meson.build | 1 + .../test_misc/t/009_test_guc_composite.pl | 49 +++++ src/test/regress/expected/guc_composite.out | 203 ++++++++++++++++++ src/test/regress/parallel_schedule | 2 +- src/test/regress/sql/guc_composite.sql | 55 +++++ 22 files changed, 539 insertions(+), 2 deletions(-) create mode 100644 contrib/test_guc/Makefile create mode 100644 contrib/test_guc/test_guc--1.0.sql create mode 100644 contrib/test_guc/test_guc.c create mode 100644 contrib/test_guc/test_guc.control create mode 100644 src/test/modules/test_misc/t/009_test_guc_composite.pl create mode 100644 src/test/regress/expected/guc_composite.out create mode 100644 src/test/regress/sql/guc_composite.sql diff --git a/contrib/test_guc/Makefile b/contrib/test_guc/Makefile new file mode 100644 index 0000000000..6e844776c6 --- /dev/null +++ b/contrib/test_guc/Makefile @@ -0,0 +1,18 @@ +# contrib/test_guc/Makefile +MODULE_big = test_guc +OBJS = \ + $(WIN32RES) \ + test_guc.o +EXTENSION = test_guc +DATA = test_guc--1.0.sql + +ifdef USE_PGXS +PG_CONFIG = pg_config +PGXS := $(shell $(PG_CONFIG) --pgxs) +include $(PGXS) +else +subdir = contrib/test_guc +top_builddir = ../.. +include $(top_builddir)/src/Makefile.global +include $(top_srcdir)/contrib/contrib-global.mk +endif \ No newline at end of file diff --git a/contrib/test_guc/test_guc--1.0.sql b/contrib/test_guc/test_guc--1.0.sql new file mode 100644 index 0000000000..e69de29bb2 diff --git a/contrib/test_guc/test_guc.c b/contrib/test_guc/test_guc.c new file mode 100644 index 0000000000..15148ab11d --- /dev/null +++ b/contrib/test_guc/test_guc.c @@ -0,0 +1,118 @@ +#include "postgres.h" +#include "fmgr.h" +#include "utils/guc.h" +#include "utils/guc_tables.h" +#include "utils/elog.h" +#include "string.h" + +PG_MODULE_MAGIC; + +struct LogLevel +{ + char *name; + int level; +}; + +struct LogLevel logLevelsMapping[5]; +struct LogLevel logLevelsMappingBoot[5]; + + +struct NodeConfig +{ + char *name; + char *ip; + int port; + int max_connections; + bool enable_connections; +}; + +struct NodeArray +{ + struct NodeConfig *data; + int size; +}; + +struct ClusterConfig +{ + char *name; + char *leader; + struct NodeArray nodes; +}; + +struct ClusterConfig clusterConfig; +struct ClusterConfig clusterConfigBoot; + + +bool validateCluster(void *newValue, void **extra, GucSource source); + +void _PG_init(void); + +void +_PG_init(void) +{ + DefineCustomCompositeType("ext.loglevelmap", "string name; int level"); + + DefineCustomCompositeVariable("ext.loglevels", + "Mapping between level names and values", + NULL, + "ext.loglevelmap[5]", + &logLevelsMapping, + &logLevelsMappingBoot, + PGC_USERSET, + 0, + NULL, + NULL, + NULL); + + + + DefineCustomCompositeType("ext.nodeConfig", "string name; string ip; int port; int max_connections; bool enable_connections"); + DefineCustomCompositeType("ext.clusterConfig", "string name; string leader; ext.nodeConfig[] nodes"); + + DefineCustomCompositeVariable("ext.cluster", + "Example of multi-level structure", + NULL, + "ext.clusterConfig", + &clusterConfig, + &clusterConfigBoot, + PGC_USERSET, + 0, + &validateCluster, + NULL, + NULL); + + MarkGUCPrefixReserved("ext"); +} + + +bool +validateCluster(void *newValueRaw, void **extra, GucSource source) +{ + struct ClusterConfig *newValue = (struct ClusterConfig *)newValueRaw; + bool found_leader = false; + int max_connections = 0; + + if (newValue->name == NULL) + return true; + + for (int i = 0; i < newValue->nodes.size; i++) + { + if (strcmp(newValue->leader, newValue->nodes.data[i].name) == 0) + { + found_leader = true; + max_connections = newValue->nodes.data[i].max_connections; + break; + } + } + + if (!found_leader) + return false; + + for (int i = 0; i < newValue->nodes.size; i++) + { + if (newValue->nodes.data[i].max_connections > max_connections) + return false; + } + + return true; +} \ No newline at end of file diff --git a/contrib/test_guc/test_guc.control b/contrib/test_guc/test_guc.control new file mode 100644 index 0000000000..17f1864d38 --- /dev/null +++ b/contrib/test_guc/test_guc.control @@ -0,0 +1,5 @@ +# test_guc extension +comment = 'Simple GUC extension with composite parameters' +default_version = '1.0' +module_pathname = '$libdir/test_guc' +relocatable = true \ No newline at end of file diff --git a/src/backend/replication/slot.c b/src/backend/replication/slot.c index fd0fdb96d4..61c00c0826 100644 --- a/src/backend/replication/slot.c +++ b/src/backend/replication/slot.c @@ -163,6 +163,9 @@ int idle_replication_slot_timeout_secs = 0; */ char *synchronized_standby_slots; +struct ReplicaConfig replicaConfig; +struct ReplicaConfig replicaConfigBoot; + /* This is the parsed and cached configuration for synchronized_standby_slots */ static SyncStandbySlotsConfigData *synchronized_standby_slots_config; diff --git a/src/backend/replication/syncrep.c b/src/backend/replication/syncrep.c index 32cf3a48b8..c45426afbb 100644 --- a/src/backend/replication/syncrep.c +++ b/src/backend/replication/syncrep.c @@ -89,6 +89,9 @@ /* User-settable parameters for sync rep */ char *SyncRepStandbyNames; +struct SSNType ssn; +struct SSNType ssnBoot; + #define SyncStandbysDefined() \ (SyncRepStandbyNames != NULL && SyncRepStandbyNames[0] != '\0') diff --git a/src/backend/utils/error/elog.c b/src/backend/utils/error/elog.c index b7b9692f8c..d7c8c08617 100644 --- a/src/backend/utils/error/elog.c +++ b/src/backend/utils/error/elog.c @@ -112,6 +112,8 @@ int Log_destination = LOG_DESTINATION_STDERR; char *Log_destination_string = NULL; bool syslog_sequence_numbers = true; bool syslog_split_messages = true; +char *logLevelNames[5]; +char *logLevelNamesBoot[5] = {"TRACE","LOG","WARNING","ERROR","FATAL"}; /* Processed form of backtrace_functions GUC */ static char *backtrace_function_list; diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index 545d1e90fb..e1c515b903 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -1833,6 +1833,9 @@ char *session_preload_libraries_string = NULL; char *shared_preload_libraries_string = NULL; char *local_preload_libraries_string = NULL; +struct ShrLibArr sharedLibs; +struct ShrLibArr sharedLibsBoot; + /* Flag telling that we are loading shared_preload_libraries */ bool process_shared_preload_libraries_in_progress = false; bool process_shared_preload_libraries_done = false; diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c index eac986a1c7..de1b278d97 100644 --- a/src/backend/utils/misc/guc.c +++ b/src/backend/utils/misc/guc.c @@ -81,7 +81,6 @@ char *GUC_check_errmsg_string; char *GUC_check_errdetail_string; char *GUC_check_errhint_string; - /* * Unit conversion tables. * diff --git a/src/backend/utils/misc/guc_parameters.dat b/src/backend/utils/misc/guc_parameters.dat index c392d00972..c8409a5e9c 100644 --- a/src/backend/utils/misc/guc_parameters.dat +++ b/src/backend/utils/misc/guc_parameters.dat @@ -3481,4 +3481,32 @@ assign_hook => 'assign_io_method', }, +{ name => 'replica', type => 'composite', context => 'PGC_USERSET', group => 'REPLICATION_PRIMARY', + short_desc => 'config of replica', + type_name => 'ReplicaConfigType', + variable => 'replicaConfig', + boot_val => '&replicaConfigBoot', +}, + +{ name => 'shared_preload_libraries_list',type => 'composite', context => 'PGC_USERSET', group => 'CLIENT_CONN_PRELOAD', + short_desc => 'list of shared preloaded libraries', + type_name => 'string[]', + variable => 'sharedLibs', + boot_val => '&sharedLibsBoot', +}, + +{ name => 'ssn', type => 'composite', context => 'PGC_USERSET', group => 'REPLICATION_PRIMARY', + short_desc => 'new synchronous_standby_names', + type_name => 'SSNType', + variable => 'ssn', + boot_val => '&ssnBoot', +}, + +{ name => 'log_level_names', type => 'composite', context => 'PGC_USERSET', group => 'LOGGING_WHAT', + short_desc => 'array of names for 5 log levels', + type_name => 'string[5]', + variable => 'logLevelNames', + boot_val => '&logLevelNamesBoot', +}, + ] diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index c45be54f3e..2a60b9ae13 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -817,6 +817,14 @@ struct type_definition UserDefinedConfigureTypes[] = { sizeof(char *), NULL }, + { + "ReplicaConfigType", + "bool enable_connections; int max_delay; int max_slot_size" + }, + { + "SSNType", + "string mode; int threshold; string[] names" + }, /* End-of-list marker */ { NULL, NULL diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample index c36fcb9ab6..cd508abb5d 100644 --- a/src/backend/utils/misc/postgresql.conf.sample +++ b/src/backend/utils/misc/postgresql.conf.sample @@ -882,4 +882,9 @@ autovacuum_worker_slots = 16 # autovacuum worker slots to allocate # CUSTOMIZED OPTIONS #------------------------------------------------------------------------------ +#replica = {} +#shared_preload_libraries_list = [] +#ssn = {} +#log_level_names = [] + # Add settings for extensions here diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h index 1bef98471c..01bab98f39 100644 --- a/src/include/miscadmin.h +++ b/src/include/miscadmin.h @@ -516,6 +516,15 @@ extern PGDLLIMPORT char *session_preload_libraries_string; extern PGDLLIMPORT char *shared_preload_libraries_string; extern PGDLLIMPORT char *local_preload_libraries_string; +struct ShrLibArr +{ + char **libs; + int size; +}; + +extern PGDLLIMPORT struct ShrLibArr sharedLibs; +extern PGDLLIMPORT struct ShrLibArr sharedLibsBoot; + extern void CreateDataDirLockFile(bool amPostmaster); extern void CreateSocketLockFile(const char *socketfile, bool amPostmaster, const char *socketDir); diff --git a/src/include/replication/slot.h b/src/include/replication/slot.h index fe62162cde..7735fbd058 100644 --- a/src/include/replication/slot.h +++ b/src/include/replication/slot.h @@ -294,6 +294,16 @@ extern PGDLLIMPORT int max_replication_slots; extern PGDLLIMPORT char *synchronized_standby_slots; extern PGDLLIMPORT int idle_replication_slot_timeout_secs; +struct ReplicaConfig +{ + bool enable_connections; + int max_delay; + int max_slots_size; +}; + +extern PGDLLIMPORT struct ReplicaConfig replicaConfig; +extern PGDLLIMPORT struct ReplicaConfig replicaConfigBoot; + /* shmem initialization functions */ extern Size ReplicationSlotsShmemSize(void); extern void ReplicationSlotsShmemInit(void); diff --git a/src/include/replication/syncrep.h b/src/include/replication/syncrep.h index dc2b118b16..6d323264bd 100644 --- a/src/include/replication/syncrep.h +++ b/src/include/replication/syncrep.h @@ -76,6 +76,21 @@ extern PGDLLIMPORT SyncRepConfigData *SyncRepConfig; /* user-settable parameters for synchronous replication */ extern PGDLLIMPORT char *SyncRepStandbyNames; +struct DynArrStr +{ + char **data; + int size; +}; +struct SSNType +{ + char *mode; + int threshold; + struct DynArrStr names; +}; + +extern PGDLLIMPORT struct SSNType ssn; +extern PGDLLIMPORT struct SSNType ssnBoot; + /* called by user backend */ extern void SyncRepWaitForLSN(XLogRecPtr lsn, bool commit); diff --git a/src/include/utils/elog.h b/src/include/utils/elog.h index 675f4f5f46..2ee3eca956 100644 --- a/src/include/utils/elog.h +++ b/src/include/utils/elog.h @@ -493,6 +493,8 @@ extern PGDLLIMPORT int Log_destination; extern PGDLLIMPORT char *Log_destination_string; extern PGDLLIMPORT bool syslog_sequence_numbers; extern PGDLLIMPORT bool syslog_split_messages; +extern PGDLLIMPORT char *logLevelNames[5]; +extern PGDLLIMPORT char *logLevelNamesBoot[5]; /* Log destination bitmap */ #define LOG_DESTINATION_STDERR 1 diff --git a/src/include/utils/guc.h b/src/include/utils/guc.h index b197a81f28..2b8928439d 100644 --- a/src/include/utils/guc.h +++ b/src/include/utils/guc.h @@ -199,6 +199,7 @@ typedef const char *(*GucShowHook) (void); /* * Miscellaneous */ + typedef enum { /* Types of set_config_option actions */ diff --git a/src/test/modules/test_misc/meson.build b/src/test/modules/test_misc/meson.build index 6b1e730bf4..31e2341f19 100644 --- a/src/test/modules/test_misc/meson.build +++ b/src/test/modules/test_misc/meson.build @@ -17,6 +17,7 @@ tests += { 't/006_signal_autovacuum.pl', 't/007_catcache_inval.pl', 't/008_replslot_single_user.pl', + 't/009_test_guc_composite.pl', ], }, } diff --git a/src/test/modules/test_misc/t/009_test_guc_composite.pl b/src/test/modules/test_misc/t/009_test_guc_composite.pl new file mode 100644 index 0000000000..58b1cbc91c --- /dev/null +++ b/src/test/modules/test_misc/t/009_test_guc_composite.pl @@ -0,0 +1,49 @@ +# Copyright (c) 2024-2025, PostgreSQL Global Development Group + +# Tests composite GUC parameters + +use strict; +use warnings FATAL => 'all'; +use PostgreSQL::Test::Cluster; +use PostgreSQL::Test::Utils; +use Test::More; + +my $node = PostgreSQL::Test::Cluster->new('main'); +$node->init; +$node->start; + +# Test ALTER SYSTEM command: +# ALTER SYSTEM must only write changed parameter in .auto.conf file +$node->safe_psql( + 'postgres', + "ALTER SYSTEM SET replica->max_delay = 42" +); + +# Check postgresql.auto.conf +my $auto_conf_path = $node->data_dir . '/postgresql.auto.conf'; +my $expected_line = "replica = {enable_connections: \'false\', max_delay: \'42\', max_slot_size: \'0\'}"; +my $found = 0; +open(my $fh, '<', $auto_conf_path) or die "Cannot open file: $!"; +while (my $line = <$fh>) { + chomp $line; + if ($line =~ /^\s*\Q$expected_line\E\s*$/) { + $found = 1; + last; + } +} +close($fh); + +ok($found, 'Composite parameter setting found in postgresql.auto.conf'); + +#reload config +$node->safe_psql('postgres', 'SELECT pg_reload_conf()'); + +# Check that parameter was applied +my $current_value = $node->safe_psql( + 'postgres', + "SHOW replica->max_delay" +); + +is($current_value, '42', 'Composite parameter value is correctly set'); + +done_testing(); diff --git a/src/test/regress/expected/guc_composite.out b/src/test/regress/expected/guc_composite.out new file mode 100644 index 0000000000..0eafceed60 --- /dev/null +++ b/src/test/regress/expected/guc_composite.out @@ -0,0 +1,203 @@ +-- STRUCT +SHOW replica; + replica +------------------------------------ + { + + enable_connections: false,+ + max_delay: 0, + + max_slot_size: 0 + + } +(1 row) + +SET replica->max_delay to 42; +SHOW replica->max_delay; + replica +--------- + 42 +(1 row) + +SET replica to {enable_connections: true, max_slot_size: 10}; +SHOW replica; + replica +----------------------------------- + { + + enable_connections: true,+ + max_delay: 42, + + max_slot_size: 10 + + } +(1 row) + +SET replica->invalid_field to 4; +ERROR: invalid value for parameter "replica": "{invalid_field: '4'}" +HINT: incorrect field name at or near ":" +-- STATIC ARRAY +SHOW log_level_names; + log_level_names +-------------------- + [ + + 'TRACE', + + 'LOG', + + 'WARNING',+ + 'ERROR', + + 'FATAL' + + ] +(1 row) + +SET log_level_names[0] to 'DEBUG'; +SHOW log_level_names[0]; + log_level_names +----------------- + 'DEBUG' +(1 row) + +SET log_level_names[10] to 'EXTRA'; +ERROR: invalid value for parameter "log_level_names": "[10: 'EXTRA']" +HINT: there is index which is greater than size of array at or near ":" +SET log_level_names[-1] to 'EXTRA'; +ERROR: syntax error at or near "-" +LINE 1: SET log_level_names[-1] to 'EXTRA'; + ^ +-- DYNAMIC ARRAY +SHOW shared_preload_libraries_list; + shared_preload_libraries_list +------------------------------- + [ + + ] +(1 row) + +SET shared_preload_libraries_list[0] to 'yet_another_ext'; +SHOW shared_preload_libraries_list->data[0]; + shared_preload_libraries_list +------------------------------- + 'yet_another_ext' +(1 row) + +SET extended_guc_arrays to true; +SHOW shared_preload_libraries_list; + shared_preload_libraries_list +----------------------------------- + { + + size: 1, + + data: [ + + 'yet_another_ext'+ + ] + + } +(1 row) + +SET shared_preload_libraries_list->size to 2; +SHOW shared_preload_libraries_list; + shared_preload_libraries_list +------------------------------------ + { + + size: 2, + + data: [ + + 'yet_another_ext',+ + nil + + ] + + } +(1 row) + +SET shared_preload_libraries_list to {size: 2, data: [3: 'third_ext']}; +ERROR: invalid value for parameter "shared_preload_libraries_list": "{size: 2, data: [3: 'third_ext']}" +HINT: there is index greater than array length. Change "size" field at or near "'third_ext'" +SET extended_guc_arrays to false; +-- CHECK GUC STACK +SHOW replica; + replica +----------------------------------- + { + + enable_connections: true,+ + max_delay: 42, + + max_slot_size: 10 + + } +(1 row) + +BEGIN; +SET replica->max_delay to 6; +SHOW replica; + replica +----------------------------------- + { + + enable_connections: true,+ + max_delay: 6, + + max_slot_size: 10 + + } +(1 row) + +COMMIT; +SHOW replica; + replica +----------------------------------- + { + + enable_connections: true,+ + max_delay: 6, + + max_slot_size: 10 + + } +(1 row) + +SHOW replica; + replica +----------------------------------- + { + + enable_connections: true,+ + max_delay: 6, + + max_slot_size: 10 + + } +(1 row) + +BEGIN; +SET replica->max_delay to 28; +SHOW replica; + replica +----------------------------------- + { + + enable_connections: true,+ + max_delay: 28, + + max_slot_size: 10 + + } +(1 row) + +ABORT; +SHOW replica; + replica +----------------------------------- + { + + enable_connections: true,+ + max_delay: 6, + + max_slot_size: 10 + + } +(1 row) + +SHOW replica; + replica +----------------------------------- + { + + enable_connections: true,+ + max_delay: 6, + + max_slot_size: 10 + + } +(1 row) + +BEGIN; +SET LOCAL replica->max_delay to 28; +SHOW replica; + replica +----------------------------------- + { + + enable_connections: true,+ + max_delay: 28, + + max_slot_size: 10 + + } +(1 row) + +COMMIT; +SHOW replica; + replica +----------------------------------- + { + + enable_connections: true,+ + max_delay: 6, + + max_slot_size: 10 + + } +(1 row) + diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule index fbffc67ae6..d6d41bbccc 100644 --- a/src/test/regress/parallel_schedule +++ b/src/test/regress/parallel_schedule @@ -102,7 +102,7 @@ test: publication subscription # Another group of parallel tests # select_views depends on create_view # ---------- -test: select_views portals_p2 foreign_key cluster dependency guc bitmapops combocid tsearch tsdicts foreign_data window xmlmap functional_deps advisory_lock indirect_toast equivclass +test: select_views portals_p2 foreign_key cluster dependency guc_composite guc bitmapops combocid tsearch tsdicts foreign_data window xmlmap functional_deps advisory_lock indirect_toast equivclass # ---------- # Another group of parallel tests (JSON related) diff --git a/src/test/regress/sql/guc_composite.sql b/src/test/regress/sql/guc_composite.sql new file mode 100644 index 0000000000..338cc8692c --- /dev/null +++ b/src/test/regress/sql/guc_composite.sql @@ -0,0 +1,55 @@ +-- STRUCT +SHOW replica; + +SET replica->max_delay to 42; +SHOW replica->max_delay; + +SET replica to {enable_connections: true, max_slot_size: 10}; +SHOW replica; +SET replica->invalid_field to 4; + +-- STATIC ARRAY +SHOW log_level_names; +SET log_level_names[0] to 'DEBUG'; +SHOW log_level_names[0]; +SET log_level_names[10] to 'EXTRA'; +SET log_level_names[-1] to 'EXTRA'; + +-- DYNAMIC ARRAY + +SHOW shared_preload_libraries_list; + +SET shared_preload_libraries_list[0] to 'yet_another_ext'; +SHOW shared_preload_libraries_list->data[0]; + +SET extended_guc_arrays to true; +SHOW shared_preload_libraries_list; + +SET shared_preload_libraries_list->size to 2; +SHOW shared_preload_libraries_list; + +SET shared_preload_libraries_list to {size: 2, data: [3: 'third_ext']}; +SET extended_guc_arrays to false; + +-- CHECK GUC STACK + +SHOW replica; +BEGIN; +SET replica->max_delay to 6; +SHOW replica; +COMMIT; +SHOW replica; + +SHOW replica; +BEGIN; +SET replica->max_delay to 28; +SHOW replica; +ABORT; +SHOW replica; + +SHOW replica; +BEGIN; +SET LOCAL replica->max_delay to 28; +SHOW replica; +COMMIT; +SHOW replica; -- 2.48.1