From 88af54efa27108826f45eb89ac1f427fb4db2f58 Mon Sep 17 00:00:00 2001
From: Anton Chumak <A.M.Chumak@yandex.com>
Date: Mon, 8 Sep 2025 10:10:10 +0700
Subject: [PATCH] Composite data types have been added to the GUC
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

The new module of the configuration system allows you to map the values of
composite data types into composite variables (structures, static arrays,
dynamic arrays) in the C code in a unified way.

Mapping works due to the signature that the programmer declares for the
composite type.  For core options, the declaration data is in the
UserDefinedConfigureTypes array. Composite types for options from
extensions are declared using the DefineCustomCompositeType function.
All declarations must be arranged topologically. That is, if the type A
option contains a type B field, then type B must first be declared,
and then type A.

The main fields in the type definition are the type name and its signature.
The type signature has the following syntax:
“field_type field_name; field_type field_name; ...; field_type field_name”
where field_type is the already registered type, field_name is the field name.

There are also data types that do not need to be declared - these are arrays.
So, if there is a registered type A, then data types automatically become
available: A[n] is a static array of length n and A[] is a dynamic array.

Note that the declared type signature must match the signature of the structure
in the C code, since it will then be used to calculate the alignment of fields
according to the rules of the C language.
Dynamic arrays are always mapped into a structure like:
struct DynArr {
	void *data; //pointer to data
	int size; //length of the array
}

After declaring the type, you can declare a composite type configuration
option. The core options are declared in the guc_parameters.dat file. They must
specify the type => ‘composite’ fields and specify in the type_name field
the name of the composite type that was declared earlier. In the boot_val
field, write a pointer to a global variable that will store this value.
Options from extensions are declared using DefineCustomCompositeVariable.

Now you can use the following syntax to work with the new options both in
the configuration file and in psql:

Field access: option_name->field_name
Access to an array element: option_name[index]
You can combine these access methods.
Dynamic arrays always have implicit fields data and size.
data is the data of the array, size is its length.

Values of composite types have the following syntax:
- Structures: {field: value, ..., field: value}
- Static arrays: [index: value, index: value]
- Dynamic arrays arrays have implicit fields, so you can use 2 syntaxes:
-- compact (same as for static arrays) and
-- extended:
   {data: [index: value, .., index: value], size: value}.
It is not necessary to write indexes in array values. If you write
without indexes, it is assumed that indexing starts from 0 with an increment
of 1. In this case, all elements within the same array must be either with
or without indexes.

When using the show command, the display of the dynamic array depends on the
extended_guc_arrays option. If this flag is true, then the extended form
is used, otherwise the compact form is used.

String values within composite types also support escape sequences.
All the functionality available to scalar options is also

The system uses incremental semantics. This means that when writing to a .conf
file or the set command, only the specified fields of the structure will be
changed, the remaining fields will not be involved. The same applies to the
alter system see. When using this command, the current value will be written
to the .auto.conf file with the changed fields that were described when
calling the command, while the current value of the option will not change.
---
 src/backend/Makefile                        |    2 +-
 src/backend/nodes/makefuncs.c               |   16 +
 src/backend/nodes/value.c                   |   14 +
 src/backend/parser/gram.y                   |   72 +-
 src/backend/parser/scan.l                   |   11 +-
 src/backend/utils/misc/Makefile             |   16 +
 src/backend/utils/misc/README               |  118 ++
 src/backend/utils/misc/gen_guc_tables.pl    |    4 +-
 src/backend/utils/misc/guc-file.l           |   21 +-
 src/backend/utils/misc/guc.c                | 1120 +++++++++++++-
 src/backend/utils/misc/guc_composite.c      | 1486 +++++++++++++++++++
 src/backend/utils/misc/guc_composite.h      |   84 ++
 src/backend/utils/misc/guc_composite_gram.y |  787 ++++++++++
 src/backend/utils/misc/guc_composite_scan.l |  132 ++
 src/backend/utils/misc/guc_funcs.c          |  113 +-
 src/backend/utils/misc/guc_parameters.dat   |    6 +
 src/backend/utils/misc/guc_tables.c         |   64 +-
 src/backend/utils/misc/help_config.c        |   13 +
 src/backend/utils/misc/meson.build          |   26 +
 src/include/nodes/makefuncs.h               |    1 +
 src/include/nodes/value.h                   |   10 +
 src/include/utils/guc.h                     |   26 +
 src/include/utils/guc_tables.h              |   40 +
 src/interfaces/ecpg/preproc/parse.pl        |    4 +
 24 files changed, 4108 insertions(+), 78 deletions(-)
 create mode 100644 src/backend/utils/misc/guc_composite.c
 create mode 100644 src/backend/utils/misc/guc_composite.h
 create mode 100644 src/backend/utils/misc/guc_composite_gram.y
 create mode 100644 src/backend/utils/misc/guc_composite_scan.l

diff --git a/src/backend/Makefile b/src/backend/Makefile
index 7344c8c7f5..d270596ed4 100644
--- a/src/backend/Makefile
+++ b/src/backend/Makefile
@@ -166,7 +166,7 @@ generated-parser-sources:
 	$(MAKE) -C bootstrap	bootparse.c bootparse.h bootscanner.c
 	$(MAKE) -C replication	repl_gram.c repl_gram.h repl_scanner.c syncrep_gram.c syncrep_gram.h syncrep_scanner.c
 	$(MAKE) -C utils/adt	jsonpath_gram.c jsonpath_gram.h jsonpath_scan.c
-	$(MAKE) -C utils/misc	guc-file.c
+	$(MAKE) -C utils/misc	guc-file.c guc_composite_gram.c guc_composite_gram.h guc_composite_scan.c
 
 
 ##########################################################################
diff --git a/src/backend/nodes/makefuncs.c b/src/backend/nodes/makefuncs.c
index e2d9e9be41..4707d20749 100644
--- a/src/backend/nodes/makefuncs.c
+++ b/src/backend/nodes/makefuncs.c
@@ -626,6 +626,22 @@ makeStringConst(char *str, int location)
 	return (Node *) n;
 }
 
+/*
+ * makeSerializedCompositeConst -
+ * 	build a A_Const node of type T_SerializedComposite for given string
+ */
+Node *
+makeSerializedCompositeConst(char *str, int location)
+{
+	A_Const    *n = makeNode(A_Const);
+
+	n->val.sval.type = T_SerializedComposite;
+	n->val.sval.sval = str;
+	n->location = location;
+
+	return (Node *) n;
+}
+
 /*
  * makeDefElem -
  *	build a DefElem node
diff --git a/src/backend/nodes/value.c b/src/backend/nodes/value.c
index 5a8c1ce247..e3af859900 100644
--- a/src/backend/nodes/value.c
+++ b/src/backend/nodes/value.c
@@ -81,3 +81,17 @@ makeBitString(char *str)
 	v->bsval = str;
 	return v;
 }
+
+/*
+ *	makeSerializedComposite
+ *
+ * Caller is responsible for passing a palloc'd string.
+ */
+SerializedComposite *
+makeSerializedComposite(char *str)
+{
+	SerializedComposite	   *v = makeNode(SerializedComposite);
+
+	v->sval = str;
+	return v;
+}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 9fd48acb1f..48f6291323 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -575,9 +575,10 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 
 %type <ival>	Iconst SignedIconst
 %type <str>		Sconst comment_text notify_payload
-%type <str>		RoleId opt_boolean_or_string
+%type <str>		RoleId opt_boolean_or_string opt_boolean_or_string_extended
 %type <list>	var_list
-%type <str>		ColId ColLabel BareColLabel
+%type <str>		ColId ColLabel BareColLabel IndexCh
+%type <str>     composite_only list_expr list_item
 %type <str>		NonReservedWord NonReservedWord_or_Sconst
 %type <str>		var_name type_function_name param_name
 %type <str>		createdb_opt_name plassign_target
@@ -684,7 +685,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
  * DOT_DOT is unused in the core SQL grammar, and so will always provoke
  * parse errors.  It is needed by PL/pgSQL.
  */
-%token <str>	IDENT UIDENT FCONST SCONST USCONST BCONST XCONST Op
+%token <str>	IDENT UIDENT FCONST SCONST USCONST BCONST XCONST DEREF Op
 %token <ival>	ICONST PARAM
 %token			TYPECAST DOT_DOT COLON_EQUALS EQUALS_GREATER
 %token			LESS_EQUALS GREATER_EQUALS NOT_EQUALS
@@ -889,6 +890,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 %left		AT				/* sets precedence for AT TIME ZONE, AT LOCAL */
 %left		COLLATE
 %right		UMINUS
+%left		'{' '}'			/* that is '{' and '}' */
 %left		'[' ']'
 %left		'(' ')'
 %left		TYPECAST
@@ -1842,8 +1844,14 @@ set_rest_more:	/* Generic SET syntaxes: */
 var_name:	ColId								{ $$ = $1; }
 			| var_name '.' ColId
 				{ $$ = psprintf("%s.%s", $1, $3); }
+			| var_name DEREF ColId
+				{ $$ = psprintf("%s->%s", $1, $3); }
+			| var_name IndexCh
+				{ $$ = psprintf("%s%s", $1, $2); }
 		;
 
+IndexCh: '['ICONST']' { $$ = psprintf("[%d]", $2); };
+
 var_list:	var_value								{ $$ = list_make1($1); }
 			| var_list ',' var_value				{ $$ = lappend($1, $3); }
 		;
@@ -1852,7 +1860,51 @@ var_value:	opt_boolean_or_string
 				{ $$ = makeStringConst($1, @1); }
 			| NumericOnly
 				{ $$ = makeAConst($1, @1); }
-		;
+			| composite_only
+				{ $$ = makeSerializedCompositeConst($1, @1); }
+		;
+
+composite_only:	'{' list_expr '}'
+					{ $$ = psprintf("{%s}", $2); }
+				| '{' '}'
+					{ $$ = psprintf("{}"); }
+				| '[' list_expr ']'
+					{ $$ = psprintf("[%s]", $2); }
+				| '[' ']'
+					{ $$ = psprintf("[]"); }
+		;
+
+list_expr:	list_item
+				{ $$ = psprintf("%s", $1); }
+			| list_expr ',' list_item
+				{ $$ = psprintf("%s, %s", $1, $3); }
+		;
+
+list_item:	ICONST ':' ICONST
+				{ $$ = psprintf("%d: %d", $1, $3); }
+			| ICONST ':' FCONST
+				{ $$ = psprintf("%d: %s", $1, $3); }
+			| ICONST ':' opt_boolean_or_string_extended
+				{ $$ = psprintf("%d: %s", $1, $3); }
+			| ICONST ':' composite_only
+				{ $$ = psprintf("%d: %s", $1, $3); }
+			| opt_boolean_or_string ':' ICONST
+				{ $$ = psprintf("%s: %d", $1, $3); }
+			| opt_boolean_or_string ':' FCONST
+				{ $$ = psprintf("%s: %s", $1, $3); }
+			| opt_boolean_or_string ':' opt_boolean_or_string_extended
+				{ $$ = psprintf("%s: %s", $1, $3); }
+			| opt_boolean_or_string ':' composite_only
+				{ $$ = psprintf("%s: %s", $1, $3); }
+			| ICONST
+				{ $$ = psprintf("%d", $1); }
+			| FCONST
+				{ $$ = psprintf("%s", $1); }
+			| opt_boolean_or_string_extended
+				{ $$ = psprintf("%s", $1); }
+			| composite_only
+				{ $$ = psprintf("%s", $1); }
+			;
 
 iso_level:	READ UNCOMMITTED						{ $$ = "read uncommitted"; }
 			| READ COMMITTED						{ $$ = "read committed"; }
@@ -1860,6 +1912,18 @@ iso_level:	READ UNCOMMITTED						{ $$ = "read uncommitted"; }
 			| SERIALIZABLE							{ $$ = "serializable"; }
 		;
 
+opt_boolean_or_string_extended:
+			TRUE_P									{ $$ = "true"; }
+			| FALSE_P								{ $$ = "false"; }
+			| ON									{ $$ = "on"; }
+			/*
+			 * OFF is also accepted as a boolean value, but is handled by
+			 * the NonReservedWord rule.  The action for booleans and strings
+			 * is the same, so we don't need to distinguish them here.
+			 */
+			| NonReservedWord_or_Sconst				{ $$ = psprintf("'%s'", $1); }
+		;
+
 opt_boolean_or_string:
 			TRUE_P									{ $$ = "true"; }
 			| FALSE_P								{ $$ = "false"; }
diff --git a/src/backend/parser/scan.l b/src/backend/parser/scan.l
index 08990831fe..8a7bc6533f 100644
--- a/src/backend/parser/scan.l
+++ b/src/backend/parser/scan.l
@@ -331,7 +331,7 @@ xcinside		[^*/]+
 
 ident_start		[A-Za-z\200-\377_]
 ident_cont		[A-Za-z\200-\377_0-9\$]
-
+dereference		"->"
 identifier		{ident_start}{ident_cont}*
 
 /* Assorted special-case operators and operator-like tokens */
@@ -363,7 +363,7 @@ not_equals		"!="
  * If you change either set, adjust the character lists appearing in the
  * rule for "operator"!
  */
-self			[,()\[\].;\:\+\-\*\/\%\^\<\>\=]
+self			[,()\[\].;\:\+\-\*\/\%\^\<\>\=\{\}]
 op_chars		[\~\!\@\#\^\&\|\`\?\+\-\*\/\%\<\>\=]
 operator		{op_chars}+
 
@@ -883,6 +883,11 @@ other			.
 					return yytext[0];
 				}
 
+{dereference}	{
+					SET_YYLLOC();
+					return DEREF;
+}
+
 {operator}		{
 					/*
 					 * Check for embedded slash-star or dash-dash; those
@@ -955,7 +960,7 @@ other			.
 						 * that the "self" rule would have.
 						 */
 						if (nchars == 1 &&
-							strchr(",()[].;:+-*/%^<>=", yytext[0]))
+							strchr(",()[].;:+-*/%^<>={}", yytext[0]))
 							return yytext[0];
 						/*
 						 * Likewise, if what we have left is two chars, and
diff --git a/src/backend/utils/misc/Makefile b/src/backend/utils/misc/Makefile
index b362ae4377..124133e7dd 100644
--- a/src/backend/utils/misc/Makefile
+++ b/src/backend/utils/misc/Makefile
@@ -16,8 +16,11 @@ override CPPFLAGS := -I. -I$(srcdir) $(CPPFLAGS)
 
 OBJS = \
 	conffiles.o \
+	guc_composite.o \
 	guc.o \
 	guc-file.o \
+	guc_composite_gram.o\
+	guc_composite_scan.o\
 	guc_funcs.o \
 	guc_tables.o \
 	help_config.o \
@@ -42,5 +45,18 @@ endif
 
 include $(top_srcdir)/src/backend/common.mk
 
+# See notes in src/backend/parser/Makefile about the following two rules
+guc_composite_gram.h: guc_composite_gram.c
+	touch $@
+
+guc_composite_gram.c: BISONFLAGS += -d
+
+# Force these dependencies to be known even without dependency info built:
+guc_composite_gram.o guc_composite_scanner.o: guc_composite_gram.h
+
+
 clean:
 	rm -f guc-file.c
+	rm -f guc_composite_scan.c
+	rm -f guc_composite_gram.c
+	rm -f guc_composite_gram.h
diff --git a/src/backend/utils/misc/README b/src/backend/utils/misc/README
index 85d97d29b6..a660d890c5 100644
--- a/src/backend/utils/misc/README
+++ b/src/backend/utils/misc/README
@@ -292,3 +292,121 @@ worry about NULL values ever, the variable can be given a non-null static
 initializer as well as a non-null boot_val.  guc.c will overwrite the
 static initializer pointer with a copy of the boot_val during
 InitializeGUCOptions, but the variable will never contain a NULL.
+
+
+GUC composite type options
+--------------------------
+
+Composite types include structures, static and dynamic arrays.
+Composite types can have several levels of nesting.
+
+User interface:
+
+	To use the composite type options, you need to register the types
+	used in the system. To do this, add the type description to the guc_tables.c
+	file in the UserDefinedConfigureTypes array or use the DefineCustomCompositeType
+	function if you are creating a type from an extension. Note that the sequence
+	of fields in the type signature must exactly match the signature
+	of the structure in the C code.
+
+	type definition is a structure type_definition. See guc_tables.h
+	Main fields of the definition are type_name and signature.
+	The signature syntax is as follows:
+	"<type_1> <name_1>;<type_2> <name_2>; ... ; <type_K> <name_K>"
+	type_i - type of field
+	name_i - name of field
+
+	The field can have a type that is already registered in the system
+	(types that are in the array before) or array type of already registered types
+	Arrays syntax:
+	<type>[<length>] - static array
+	<type>[] - dynamic array
+
+	Representation in C code of dynamic array is a structure like
+	struct DynamicArray
+	{
+		void *data;
+		int size;
+	};
+	It is important to keep the order of the fields, because mapping takes place
+	according to the type signature and alignment rules of the C language.
+
+	After registering the composite type, you can use it when declaring
+	a new composite option in the ConfigureNamesComposite array.
+
+	Composite types in the user interface support the following syntax:
+	option_name = {field_name: field_value, ..., field_name: field_value}
+	option_name->field_name = field_value
+	option_name = [value, value, ..., value]
+	option_name = [<index>: value, ..., <index>: value]
+	The same syntax is used in psql in SET and SHOW commands.
+
+Example:
+
+	Consider example option in C code:
+
+	struct DynArrStr
+	{
+		char **data;
+		int size;
+	};
+
+	struct ExampleOptionType
+	{
+		int a;
+		struct DynArrStr b;
+		double c[3];
+	};
+
+	struct ExampleOptionType example_option;
+
+	To register new option in GUC follow instruction:
+
+	1. Declare global structure for boot value:
+
+		struct ExampleOptionType example_option_boot;
+
+	2. In guc_tables.c in UserDefinedConfigureTypes declare element
+
+		{
+			"example_type",
+			"int a; string[] b; real[3] c"
+		} /* other fields will be filled automatically */
+		Notice, that declarations in UserDefinedConfigureTypes
+		must be sorted with topological order
+
+	3. In guc_tables.c in ConfigureNamesComposite declare element
+
+		{
+			{"example_option",...}, /* config_generic */
+			"example_type", /* option type */
+			&example_option,
+			&example_option_boot,
+			NULL, NULL, NULL /* hooks */
+		}
+		Option type is a type that declared in UserDefinedConfigureTypes or array type
+
+	For extensions use functions:
+		DefineCustomCompositeType - define new type
+		DefineCustomCompositeVariable - define new composite guc option
+
+Internals:
+
+	Composite types use incremental value setting semantics.
+	When setting a value, you can omit some fields of the structure/array,
+	then the unspecified fields will not change their values.
+	Maintaining this semantics for extensions required special processing
+	of placeholders for structural values. Instead of rewriting the value
+	of the field, we add it using ';'. This preserves the linear order in which
+	values are applied.
+
+	Since the logic of processing placeholders for composite types differs
+	from others, there is a need to distinguish between composite types and
+	scalar ones. To do this, it is forbidden to wrap a compound type value
+	in quotation marks. Inside, the system will add characters "->" to the
+	name of such a value, which only composite option can have.
+
+	In general, the processing of quotation marks for composite types differs
+	from scalar types, since string fields are wrapped in quotation marks
+	and the usual Deescape is not suitable for the entire structure.
+	Therefore, be careful when changing this logic.
diff --git a/src/backend/utils/misc/gen_guc_tables.pl b/src/backend/utils/misc/gen_guc_tables.pl
index bc8233f2d3..a612b2ddfd 100644
--- a/src/backend/utils/misc/gen_guc_tables.pl
+++ b/src/backend/utils/misc/gen_guc_tables.pl
@@ -25,7 +25,7 @@ my $parse = Catalog::ParseData($input_fname);
 open my $ofh, '>', $output_fname or die;
 
 print_boilerplate($ofh, $output_fname, 'GUC tables');
-foreach my $type (qw(bool int real string enum))
+foreach my $type (qw(bool int real string enum composite))
 {
 	print_one_table($ofh, $type);
 }
@@ -79,6 +79,8 @@ sub print_one_table
 			print $ofh "\n";
 		}
 		print $ofh "\t\t},\n";
+		printf $ofh "\t\t%s,\n", dquote($entry->{type_name})
+		  if $entry->{type} eq 'composite';
 		print $ofh "\t\t&$entry->{variable},\n";
 		print $ofh "\t\t$entry->{boot_val},";
 		print $ofh " $entry->{min},"
diff --git a/src/backend/utils/misc/guc-file.l b/src/backend/utils/misc/guc-file.l
index 11a1e2a3f9..3e3f3ae522 100644
--- a/src/backend/utils/misc/guc-file.l
+++ b/src/backend/utils/misc/guc-file.l
@@ -83,10 +83,13 @@ LETTER			[A-Za-z_\200-\377]
 LETTER_OR_DIGIT [A-Za-z_0-9\200-\377]
 
 ID				{LETTER}{LETTER_OR_DIGIT}*
-QUALIFIED_ID	{ID}"."{ID}
+DEREF           "->"
+INDEX           "["{DIGIT}+"]"
+AID             {ID}({DEREF}{ID}|{INDEX})*
+QUALIFIED_ID	{ID}"."{AID}
 
-UNQUOTED_STRING {LETTER}({LETTER_OR_DIGIT}|[-._:/])*
 STRING			\'([^'\\\n]|\\.|\'\')*\'
+UNQUOTED_STRING ({LETTER}|\{|\[)({LETTER_OR_DIGIT}|{STRING}|[-._:/\,\[\]\{\}\ ])*({LETTER_OR_DIGIT}|\}|\])
 
 %%
 
@@ -94,7 +97,7 @@ STRING			\'([^'\\\n]|\\.|\'\')*\'
 [ \t\r]+		/* eat whitespace */
 #.*				/* eat comment (.* matches anything until newline) */
 
-{ID}			return GUC_ID;
+{AID}			return GUC_ID;
 {QUALIFIED_ID}	return GUC_QUALIFIED_ID;
 {STRING}		return GUC_STRING;
 {UNQUOTED_STRING} return GUC_UNQUOTED_STRING;
@@ -421,8 +424,20 @@ ParseConfigFp(FILE *fp, const char *config_file, int depth, int elevel,
 		if (token == GUC_STRING)	/* strip quotes and escapes */
 			opt_value = DeescapeQuotedString(yytext);
 		else
+		{
 			opt_value = pstrdup(yytext);
 
+			if (token == GUC_UNQUOTED_STRING && (opt_value[0] == '{' || opt_value[0] == '['))
+			{
+				/* mark name as composite */
+				int name_len = strlen(opt_name) + 3;
+				char *composite_name = (char *)palloc(name_len);
+				snprintf(composite_name, name_len, "%s->", opt_name);
+				pfree(opt_name);
+				opt_name = composite_name;
+			}
+		}
+
 		/* now we'd like an end of line, or possibly EOF */
 		token = yylex(scanner);
 		if (token != GUC_EOL)
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 46fdefebe3..eac986a1c7 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -35,6 +35,7 @@
 #include "catalog/pg_authid.h"
 #include "catalog/pg_parameter_acl.h"
 #include "guc_internal.h"
+#include "guc_composite.h"
 #include "libpq/pqformat.h"
 #include "libpq/protocol.h"
 #include "miscadmin.h"
@@ -61,18 +62,16 @@
 #define CONFIG_EXEC_PARAMS_NEW "global/config_exec_params.new"
 #endif
 
-/*
- * Precision with which REAL type guc values are to be printed for GUC
- * serialization.
- */
-#define REALTYPE_PRECISION 17
-
 /*
  * Safe search path when executing code as the table owner, such as during
  * maintenance operations.
  */
 #define GUC_SAFE_SEARCH_PATH "pg_catalog, pg_temp"
 
+#define is_marked_scalar(record,item) (record->status & GUC_IS_IN_FILE)\
+				&& !strchr(item->name, '-')\
+				&& !strchr(item->name, '[')
+
 static int	GUC_check_errcode_value;
 
 static List *reserved_class_prefix = NIL;
@@ -270,6 +269,8 @@ static bool call_string_check_hook(struct config_string *conf, char **newval,
 								   void **extra, GucSource source, int elevel);
 static bool call_enum_check_hook(struct config_enum *conf, int *newval,
 								 void **extra, GucSource source, int elevel);
+static bool call_composite_check_hook(struct config_composite *conf, void *newval,
+									  void **extra, GucSource source, int elevel);
 
 
 /*
@@ -401,8 +402,13 @@ ProcessConfigFileInternal(GucContext context, bool applySettings, int elevel)
 
 		if (record)
 		{
-			/* If it's already marked, then this is a duplicate entry */
-			if (record->status & GUC_IS_IN_FILE)
+			/*
+			 * If it's already marked, then this is a duplicate entry However
+			 * composite values are patches that could intersect or union like
+			 * sets
+			 */
+			if (is_marked_scalar(record, item)) /* do not optimize for
+												 * composites */
 			{
 				/*
 				 * Mark the earlier occurrence(s) as dead/ignorable.  We could
@@ -741,6 +747,54 @@ set_string_field(struct config_string *conf, char **field, char *newval)
 		guc_free(oldval);
 }
 
+
+/*
+ * Detect whether compositeval is referenced anywhere in a GUC struct item
+ */
+static bool
+composite_used(struct config_composite *conf, void *compositeval)
+{
+	GucStack   *stack;
+
+	if (compositeval == conf->variable ||
+		compositeval == conf->reset_val ||
+		compositeval == conf->boot_val)
+		return true;
+
+	for (stack = conf->gen.stack; stack; stack = stack->prev)
+	{
+		if (compositeval == stack->prior.val.compositeval ||
+			compositeval == stack->masked.val.compositeval)
+			return true;
+	}
+
+	return false;
+}
+
+/*
+ * Assign value to a composite GUC option. Free the prior value if it's not
+ * referenced anywhere else in the option values (including stacked states).
+ * Set flag "value_on_heap" to true to allocate new value.
+ */
+static void
+set_composite(struct config_composite *conf, void **field, void *newval, bool value_on_heap)
+{
+	void	   *oldval = *field;
+
+	/* Do the assignment */
+	if (value_on_heap)
+		*field = composite_dup(newval, conf->type_name);
+	else
+	{
+		free_composite_impl(*field, conf->type_name);
+		composite_dup_impl(*field, newval, conf->type_name);
+	}
+
+	/* Free old value if it's not NULL and isn't referenced anymore */
+	if (value_on_heap && oldval && !composite_used(conf, oldval))
+		free_composite(oldval, conf->type_name);
+}
+
 /*
  * Detect whether an "extra" struct is referenced anywhere in a GUC item
  */
@@ -773,6 +827,10 @@ extra_field_used(struct config_generic *gconf, void *extra)
 			if (extra == ((struct config_enum *) gconf)->reset_extra)
 				return true;
 			break;
+		case PGC_COMPOSITE:
+			if (extra == ((struct config_composite *) gconf)->reset_extra)
+				return true;
+			break;
 	}
 	for (stack = gconf->stack; stack; stack = stack->prev)
 	{
@@ -835,6 +893,14 @@ set_stack_value(struct config_generic *gconf, config_var_value *val)
 			val->val.enumval =
 				*((struct config_enum *) gconf)->variable;
 			break;
+		case PGC_COMPOSITE:
+			{
+				bool		value_on_heap = true;
+
+				set_composite((struct config_composite *) gconf,
+							  &(val->val.compositeval),
+							  ((struct config_composite *) gconf)->variable, value_on_heap);
+			}
 	}
 	set_extra_field(gconf, &(val->extra), gconf->extra);
 }
@@ -859,6 +925,15 @@ discard_stack_value(struct config_generic *gconf, config_var_value *val)
 							 &(val->val.stringval),
 							 NULL);
 			break;
+		case PGC_COMPOSITE:
+			{
+				bool		value_on_heap = true;
+
+				set_composite((struct config_composite *) gconf,
+							  &(val->val.compositeval),
+							  NULL, value_on_heap);
+				break;
+			}
 	}
 	set_extra_field(gconf, &(val->extra), NULL);
 }
@@ -907,6 +982,12 @@ build_guc_variables(void)
 	int			num_vars = 0;
 	HASHCTL		hash_ctl;
 	GUCHashEntry *hentry;
+
+	int			size_types;
+	int			num_types = 0;
+	HASHCTL		types_hash_ctl;
+	OptionTypeHashEntry *type_hentry;
+
 	bool		found;
 	int			i;
 
@@ -918,6 +999,12 @@ build_guc_variables(void)
 											 "GUCMemoryContext",
 											 ALLOCSET_DEFAULT_SIZES);
 
+	/*
+	 * Count all type defintions
+	 */
+	for (i = 0; UserDefinedConfigureTypes[i].type_name; i++)
+		num_types++;
+
 	/*
 	 * Count all the built-in variables, and set their vartypes correctly.
 	 */
@@ -962,9 +1049,46 @@ build_guc_variables(void)
 		num_vars++;
 	}
 
+	for (i = 0; ConfigureNamesComposite[i].gen.name; i++)
+	{
+		struct config_composite *conf = &ConfigureNamesComposite[i];
+
+		conf->gen.vartype = PGC_COMPOSITE;
+		num_vars++;
+	}
+
 	/*
-	 * Create hash table with 20% slack
+	 * Create hash tables with 20% slack
 	 */
+
+	size_types = num_types + num_types / 4;
+	types_hash_ctl.keysize = sizeof(char *);
+	types_hash_ctl.entrysize = sizeof(OptionTypeHashEntry);
+	types_hash_ctl.hash = guc_name_hash;
+	types_hash_ctl.match = guc_name_match;
+	types_hash_ctl.hcxt = GUCMemoryContext;
+	guc_types_hashtab = hash_create("GUC user types hash table",
+									size_types,
+									&types_hash_ctl,
+									HASH_ELEM | HASH_FUNCTION | HASH_COMPARE | HASH_CONTEXT);
+
+	for (i = 0; UserDefinedConfigureTypes[i].type_name; i++)
+	{
+		struct type_definition *type_definition = &UserDefinedConfigureTypes[i];
+
+		type_hentry = (OptionTypeHashEntry *) hash_search(guc_types_hashtab,
+														  &type_definition->type_name,
+														  HASH_ENTER,
+														  &found);
+		Assert(!found);
+		type_hentry->definition = type_definition;
+
+		if (!is_scalar_type(type_definition->type_name))
+			init_type_definition(type_definition);
+	}
+
+	Assert(num_types == hash_get_num_entries(guc_types_hashtab));
+
 	size_vars = num_vars + num_vars / 4;
 
 	hash_ctl.keysize = sizeof(char *);
@@ -1037,6 +1161,17 @@ build_guc_variables(void)
 		hentry->gucvar = gucvar;
 	}
 
+	for (i = 0; ConfigureNamesComposite[i].gen.name; i++)
+	{
+		struct config_generic *gucvar = &ConfigureNamesComposite[i].gen;
+		hentry = (GUCHashEntry *) hash_search(guc_hashtab,
+											  &gucvar->name,
+											  HASH_ENTER,
+											  &found);
+		Assert(!found);
+		hentry->gucvar = gucvar;
+	}
+
 	Assert(num_vars == hash_get_num_entries(guc_hashtab));
 }
 
@@ -1078,13 +1213,15 @@ valid_custom_variable_name(const char *name)
 {
 	bool		saw_sep = false;
 	bool		name_start = true;
+	bool		is_field = false;
+	const char *p;
 
-	for (const char *p = name; *p; p++)
+	for (p = name; *p; p++)
 	{
 		if (*p == GUC_QUALIFIER_SEPARATOR)
 		{
-			if (name_start)
-				return false;	/* empty name component */
+			if (name_start || is_field)
+				return false;	/* empty name component or field of struct */
 			saw_sep = true;
 			name_start = true;
 		}
@@ -1097,10 +1234,40 @@ valid_custom_variable_name(const char *name)
 		}
 		else if (!name_start && strchr("0123456789$", *p) != NULL)
 			 /* okay as non-first character */ ;
+		else if (*p == '-')
+		{
+			if (!name_start && *(p + 1) == '>')
+			{
+				name_start = true;
+				is_field = true;
+				p++;
+			}
+			else
+				return false;
+		}
+		else if (!name_start && *p == '[')
+		{
+			p++;
+			while (strchr("0123456789 ", *p) != NULL)
+				p++;
+
+			if ((*p == ']' && !*(p + 1)) || *(p + 1) == '-')
+				is_field = true;
+			else
+				return false;
+		}
 		else
 			return false;
 	}
-	if (name_start)
+
+	/*
+	 * Composite's name could be ended with '->', we should ignore last
+	 * dereference This dirty hack is used to see difference between composite
+	 * names and other GUCs It is important in case when we write placeholder
+	 * for composite, cause rule of writing placeholders for composite and
+	 * for other types are not the same
+	 */
+	if (name_start && *(p - 1) != '>')
 		return false;			/* empty name component */
 	/* OK if we found at least one separator */
 	return saw_sep;
@@ -1238,6 +1405,10 @@ find_option(const char *name, bool create_placeholders, bool skip_errors,
 {
 	GUCHashEntry *hentry;
 	int			i;
+	char	   *field_path_start = NULL;
+	char	   *first_bracket = NULL;
+	char	   *first_minus = NULL;
+	char	   *struct_name = NULL;
 
 	Assert(name);
 
@@ -1249,6 +1420,37 @@ find_option(const char *name, bool create_placeholders, bool skip_errors,
 	if (hentry)
 		return hentry->gucvar;
 
+	/*
+	 * If value is a field of composite, then it's name is a path that
+	 * consists of fields names, dereferences "->" and indexes [<number>]
+	 */
+	first_bracket = strchr(name, '[');
+	first_minus = strchr(name, '-');
+
+	if (first_bracket != NULL || first_minus != NULL)
+	{
+		/* take first dereference */
+		if (((first_bracket < first_minus) && first_bracket != NULL) || first_minus == NULL)
+			field_path_start = first_bracket;
+		else
+			field_path_start = first_minus;
+
+		struct_name = guc_malloc(ERROR, (field_path_start - name + 1));
+		strncpy(struct_name, name, (field_path_start - name));
+		struct_name[field_path_start - name] = 0;
+
+		hentry = (GUCHashEntry *) hash_search(guc_hashtab,
+											  &struct_name,
+											  HASH_FIND,
+											  NULL);
+
+		if (hentry)
+		{
+			guc_free(struct_name);
+			return hentry->gucvar;
+		}
+	}
+
 	/*
 	 * See if the name is an obsolete name for a variable.  We assume that the
 	 * set of supported old names is short enough that a brute-force search is
@@ -1267,11 +1469,24 @@ find_option(const char *name, bool create_placeholders, bool skip_errors,
 		 * Check if the name is valid, and if so, add a placeholder.
 		 */
 		if (assignable_custom_variable_name(name, skip_errors, elevel))
+		{
+			if (field_path_start)
+			{
+				struct config_generic *result;
+
+				result = add_placeholder_variable(struct_name, elevel);
+				guc_free(struct_name);
+
+				return result;
+			}
+
 			return add_placeholder_variable(name, elevel);
+		}
 		else
 			return NULL;		/* error message, if any, already emitted */
 	}
 
+	guc_free(struct_name);
 	/* Unknown name and we're not supposed to make a placeholder */
 	if (!skip_errors)
 		ereport(elevel,
@@ -1431,6 +1646,7 @@ check_GUC_name_for_parameter_acl(const char *name)
  * real - can be 0.0, otherwise must be same as the boot_val
  * string - can be NULL, otherwise must be strcmp equal to the boot_val
  * enum - must be same as the boot_val
+ * struct - can be NULL, otherwise must be bitwise equal to the boot_val
  */
 #ifdef USE_ASSERT_CHECKING
 static bool
@@ -1499,6 +1715,38 @@ check_GUC_init(struct config_generic *gconf)
 						 conf->gen.name, conf->boot_val, *conf->variable);
 					return false;
 				}
+				break;
+			}
+		case PGC_COMPOSITE:
+			{
+				struct config_composite *conf = (struct config_composite *) gconf;
+				bool		is_null = true;
+				int			type_size = get_composite_size(conf->type_name);
+
+				for (int i = 0; i < type_size; ++i)
+				{
+					if (*((char *) (conf->variable) + i) != 0)
+					{
+						is_null = false;
+						break;
+					}
+				}
+
+				if (!is_null && composite_cmp(conf->variable, conf->boot_val, conf->type_name))
+				{
+					bool		not_write_to_file = false;
+					char	   *boot_str = composite_to_str(conf->boot_val, conf->type_name, not_write_to_file);
+					char	   *var_str = composite_to_str(conf->variable, conf->type_name, not_write_to_file);
+
+					elog(LOG, "GUC (PGC_COMPOSITE %s) %s, boot_val=%s, C-var=%s",
+						 conf->type_name, conf->gen.name, boot_str, var_str);
+
+					guc_free(boot_str);
+					guc_free(var_str);
+
+					return false;
+				}
+
 				break;
 			}
 	}
@@ -1747,6 +1995,65 @@ InitializeOneGUCOption(struct config_generic *gconf)
 					conf->assign_hook(newval, extra);
 				*conf->variable = conf->reset_val = newval;
 				conf->gen.extra = conf->reset_extra = extra;
+				break;
+			}
+		case PGC_COMPOSITE:
+			{
+				struct config_composite *conf = (struct config_composite *) gconf;
+				void	   *newval;
+				void	   *extra = NULL;
+				void	   *var_buffer = NULL;
+				int			valsize;
+
+				if (!conf->type_name && !conf->definition)
+				{
+					elog(FATAL, "failed to initialize %s. The type not specified",
+						 conf->gen.name);
+
+					break;
+				}
+
+				if (!conf->type_name)
+					conf->type_name = conf->definition->type_name;
+
+				if (!conf->definition
+					&& !is_static_array_type(conf->type_name)
+					&& !is_dynamic_array_type(conf->type_name))
+				{
+					conf->definition = get_type_definition(conf->type_name);
+
+					if (!conf->definition)
+					{
+						elog(FATAL, "failed to initialize %s. Undefined type %s",
+							 conf->gen.name, conf->type_name);
+
+						break;
+					}
+				}
+
+				/* non-NULL boot_val must always get compositedup'd */
+				if (conf->boot_val != NULL)
+					newval = composite_dup(conf->boot_val, conf->type_name);
+				else
+					newval = NULL;
+
+				if (!call_composite_check_hook(conf, newval, &extra,
+											   PGC_S_DEFAULT, LOG))
+					elog(FATAL, "failed to initialize %s to %s",
+						 conf->gen.name, newval ? composite_to_str(newval, conf->type_name, false) : "");
+
+				if (conf->assign_hook)
+					conf->assign_hook(newval, extra);
+
+				conf->reset_val = newval;
+
+				valsize = get_composite_size(conf->type_name);
+				var_buffer = composite_dup(newval, conf->type_name);
+				memcpy(conf->variable, var_buffer, valsize);
+				guc_free(var_buffer);
+
+				conf->gen.extra = conf->reset_extra = extra;
+
 				break;
 			}
 	}
@@ -2091,6 +2398,23 @@ ResetAllOptions(void)
 									conf->reset_extra);
 					break;
 				}
+			case PGC_COMPOSITE:
+				{
+					struct config_composite *conf = (struct config_composite *) gconf;
+					int			valsize;
+
+					if (conf->assign_hook)
+						conf->assign_hook(conf->reset_val,
+										  conf->reset_extra);
+
+					valsize = get_composite_size(conf->type_name);
+					memcpy(conf->variable, conf->reset_val, valsize);
+
+					set_extra_field(&conf->gen, &conf->gen.extra,
+									conf->reset_extra);
+
+					break;
+				}
 		}
 
 		set_guc_source(gconf, gconf->reset_source);
@@ -2505,6 +2829,37 @@ AtEOXact_GUC(bool isCommit, int nestLevel)
 							}
 							break;
 						}
+					case PGC_COMPOSITE:
+						{
+							bool		value_on_heap = true;
+							struct config_composite *conf = (struct config_composite *) gconf;
+							void	   *newval = newvalue.val.compositeval;
+							void	   *newextra = newvalue.extra;
+
+							if (composite_cmp(conf->variable, newval, conf->type_name) ||
+								conf->gen.extra != newextra)
+							{
+								bool		value_not_on_heap = false;
+
+								if (conf->assign_hook)
+									conf->assign_hook(newval, newextra);
+
+								set_composite(conf, &conf->variable, newval, value_not_on_heap);
+								set_extra_field(&conf->gen, &conf->gen.extra,
+												newextra);
+								changed = true;
+							}
+
+							/*
+							 * Release stacked values if not used anymore. We
+							 * could use discard_stack_value() here, but since
+							 * we have type-specific code anyway, might as
+							 * well inline it.
+							 */
+							set_composite(conf, &stack->prior.val.compositeval, NULL, value_on_heap);
+							set_composite(conf, &stack->masked.val.compositeval, NULL, value_on_heap);
+							break;
+						}
 				}
 
 				/*
@@ -3296,6 +3651,27 @@ parse_and_validate_value(struct config_generic *record,
 					return false;
 			}
 			break;
+		case PGC_COMPOSITE:
+			{
+				struct config_composite *conf = (struct config_composite *) record;
+				const char *hintmsg;
+
+				if (!parse_composite(value, conf->type_name, &newval->compositeval, conf->variable,
+									 conf->gen.flags, &hintmsg))
+				{
+					ereport(elevel,
+							(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+							 errmsg("invalid value for parameter \"%s\": \"%s\"",
+									conf->gen.name, value),
+							 hintmsg ? errhint("%s", _(hintmsg)) : 0));
+					return false;
+				}
+
+				if (!call_composite_check_hook(conf, newval->compositeval, newextra,
+											   source, elevel))
+					return false;
+			}
+			break;
 	}
 
 	return true;
@@ -4012,9 +4388,76 @@ set_config_with_handle(const char *name, config_handle *handle,
 
 				if (value)
 				{
-					if (!parse_and_validate_value(record, value,
-												  source, elevel,
-												  &newval_union, &newextra))
+					/* prepare placeholder for structure */
+					bool		is_placeholder = false;
+					bool		composite_value = false;
+					bool		check_parsing;
+					char	   *prepared_strval = (char *) value;	/* be careful with free */
+
+					/* check if string is a field of composite object or it */
+					if (strchr(name, '-') || strchr(name, '['))
+						is_placeholder = true;
+
+					if (suffix_is_arrow(name))
+						composite_value = true;
+
+					if (is_placeholder)
+					{
+						char	   *list;
+						char	   *old_list;
+						int			list_length;
+
+						/*
+						 * If value is a composite, name that ended with "->"
+						 * All other values are scalar, they must be escaped
+						 */
+						if (!composite_value)
+						{
+							char	   *escaped = escape_single_quotes_ascii(value);
+							int			quoted_length = strlen(escaped) + 3;
+							char	   *quoted = guc_malloc(ERROR, quoted_length);
+
+							snprintf(quoted, quoted_length, "\'%s\'", escaped);
+
+							prepared_strval = convert_path_to_composite_value(name, quoted);
+
+							free(escaped);
+							guc_free(quoted);
+						}
+						else
+							prepared_strval = convert_path_to_composite_value(name, value);
+
+						/* get list from current value */
+						old_list = *(conf->variable);
+
+						if (old_list == NULL)
+							old_list = "";
+
+						/* Add value to placeholder patch list */
+						if (strlen(old_list) == 0)
+						{
+							list_length= strlen(prepared_strval) + 1;
+							list = guc_malloc(ERROR, list_length);
+							snprintf(list, list_length, "%s", prepared_strval);
+						}
+						else
+						{
+							list_length = strlen(old_list) + strlen(prepared_strval) + 2;
+							list = guc_malloc(ERROR, list_length);
+							snprintf(list, list_length, "%s;%s", old_list, prepared_strval);
+						}
+
+						guc_free(prepared_strval);
+						prepared_strval = list;
+					}
+
+					check_parsing = parse_and_validate_value(record, prepared_strval, source,
+															 elevel, &newval_union, &newextra);
+
+					if (prepared_strval != value)
+						guc_free(prepared_strval);
+
+					if (!check_parsing)
 						return 0;
 				}
 				else if (source == PGC_S_DEFAULT)
@@ -4268,33 +4711,172 @@ set_config_with_handle(const char *name, config_handle *handle,
 
 #undef newval
 			}
-	}
-
-	if (changeVal && (record->flags & GUC_REPORT) &&
-		!(record->status & GUC_NEEDS_REPORT))
-	{
-		record->status |= GUC_NEEDS_REPORT;
-		slist_push_head(&guc_report_list, &record->report_link);
-	}
-
-	return changeVal ? 1 : -1;
-}
-
-
-/*
- * Retrieve a config_handle for the given name, suitable for calling
- * set_config_with_handle(). Only return handle to permanent GUC.
- */
-config_handle *
-get_config_handle(const char *name)
-{
-	struct config_generic *gen = find_option(name, false, false, 0);
+		case PGC_COMPOSITE:
+			{
+				struct config_composite *conf = (struct config_composite *) record;
+				bool		parse_result;
 
-	if (gen && ((gen->flags & GUC_CUSTOM_PLACEHOLDER) == 0))
-		return gen;
+#define newval (newval_union.compositeval)
+				if (value)
+				{
+					const char *str_val = (const char *) normalize_composite_value(name, value);
 
-	return NULL;
-}
+					parse_result = parse_and_validate_value(record, str_val, source, elevel,
+															&newval_union, &newextra);
+
+					guc_free((void *) str_val);
+
+					if (!parse_result)
+						return 0;
+				}
+				else if (source == PGC_S_DEFAULT)
+				{
+					/* non-NULL boot_val must be compositedup'd */
+					if (conf->boot_val != NULL)
+					{
+						newval = composite_dup(conf->boot_val, conf->type_name);
+						if (newval == NULL)
+							return 0;
+					}
+					else
+						newval = NULL;
+
+					if (!call_composite_check_hook(conf, newval, &newextra,
+												   source, elevel))
+					{
+						guc_free(newval);
+						return 0;
+					}
+				}
+				else
+				{
+					/*
+					 * composite_dup not needed, since reset_val is already
+					 * under guc.c's control
+					 */
+					newval = conf->reset_val;
+					newextra = conf->reset_extra;
+					source = conf->gen.reset_source;
+					context = conf->gen.reset_scontext;
+					srole = conf->gen.reset_srole;
+				}
+
+				if (prohibitValueChange)
+				{
+					bool		newval_different;
+
+					/* newval shouldn't be NULL, so we're a bit sloppy here */
+					newval_different = (conf->variable == NULL ||
+										newval == NULL ||
+										composite_cmp(conf->variable, newval, conf->type_name) != 0);
+
+					/* Release newval, unless it's reset_val */
+					if (newval && !composite_used(conf, newval))
+						free_composite(newval, conf->type_name);
+					/* Release newextra, unless it's reset_extra */
+					if (newextra && !extra_field_used(&conf->gen, newextra))
+						guc_free(newextra);
+
+					if (newval_different)
+					{
+						record->status |= GUC_PENDING_RESTART;
+						ereport(elevel,
+								(errcode(ERRCODE_CANT_CHANGE_RUNTIME_PARAM),
+								 errmsg("parameter \"%s\" cannot be changed without restarting the server",
+										conf->gen.name)));
+						return 0;
+					}
+					record->status &= ~GUC_PENDING_RESTART;
+					return -1;
+				}
+
+				if (changeVal)
+				{
+					bool		value_on_heap = false;
+
+					/* Save old value to support transaction abort */
+					if (!makeDefault)
+						push_old_value(&conf->gen, action);
+
+					if (conf->assign_hook)
+						conf->assign_hook(newval, newextra);
+					set_composite(conf, &conf->variable, newval, value_on_heap);
+					set_extra_field(&conf->gen, &conf->gen.extra,
+									newextra);
+					set_guc_source(&conf->gen, source);
+					conf->gen.scontext = context;
+					conf->gen.srole = srole;
+				}
+
+				if (makeDefault)
+				{
+					bool		value_on_heap = true;
+					GucStack   *stack;
+
+					if (conf->gen.reset_source <= source)
+					{
+						set_composite(conf, &conf->reset_val, newval, value_on_heap);
+						set_extra_field(&conf->gen, &conf->reset_extra,
+										newextra);
+						conf->gen.reset_source = source;
+						conf->gen.reset_scontext = context;
+						conf->gen.reset_srole = srole;
+					}
+					for (stack = conf->gen.stack; stack; stack = stack->prev)
+					{
+						if (stack->source <= source)
+						{
+							set_composite(conf, &stack->prior.val.compositeval,
+										  newval, value_on_heap);
+							set_extra_field(&conf->gen, &stack->prior.extra,
+											newextra);
+							stack->source = source;
+							stack->scontext = context;
+							stack->srole = srole;
+						}
+					}
+				}
+
+
+				/* Perhaps we didn't install newval anywhere */
+				if (newval && !composite_used(conf, newval))
+					free_composite(newval, conf->type_name);
+
+				/* Perhaps we didn't install newextra anywhere */
+				if (newextra && !extra_field_used(&conf->gen, newextra))
+				{
+					guc_free(newextra);
+					break;
+				}
+#undef newval
+			}
+	}
+
+	if (changeVal && (record->flags & GUC_REPORT) &&
+		!(record->status & GUC_NEEDS_REPORT))
+	{
+		record->status |= GUC_NEEDS_REPORT;
+		slist_push_head(&guc_report_list, &record->report_link);
+	}
+
+	return changeVal ? 1 : -1;
+}
+
+
+/*
+ * Retrieve a config_handle for the given name, suitable for calling
+ * set_config_with_handle(). Only return handle to permanent GUC.
+ */
+config_handle *
+get_config_handle(const char *name)
+{
+	struct config_generic *gen = find_option(name, false, false, 0);
+
+	if (gen && ((gen->flags & GUC_CUSTOM_PLACEHOLDER) == 0))
+		return gen;
+
+	return NULL;
+}
 
 
 /*
@@ -4395,6 +4977,14 @@ GetConfigOption(const char *name, bool missing_ok, bool restrict_privileged)
 		case PGC_ENUM:
 			return config_enum_lookup_by_value((struct config_enum *) record,
 											   *((struct config_enum *) record)->variable);
+		case PGC_COMPOSITE:
+			{
+				bool		not_write_to_file = false;
+				void	   *var = ((struct config_composite *) record)->variable;
+				const char *var_type = ((struct config_composite *) record)->type_name;
+
+				return var ? composite_to_str(var, var_type, not_write_to_file) : "nil";
+			}
 	}
 	return NULL;
 }
@@ -4443,6 +5033,15 @@ GetConfigOptionResetString(const char *name)
 		case PGC_ENUM:
 			return config_enum_lookup_by_value((struct config_enum *) record,
 											   ((struct config_enum *) record)->reset_val);
+
+		case PGC_COMPOSITE:
+			{
+				bool		not_write_to_file = false;
+				void	   *val = ((struct config_composite *) record)->reset_val;
+				const char *val_type = ((struct config_composite *) record)->type_name;
+
+				return val ? composite_to_str(val, val_type, not_write_to_file) : "nil";
+			}
 	}
 	return NULL;
 }
@@ -4493,25 +5092,52 @@ write_auto_conf_file(int fd, const char *filename, ConfigVariable *head)
 				 errmsg("could not write to file \"%s\": %m", filename)));
 	}
 
-	/* Emit each parameter, properly quoting the value */
+	/*
+	 * Emit each parameter, quoting the value except when the option has a
+	 * composite value
+	 */
 	for (item = head; item != NULL; item = item->next)
 	{
+		bool		is_struct = false;
 		char	   *escaped;
 
 		resetStringInfo(&buf);
 
-		appendStringInfoString(&buf, item->name);
-		appendStringInfoString(&buf, " = '");
+		/*
+		 * Strutures names could be ended with "->" only in internal
+		 * representation. So delete this suffix for user interface
+		 */
+		if (item->name[strlen(item->name) - 2] == '-')
+		{
+			item->name[strlen(item->name) - 2] = 0;
+			appendStringInfoString(&buf, item->name);
+			is_struct = true;
+			item->name[strlen(item->name) - 2] = '-';
+		}
+		else
+			appendStringInfoString(&buf, item->name);
+		appendStringInfoString(&buf, " = ");
 
-		escaped = escape_single_quotes_ascii(item->value);
-		if (!escaped)
-			ereport(ERROR,
-					(errcode(ERRCODE_OUT_OF_MEMORY),
-					 errmsg("out of memory")));
-		appendStringInfoString(&buf, escaped);
-		free(escaped);
+		/*
+		 * Append quotes for all scalar types But do not append qoutes for
+		 * structure values
+		 */
+		if (!is_struct)
+		{
+			appendStringInfoString(&buf, "\'");
+			escaped = escape_single_quotes_ascii(item->value);
+			if (!escaped)
+				ereport(ERROR,
+						(errcode(ERRCODE_OUT_OF_MEMORY),
+						 errmsg("out of memory")));
+			appendStringInfoString(&buf, escaped);
+			free(escaped);
+			appendStringInfoString(&buf, "\'");
+		}
+		else
+			appendStringInfoString(&buf, item->value);
 
-		appendStringInfoString(&buf, "'\n");
+		appendStringInfoString(&buf, "\n");
 
 		errno = 0;
 		if (write(fd, buf.data, buf.len) != buf.len)
@@ -4546,6 +5172,9 @@ replace_auto_config_value(ConfigVariable **head_p, ConfigVariable **tail_p,
 			   *next,
 			   *prev = NULL;
 
+	char	   *short_name = tokenize_field_path(pstrdup(name));
+	bool		is_struct = strchr(name, '-');
+
 	/*
 	 * Remove any existing match(es) for "name".  Normally there'd be at most
 	 * one, but if external tools have modified the config file, there could
@@ -4553,8 +5182,11 @@ replace_auto_config_value(ConfigVariable **head_p, ConfigVariable **tail_p,
 	 */
 	for (item = *head_p; item != NULL; item = next)
 	{
+		char	   *tokenized_item_name = tokenize_field_path(pstrdup(item->name));
+
 		next = item->next;
-		if (guc_name_compare(item->name, name) == 0)
+
+		if (guc_name_compare(tokenized_item_name, short_name) == 0)
 		{
 			/* found a match, delete it */
 			if (prev)
@@ -4571,15 +5203,23 @@ replace_auto_config_value(ConfigVariable **head_p, ConfigVariable **tail_p,
 		}
 		else
 			prev = item;
+		pfree(tokenized_item_name);
 	}
 
 	/* Done if we're trying to delete it */
 	if (value == NULL)
+	{
+		pfree(short_name);
 		return;
+	}
 
 	/* OK, append a new entry */
 	item = palloc(sizeof *item);
-	item->name = pstrdup(name);
+	if (is_struct)
+		item->name = pstrdup(name);
+	else
+		item->name = pstrdup(short_name);
+
 	item->value = pstrdup(value);
 	item->errmsg = NULL;
 	item->filename = pstrdup("");	/* new item has no location */
@@ -4593,6 +5233,7 @@ replace_auto_config_value(ConfigVariable **head_p, ConfigVariable **tail_p,
 	else
 		(*tail_p)->next = item;
 	*tail_p = item;
+	pfree(short_name);
 }
 
 
@@ -4633,6 +5274,17 @@ AlterSystemSetConfigFile(AlterSystemStmt *altersysstmt)
 	switch (altersysstmt->setstmt->kind)
 	{
 		case VAR_SET_VALUE:
+			if (stmt_has_serialized_composite(altersysstmt->setstmt))
+			{
+				/* replace name for structs */
+				int			composite_name_len = strlen(name) + 3;
+				char	   *composite_name = (char *) palloc(composite_name_len);
+
+				snprintf(composite_name, composite_name_len, "%s->", name);
+				pfree(name);
+				name = composite_name;
+			}
+
 			value = ExtractSetVariableArgs(altersysstmt->setstmt);
 			break;
 
@@ -4705,8 +5357,15 @@ AlterSystemSetConfigFile(AlterSystemStmt *altersysstmt)
 			{
 				union config_var_val newval;
 				void	   *newextra = NULL;
+				char	   *prepared_value = value;
 
-				if (!parse_and_validate_value(record, value,
+				/*
+				 * For struct values we should convert path
+				 */
+				if (record->vartype == PGC_COMPOSITE)
+					prepared_value = normalize_composite_value(name, value);
+
+				if (!parse_and_validate_value(record, prepared_value,
 											  PGC_S_FILE, ERROR,
 											  &newval, &newextra))
 					ereport(ERROR,
@@ -4714,8 +5373,74 @@ AlterSystemSetConfigFile(AlterSystemStmt *altersysstmt)
 							 errmsg("invalid value for parameter \"%s\": \"%s\"",
 									name, value)));
 
+				/*
+				 * Each composite placeholder has -> in name For placeholder
+				 * we using rules from set_config_with_handle Also cut off
+				 * suffix of name
+				 */
+				if (record->vartype == PGC_STRING && strchr(name, '-'))
+				{
+					struct config_string *conf = (struct config_string *) record;
+
+					if (value)
+					{
+						char	   *prepared = normalize_composite_value(name, value);
+						char	   *list;
+						char	   *old_list = *conf->variable ? *conf->variable : "";
+						int			list_len = strlen(old_list) + strlen(prepared) + 2;
+
+						/* Add value to placeholder patch list */
+						list = guc_malloc(ERROR, list_len);
+						snprintf(list, list_len, "%s%s;", old_list, prepared);
+
+						pfree(value);
+						value = pstrdup(list);
+						guc_free(list);
+						guc_free(prepared);
+					}
+					tokenize_field_path(name);
+				}
+
 				if (record->vartype == PGC_STRING && newval.stringval != NULL)
+				{
 					guc_free(newval.stringval);
+				}
+
+				/*
+				 * For struct we replace value with serialized parsed value
+				 * Notice that patch is applied to current value
+				 */
+				if (record->vartype == PGC_COMPOSITE)
+				{
+					char	   *serial_struct = NULL;
+					char	   *composite_name;
+					int			composite_name_length;
+					bool		write_to_file = true;
+
+					pfree(value);
+					serial_struct = composite_to_str(newval.compositeval, ((struct config_composite *) record)->type_name, write_to_file);
+					if (serial_struct == NULL)
+						value = NULL;
+					else
+					{
+						value = pstrdup(serial_struct);
+						guc_free(serial_struct);
+					}
+					free_composite(newval.compositeval, ((struct config_composite *) record)->type_name);
+
+					/*
+					 * replace name with struct name + "->"" We need suffix to
+					 * detect structure in replace_auto_config_value and
+					 * write_auto_conf_file
+					 */
+					name = tokenize_field_path(name);
+					composite_name_length = strlen(name) + 3;
+					composite_name = (char *) palloc(composite_name_length);
+
+					snprintf(composite_name, composite_name_length, "%s->", name);
+					pfree(name);
+					name = composite_name;
+				}
 				guc_free(newextra);
 			}
 		}
@@ -4734,6 +5459,25 @@ AlterSystemSetConfigFile(AlterSystemStmt *altersysstmt)
 			 */
 			if (value || !valid_custom_variable_name(name))
 				(void) assignable_custom_variable_name(name, false, ERROR);
+
+			/*
+			 * If option is composite, we can understand it with "->" or "[]"
+			 * in name
+			 */
+			if (strchr(name, '-') || strchr(name, '['))
+			{
+				char	   *prepared = normalize_composite_value(name, value);
+
+				pfree(value);
+				value = pstrdup(prepared);
+				guc_free(prepared);
+
+				/*
+				 * prepare name to replace_auto_config_value We set -> at the
+				 * end of name to distinguish placeholders for composites
+				 */
+				tokenize_field_path(name);
+			}
 		}
 
 		/*
@@ -4944,6 +5688,15 @@ define_custom_variable(struct config_generic *variable)
 	const char *name = variable->name;
 	GUCHashEntry *hentry;
 	struct config_string *pHolder;
+	char	   *name_with_suffix = (char *) name;
+
+	if (variable->vartype == PGC_COMPOSITE)
+	{
+		int			name_with_suffix_len = strlen(name) + 3;
+
+		name_with_suffix = guc_malloc(ERROR, name_with_suffix_len);
+		snprintf(name_with_suffix, name_with_suffix_len, "%s->", name);
+	}
 
 	/* Check mapping between initial and default value */
 	Assert(check_GUC_init(variable));
@@ -5009,7 +5762,7 @@ define_custom_variable(struct config_generic *variable)
 
 	/* First, apply the reset value if any */
 	if (pHolder->reset_val)
-		(void) set_config_option_ext(name, pHolder->reset_val,
+		(void) set_config_option_ext(name_with_suffix, pHolder->reset_val,
 									 pHolder->gen.reset_scontext,
 									 pHolder->gen.reset_source,
 									 pHolder->gen.reset_srole,
@@ -5030,6 +5783,9 @@ define_custom_variable(struct config_generic *variable)
 
 	/* Now we can free the no-longer-referenced placeholder variable */
 	free_placeholder(pHolder);
+
+	if (name_with_suffix != name)
+		guc_free(name_with_suffix);
 }
 
 /*
@@ -5049,6 +5805,15 @@ reapply_stacked_values(struct config_generic *variable,
 {
 	const char *name = variable->name;
 	GucStack   *oldvarstack = variable->stack;
+	char	   *name_with_suffix = (char *) name;
+
+	if (variable->vartype == PGC_COMPOSITE)
+	{
+		int			name_with_suffix_len = strlen(name) + 3;
+
+		name_with_suffix = guc_malloc(ERROR, name_with_suffix_len);
+		snprintf(name_with_suffix, name_with_suffix_len, "%s->", name);
+	}
 
 	if (stack != NULL)
 	{
@@ -5061,21 +5826,21 @@ reapply_stacked_values(struct config_generic *variable,
 		switch (stack->state)
 		{
 			case GUC_SAVE:
-				(void) set_config_option_ext(name, curvalue,
+				(void) set_config_option_ext(name_with_suffix, curvalue,
 											 curscontext, cursource, cursrole,
 											 GUC_ACTION_SAVE, true,
 											 WARNING, false);
 				break;
 
 			case GUC_SET:
-				(void) set_config_option_ext(name, curvalue,
+				(void) set_config_option_ext(name_with_suffix, curvalue,
 											 curscontext, cursource, cursrole,
 											 GUC_ACTION_SET, true,
 											 WARNING, false);
 				break;
 
 			case GUC_LOCAL:
-				(void) set_config_option_ext(name, curvalue,
+				(void) set_config_option_ext(name_with_suffix, curvalue,
 											 curscontext, cursource, cursrole,
 											 GUC_ACTION_LOCAL, true,
 											 WARNING, false);
@@ -5083,14 +5848,14 @@ reapply_stacked_values(struct config_generic *variable,
 
 			case GUC_SET_LOCAL:
 				/* first, apply the masked value as SET */
-				(void) set_config_option_ext(name, stack->masked.val.stringval,
+				(void) set_config_option_ext(name_with_suffix, stack->masked.val.stringval,
 											 stack->masked_scontext,
 											 PGC_S_SESSION,
 											 stack->masked_srole,
 											 GUC_ACTION_SET, true,
 											 WARNING, false);
 				/* then apply the current value as LOCAL */
-				(void) set_config_option_ext(name, curvalue,
+				(void) set_config_option_ext(name_with_suffix, curvalue,
 											 curscontext, cursource, cursrole,
 											 GUC_ACTION_LOCAL, true,
 											 WARNING, false);
@@ -5116,7 +5881,7 @@ reapply_stacked_values(struct config_generic *variable,
 			cursource != pHolder->gen.reset_source ||
 			cursrole != pHolder->gen.reset_srole)
 		{
-			(void) set_config_option_ext(name, curvalue,
+			(void) set_config_option_ext(name_with_suffix, curvalue,
 										 curscontext, cursource, cursrole,
 										 GUC_ACTION_SET, true, WARNING, false);
 			if (variable->stack != NULL)
@@ -5126,6 +5891,9 @@ reapply_stacked_values(struct config_generic *variable,
 			}
 		}
 	}
+
+	if (name != name_with_suffix)
+		guc_free(name_with_suffix);
 }
 
 /*
@@ -5289,6 +6057,66 @@ DefineCustomEnumVariable(const char *name,
 	define_custom_variable(&var->gen);
 }
 
+void
+DefineCustomCompositeVariable(const char *name,
+							  const char *short_desc,
+							  const char *long_desc,
+							  const char *type_name,
+							  void *valueAddr,
+							  const void *bootValueAddr,
+							  GucContext context,
+							  int flags,
+							  GucCompositeCheckHook check_hook,
+							  GucCompositeAssignHook assign_hook,
+							  GucShowHook show_hook)
+{
+	struct config_composite *var;
+	struct type_definition *type_definition = NULL;
+
+	if (!is_static_array_type(type_name) && !is_dynamic_array_type(type_name))
+	{
+		type_definition = get_type_definition(type_name);
+		if (type_definition == NULL)
+			ereport(ERROR,
+					(errcode(ERRCODE_INTERNAL_ERROR),
+					 errmsg("option %s has undefined type %s", name, type_name)));
+	}
+
+	var = (struct config_composite *)
+		init_custom_variable(name, short_desc, long_desc, context, flags,
+							 PGC_COMPOSITE, sizeof(struct config_composite));
+	var->variable = valueAddr;
+	var->type_name = type_name;
+	var->boot_val = bootValueAddr;
+	var->check_hook = check_hook;
+	var->assign_hook = assign_hook;
+	var->show_hook = show_hook;
+	var->definition = type_definition;
+	define_custom_variable(&var->gen);
+}
+
+void
+DefineCustomCompositeType(const char *type_name, const char *signature)
+{
+	bool		found;
+	OptionTypeHashEntry *type_hentry;
+	struct type_definition *type_definition = (struct type_definition *) guc_malloc(ERROR, sizeof(struct type_definition));
+
+	memset(type_definition, 0, sizeof(struct type_definition));
+	type_definition->type_name = type_name;
+	type_definition->signature = signature;
+	type_hentry = (OptionTypeHashEntry *) hash_search(guc_types_hashtab,
+													  &type_definition->type_name,
+													  HASH_ENTER,
+													  &found);
+
+	Assert(!found);
+	type_hentry->definition = type_definition;
+
+	if (!is_scalar_type(type_definition->type_name))
+		init_type_definition(type_definition);
+}
+
 /*
  * Mark the given GUC prefix as "reserved".
  *
@@ -5429,6 +6257,22 @@ get_explain_guc_options(int *num)
 					modified = (lconf->boot_val != *(lconf->variable));
 				}
 				break;
+			case PGC_COMPOSITE:
+				{
+					struct config_composite *lconf = (struct config_composite *) conf;
+
+					if (lconf->boot_val == NULL &&
+						lconf->variable == NULL)
+						modified = false;
+					else if (lconf->boot_val == NULL ||
+							 lconf->variable == NULL)
+						modified = true;
+					else
+					{
+						modified = (composite_cmp(lconf->boot_val, lconf->variable, lconf->type_name) != 0);
+					}
+				}
+				break;
 
 			default:
 				elog(ERROR, "unexpected GUC type: %d", conf->vartype);
@@ -5454,6 +6298,8 @@ char *
 GetConfigOptionByName(const char *name, const char **varname, bool missing_ok)
 {
 	struct config_generic *record;
+	char	   *first_minus = strchr(name, '-');
+	char	   *first_bracket = strchr(name, '[');
 
 	record = find_option(name, false, missing_ok, ERROR);
 	if (record == NULL)
@@ -5473,6 +6319,39 @@ GetConfigOptionByName(const char *name, const char **varname, bool missing_ok)
 	if (varname)
 		*varname = record->name;
 
+	/*
+	 * For structure fields we use custom show function. yes it's so sloppy
+	 * but SHowGucOption can't use string as parameter
+	 */
+	if (first_minus || first_bracket)
+	{
+		void	   *structp = NULL;
+		char	   *field_type;
+		char	   *str_opt;
+		char	   *result;
+		struct config_composite *conf = (struct config_composite *) record;
+
+		if (conf->show_hook)
+			result = pstrdup(conf->show_hook());
+		else
+		{
+			bool		not_write_to_file = false;
+
+			/* find start of structure and convert it to struct */
+			structp = get_nested_field_ptr(conf->variable, conf->type_name, name);
+			field_type = get_nested_field_type_name(conf->type_name, name);
+			str_opt = composite_to_str(structp, field_type, not_write_to_file);
+
+			/* copy result */
+			result = pstrdup(str_opt);
+
+			/* free work structures */
+			guc_free(field_type);
+			guc_free(str_opt);
+		}
+		return result;
+	}
+
 	return ShowGUCOption(record, true);
 }
 
@@ -5580,6 +6459,27 @@ ShowGUCOption(struct config_generic *record, bool use_units)
 			}
 			break;
 
+		case PGC_COMPOSITE:
+			{
+				struct config_composite *conf = (struct config_composite *) record;
+
+				if (conf->show_hook)
+					val = conf->show_hook();
+				else if (conf->variable)
+				{
+					bool		not_write_to_file = false;
+					char	   *result;
+					char	   *value = composite_to_str(conf->variable, conf->type_name, not_write_to_file);
+
+					result = pstrdup(value);
+					guc_free(value);
+					return result;	/* yes it's awful, so the shortest way
+									 * that I found */
+				}
+				else
+					val = "nil";
+			}
+			break;
 		default:
 			/* just to keep compiler quiet */
 			val = "???";
@@ -5658,6 +6558,21 @@ write_one_nondefault_variable(FILE *fp, struct config_generic *gconf)
 						config_enum_lookup_by_value(conf, *conf->variable));
 			}
 			break;
+
+		case PGC_COMPOSITE:
+			{
+				struct config_composite *conf = (struct config_composite *) gconf;
+
+				if (conf->variable)
+				{
+					bool		not_write_to_file = false;
+					char	   *serialized = composite_to_str(conf->variable, conf->type_name, not_write_to_file);
+
+					fprintf(fp, "%s", serialized);
+					guc_free(serialized);
+				}
+			}
+			break;
 	}
 
 	fputc(0, fp);
@@ -5940,6 +6855,16 @@ estimate_variable_size(struct config_generic *gconf)
 				valsize = strlen(config_enum_lookup_by_value(conf, *conf->variable));
 			}
 			break;
+
+		case PGC_COMPOSITE:
+			{
+				struct config_composite *conf = (struct config_composite *) gconf;
+
+				if (conf->variable)
+					valsize = get_length_composite_str(conf->variable, conf->type_name);
+				else
+					valsize = 0;
+			}
 	}
 
 	/* Allow space for terminating zero-byte for value */
@@ -6098,6 +7023,16 @@ serialize_variable(char **destptr, Size *maxbytes,
 							 config_enum_lookup_by_value(conf, *conf->variable));
 			}
 			break;
+
+		case PGC_COMPOSITE:
+			{
+				bool		not_write_to_file = false;
+				struct config_composite *conf = (struct config_composite *) gconf;
+				char	   *struct_str = composite_to_str(conf->variable, conf->type_name, not_write_to_file);
+
+				do_serialize(destptr, maxbytes, "%s", struct_str);
+				guc_free(struct_str);
+			}
 	}
 
 	do_serialize(destptr, maxbytes, "%s",
@@ -6315,6 +7250,17 @@ RestoreGUCState(void *gucstate)
 						guc_free(conf->reset_extra);
 					break;
 				}
+			case PGC_COMPOSITE:
+				{
+					struct config_composite *conf = (struct config_composite *)gconf;
+
+					free_composite(conf->variable, conf->type_name);
+					if (conf->reset_val && conf->reset_val != conf->variable)
+						free_composite(conf->reset_val, conf->type_name);
+					if (conf->reset_extra && conf->reset_extra != gconf->extra)
+						guc_free(conf->reset_extra);
+					break;
+				}
 		}
 		/* Remove it from any lists it's in. */
 		RemoveGUCFromLists(gconf);
@@ -7008,3 +7954,53 @@ call_enum_check_hook(struct config_enum *conf, int *newval, void **extra,
 
 	return true;
 }
+
+static bool
+call_composite_check_hook(struct config_composite *conf, void *newval, void **extra,
+						GucSource source, int elevel)
+{
+	volatile bool result = true;
+
+	/* Quick success if no hook */
+	if (!conf->check_hook)
+		return true;
+
+	/*
+	 * If elevel is ERROR, or if the check_hook itself throws an elog
+	 * (undesirable, but not always avoidable), make sure we don't leak the
+	 * already-malloc'd newval struct.
+	 */
+	PG_TRY();
+	{
+		/* Reset variables that might be set by hook */
+		GUC_check_errcode_value = ERRCODE_INVALID_PARAMETER_VALUE;
+		GUC_check_errmsg_string = NULL;
+		GUC_check_errdetail_string = NULL;
+		GUC_check_errhint_string = NULL;
+
+		if (!conf->check_hook(newval, extra, source))
+		{
+			ereport(elevel,
+					(errcode(GUC_check_errcode_value),
+					 GUC_check_errmsg_string ?
+					 errmsg_internal("%s", GUC_check_errmsg_string) :
+					 errmsg("invalid value for parameter \"%s\": %s",
+							conf->gen.name, newval ? composite_to_str(newval, conf->type_name, false) : ""),
+					 GUC_check_errdetail_string ?
+					 errdetail_internal("%s", GUC_check_errdetail_string) : 0,
+					 GUC_check_errhint_string ?
+					 errhint("%s", GUC_check_errhint_string) : 0));
+			/* Flush any strings created in ErrorContext */
+			FlushErrorState();
+			result = false;
+		}
+	}
+	PG_CATCH();
+	{
+		guc_free(newval);
+		PG_RE_THROW();
+	}
+	PG_END_TRY();
+
+	return result;
+}
diff --git a/src/backend/utils/misc/guc_composite.c b/src/backend/utils/misc/guc_composite.c
new file mode 100644
index 0000000000..81319640f7
--- /dev/null
+++ b/src/backend/utils/misc/guc_composite.c
@@ -0,0 +1,1486 @@
+/*--------------------------------------------------------------------
+ * guc_composite.c
+ *
+ * This file contains the implementation of functions
+ * related to the custom composite type system.
+ *
+ * The functions are divided into 3 groups:
+ * 1. registration and support for composite types
+ * 2. support for composite options
+ * 3. printing composite options
+ *
+ * See src/backend/utils/misc/README for more information.
+ *
+ * Copyright (c) 2000-2025, PostgreSQL Global Development Group
+ *
+ * IDENTIFICATION
+ *	  src/backend/utils/misc/guc_composite.c
+ *
+ *--------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include <ctype.h>
+#include <errno.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <float.h>
+#include <string.h>
+#include <limits.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/ucontext.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "guc_composite.h"
+#include "utils/builtins.h"
+#include "lib/stringinfo.h"
+
+
+bool		extended_array_view;
+
+#define STRUCT_FIELDS_DELIMETER ';'
+#define STRUCT_FIELDS_DELIMETER_STR ";"
+
+#define element_of_data_of_dynamic_array(type_name, field) (is_dynamic_array_type(type_name) &&\
+											strcmp(field,"data") != 0 &&\
+											strcmp(field, "size") != 0)
+
+/*
+ * That structure is template of dynamic array representation in C code
+ * It is used only to get sizes and offsets of it's fields
+ */
+struct DynArrTmp
+{
+	void	   *data;
+	int			size;
+};
+
+/* The global hash table of definitions of composite types for guc options */
+HTAB	   *guc_types_hashtab;
+
+static int	get_type_offset(const char *type_name);
+static char *get_struct_field_type(const char *type_name, const char *field);
+static char *print_empty_array(bool writing_to_file, bool extend);
+static char *array_to_str(const void *data, int size, const char *type, bool writing_to_file, bool extend);
+static char *static_array_to_str(const void *structp, const char *type, bool writing_to_file);
+static char *dynamic_array_to_str(const void *structp, const char *type, bool writing_to_file);
+static char *scalar_to_str(const void *structp, const char *type_name, bool writing_to_file);
+static char *struct_to_str(const void *structp, const char *type, bool writing_to_file);
+static void static_array_dup(void *dest_struct, const void *src_struct, const char *type_name);
+static void struct_dup(void *dest_struct, const void *src_struct, const char *type_name);
+static int	array_data_cmp(const void *first, const void *second, const char *type, int size);
+static int	dynamic_array_cmp(const void *first, const void *second, const char *type);
+static int	struct_cmp(const void *first, const void *second, const char *type);
+static void free_static_array(void *delptr, const char *type);
+static void free_dynamic_array(void *delptr, const char *type);
+static void free_struct(void *delptr, const char *type);
+static void dynamic_array_dup(void *dest_struct, const void *src_struct, const char *type);
+static int	get_dynamic_array_size(const char *type_name, const void *structp);
+
+
+int
+get_static_array_length(const char *type_name)
+{
+	char	   *length_str_begin = strchr(type_name, '[');
+	char	   *length_str_end = NULL;
+	char	   *parsed_end = NULL;
+	long		length = 0;
+
+	if (length_str_begin == NULL)
+		return 0;
+
+	length_str_begin++;
+
+	length_str_end = strchr(length_str_begin, ']');
+
+	if (!length_str_end)
+		return 0;
+
+	length = strtol(length_str_begin, &parsed_end, 10);
+
+	if (errno == ERANGE)
+		return 0;
+
+	while (parsed_end != length_str_end)
+	{
+		if (!isblank(*parsed_end++))
+			return 0;
+	}
+
+	return length;
+}
+
+bool
+is_static_array_type(const char *type_name)
+{
+	return (bool) get_static_array_length(type_name);
+}
+
+bool
+is_dynamic_array_type(const char *type_name)
+{
+	char	   *size_str_begin = strchr(type_name, '[');
+
+	if (size_str_begin && *(size_str_begin + 1) == ']')
+		return true;
+
+	return false;
+}
+
+char *
+get_array_basic_type(const char *array_type)
+{
+	ptrdiff_t	first_part_len;
+	ptrdiff_t	second_part_len;
+	size_t		type_len;
+	char	   *type_name;
+	const char *bracket_close;
+	const char *bracket_open = strchr(array_type, '[');
+
+	if (!bracket_open)
+		return NULL;
+
+	bracket_close = strchr(bracket_open, ']');
+
+	if (!bracket_close)
+		return NULL;
+
+	first_part_len = bracket_open - array_type;
+	second_part_len = strchr(bracket_close, '\0') - bracket_close;
+	type_len = first_part_len + second_part_len;
+
+	type_name = guc_malloc(ERROR, type_len);
+	strncpy(type_name, array_type, first_part_len);
+	strncpy(type_name + first_part_len, bracket_close + 1, second_part_len);
+
+	return type_name;
+}
+
+struct type_definition *
+get_type_definition(const char *type_name)
+{
+	struct type_definition *definition;
+	bool		found = false;
+	OptionTypeHashEntry *type_hentry = NULL;
+
+	type_hentry = (OptionTypeHashEntry *) hash_search(guc_types_hashtab, &type_name, HASH_FIND, &found);
+
+	if (found)
+	{
+		definition = type_hentry->definition;
+
+		return definition;
+	}
+
+	return NULL;
+}
+
+int
+get_array_size(const char *type_name, const int length)
+{
+	int			array_size;
+	int			element_size;
+	int			element_offset;
+	char	   *basic_type = get_array_basic_type(type_name);
+
+	if (!basic_type || length < 0)
+		return -1;
+
+	element_offset = get_type_offset(basic_type);
+	element_size = get_composite_size(basic_type);
+	guc_free(basic_type);
+
+	if (element_offset < 0 || element_size < 0)
+		return -1;
+
+	array_size = length * (element_size + (element_size % element_offset));
+
+	return array_size;
+}
+
+static int
+get_static_array_size(const char *type_name)
+{
+	int			length = get_static_array_length(type_name);
+
+	return get_array_size(type_name, length);
+}
+
+static int
+get_dynamic_array_size(const char *type_name, const void *structp)
+{
+	int			length = dynamic_array_size(structp);
+
+	return get_array_size(type_name, length);
+}
+
+static int
+get_struct_size(const char *type_name)
+{
+	struct type_definition *struct_type = NULL;
+
+	if ((struct_type = get_type_definition(type_name)))
+		return struct_type->type_size;
+
+	return -1;
+}
+
+int
+get_composite_size(const char *type_name)
+{
+	if (!type_name)
+		return -1;
+
+	/*
+	 * Dynamic array is a struct that has 2 fields: pointer, int (see
+	 * DynArrTmp) Do not use this function for getting size of allocated data
+	 * of dynamic array
+	 */
+	if (is_dynamic_array_type(type_name))
+		return sizeof(struct DynArrTmp);
+
+	if (is_static_array_type(type_name))
+		return get_static_array_size(type_name);
+
+	return get_struct_size(type_name);
+}
+
+static int
+get_array_offset(const char *type_name)
+{
+	int			element_offset;
+	char	   *basic_type = get_array_basic_type(type_name);
+
+	if (!basic_type)
+		return -1;
+
+	element_offset = get_type_offset(basic_type);
+
+	if (element_offset < 0)
+		return -1;
+
+	guc_free(basic_type);
+
+	return element_offset;
+}
+
+static int
+get_struct_offset(const char *type_name)
+{
+	struct type_definition *struct_type = NULL;
+
+	if (!(struct_type = get_type_definition(type_name)))
+		return -1;
+
+	return struct_type->offset;
+}
+
+static int
+get_type_offset(const char *type_name)
+{
+	if (!type_name)
+		return -1;
+
+	/*
+	 * Dynamic array in struct that is 2 fields: pointer, int Therefore offset
+	 * of pointer, int and offset of the pointer are same
+	 */
+	if (is_dynamic_array_type(type_name))
+		return sizeof(void *);
+
+	if (is_static_array_type(type_name))
+		return get_array_offset(type_name);
+
+	return get_struct_offset(type_name);
+}
+
+static char *
+get_struct_field_type(const char *type_name, const char *field)
+{
+	struct type_definition *struct_type = NULL;
+
+	if (!(struct_type = get_type_definition(type_name)))
+		return NULL;
+
+	for (int i = 0; i < struct_type->cnt_fields; i++)
+	{
+		if (strcmp(field, struct_type->fields[i].name) == 0)
+			return guc_strdup(ERROR, struct_type->fields[i].type);
+	}
+
+	return NULL;
+}
+
+char *
+get_field_type_name(const char *type_name, const char *field)
+{
+	if (!type_name || !field)
+		return NULL;
+
+	/*
+	 * if field is "data" or "size", dynamic array is DynArrTmp else dynamic
+	 * array is allocated data
+	 */
+	if (is_dynamic_array_type(type_name))
+	{
+		if (strcmp(field, "size") == 0)
+			return guc_strdup(ERROR, "int");
+
+		if (strcmp(field, "data") == 0)
+			return guc_strdup(ERROR, type_name);
+
+		return get_array_basic_type(type_name);
+	}
+
+	if (is_static_array_type(type_name))
+		return get_array_basic_type(type_name);
+
+	return get_struct_field_type(type_name, field);
+}
+
+int
+get_element_offset(const char *type_name, int index)
+{
+	int			element_size;
+	int			element_offset;
+	char	   *basic_type = get_array_basic_type(type_name);
+
+	if (!basic_type || index < 0)
+		return -1;
+
+	element_offset = get_type_offset(basic_type);
+	element_size = get_composite_size(basic_type);
+	guc_free(basic_type);
+
+	if (element_offset < 0 || element_size < 0)
+		return -1;
+
+	return element_size * index;
+}
+
+static int
+get_element_offset_with_parse_index(const char *type_name, const char *field)
+{
+	char	   *parsed_end = NULL;
+	long		field_idx = -1;
+
+	field_idx = strtol(field, &parsed_end, 10);
+
+	if (errno == ERANGE)
+		return -1;
+
+	while (*parsed_end)
+	{
+		if (!isblank(*parsed_end++))
+			return -1;
+	}
+
+	return get_element_offset(type_name, (int) field_idx);
+}
+
+static int
+get_struct_field_offset(const char *type_name, const char *field_name)
+{
+	struct type_definition *struct_type = NULL;
+
+	if ((struct_type = get_type_definition(type_name)) == NULL)
+		return -1;
+
+	for (int i = 0, total_offset = 0; i < struct_type->cnt_fields; ++i)
+	{
+		int			increment;
+		int			local_off = get_type_offset(struct_type->fields[i].type);
+
+		if (local_off < 0)
+			return -1;
+
+		if (total_offset % local_off != 0)
+			total_offset += local_off - total_offset % local_off;
+
+		if (strcmp(struct_type->fields[i].name, field_name) == 0)
+			return total_offset;
+
+		increment = get_composite_size(struct_type->fields[i].type);
+		total_offset += increment;
+	}
+
+	return -1;
+}
+
+int
+get_field_offset(const char *type_name, const char *field_name)
+{
+	if (!type_name || !field_name)
+		return -1;
+
+	/*
+	 * if field_name is "data" or "size", composite value is a DynArrTmp else
+	 * composite value is allocated data for dynamic array (Attention! This
+	 * function cannot check length of dynamic array)
+	 */
+	if (is_dynamic_array_type(type_name))
+	{
+		if (strcmp(field_name, "data") == 0)
+			return offsetof(struct DynArrTmp, data);
+		else if (strcmp(field_name, "size") == 0)
+			return offsetof(struct DynArrTmp, size);
+		else
+			return get_element_offset_with_parse_index(type_name, field_name);
+	}
+
+	if (is_static_array_type(type_name))
+		return get_element_offset_with_parse_index(type_name, field_name);
+
+	return get_struct_field_offset(type_name, field_name);
+}
+
+void
+init_type_definition(struct type_definition *definition)
+{
+	const char *word_del = " \t\n\v";
+	int			max_offset = 0;
+	int			count_fields = 1;
+	struct_field *fields = NULL;	/* meta about fields */
+	char	   *signature_saveptr;
+	char	   *field_def_saveptr;
+	char	   *signature,
+			   *field_def;
+	char	   *field_def_token;
+	char	   *word_token;
+	char	   *signature_buffer;
+	int			curr_offset = 0;
+	int			i;
+
+	/* count fields in signature */
+	const char *sym = definition->signature;
+
+	if (!sym || !*sym)
+	{
+		ereport(ERROR,
+				errcode(ERRCODE_SYNTAX_ERROR),
+				errmsg("signature of \"%s\" type is empty", definition->type_name));
+
+		return;
+	}
+
+	while (*sym)
+	{
+		if (*sym == STRUCT_FIELDS_DELIMETER)
+			count_fields++;
+
+		sym++;
+	}
+
+	/* allocate structures for field definitions */
+	fields = (struct_field *) guc_malloc(ERROR, count_fields * sizeof(struct_field));
+
+	/* parse signature */
+
+	signature = guc_strdup(ERROR, definition->signature);
+	signature_buffer = signature;
+
+	/* parse sequence of structure field definitions */
+	for (i = 0;; i++, signature = NULL)
+	{
+		int			parsed_word_no = 0;
+
+		field_def_token = strtok_r(signature, STRUCT_FIELDS_DELIMETER_STR, &signature_saveptr);
+
+		if (!field_def_token)
+			break;
+
+		/*
+		 * Parse field definition First word is a type, second is a name of
+		 * field. Definitions are separated with STRUCT_FIELDS_DELIMETER
+		 */
+		for (field_def = field_def_token;; field_def = NULL)
+		{
+			word_token = strtok_r(field_def, word_del, &field_def_saveptr);
+
+			if (!word_token)
+			{
+				if (parsed_word_no != 2)
+				{
+					ereport(ERROR,
+							errcode(ERRCODE_SYNTAX_ERROR),
+							errmsg("wrong field definition: \"%s\" in definition of type \"%s\"",
+								   field_def_token, definition->type_name));
+
+					goto out;
+				}
+
+				break;
+			}
+
+			word_token = guc_strdup(ERROR, word_token);
+
+			/* parse field type */
+			if (parsed_word_no == 0)
+			{
+				int			type_offset = get_type_offset(word_token);
+				int			type_size = get_composite_size(word_token);
+
+				if (type_offset < 0 || type_size < 0)
+				{
+					ereport(ERROR,
+							errcode(ERRCODE_SYNTAX_ERROR),
+							errmsg("wrong type \"%s\"is used in field definition: \"%s\" in definition of type \"%s\"",
+								   word_token, field_def_token, definition->type_name));
+
+					goto out;
+				}
+
+				fields[i].type = word_token;
+
+				/* structure offset = max offset of field offsets */
+				if (type_offset > max_offset)
+					max_offset = type_offset;
+
+				/* field offset in structure % field type offset = 0 */
+				if (curr_offset % type_offset != 0)
+					curr_offset += type_offset - curr_offset % type_offset;
+
+				curr_offset += type_size;
+			}
+			else if (parsed_word_no == 1)	/* parse field name */
+				fields[i].name = word_token;
+			else
+			{
+				ereport(ERROR,
+						errcode(ERRCODE_SYNTAX_ERROR),
+						errmsg("wrong field definition: \"%s\" in definition of type \"%s\"",
+							   field_def_token, definition->type_name));
+
+				goto out;
+			}
+
+			parsed_word_no++;
+		}
+	}
+
+	/* structure size % structure offset = 0 */
+	if (curr_offset % max_offset != 0)
+		curr_offset += max_offset - curr_offset % max_offset;
+
+	definition->offset = max_offset;
+	definition->type_size = curr_offset;
+	definition->cnt_fields = count_fields;
+	definition->fields = fields;
+	fields = NULL;
+	word_token = NULL;
+
+out:
+	guc_free(fields);
+	guc_free(word_token);
+	guc_free(signature_buffer);
+
+	return;
+}
+
+char *
+get_nested_field_type_name(const char *type_name, const char *field_path)
+{
+	char	   *path;
+	char	   *cur_type_name;
+	char	   *cur_field;
+
+	if (!type_name || !field_path)
+		return NULL;
+
+	path = guc_strdup(ERROR, field_path);
+	cur_type_name = guc_strdup(ERROR, type_name);
+	cur_field = tokenize_field_path(path);
+	cur_field = tokenize_field_path(NULL);	/* skip name of structure name */
+
+	/* Follow the path of the field */
+	while (cur_field && cur_type_name)
+	{
+		char	   *next_type = get_field_type_name(cur_type_name, cur_field);
+
+		guc_free(cur_type_name);
+		cur_type_name = next_type;
+
+		cur_field = tokenize_field_path(NULL);
+	}
+
+	guc_free(path);
+
+	return cur_type_name;
+}
+
+void *
+get_nested_field_ptr(const void *composite_start, const char *type_name, const char *field_path)
+{
+	char	   *path;
+	char	   *cur_type_name;
+	char	   *cur_field;
+	char	   *cur_ptr;
+
+	if (!composite_start || !field_path || !type_name)
+		return NULL;
+
+	path = guc_strdup(ERROR, field_path);
+	cur_type_name = guc_strdup(ERROR, type_name);
+
+	cur_field = tokenize_field_path(path);
+	cur_field = tokenize_field_path(NULL);	/* skip name of structure */
+	cur_ptr = (char *) composite_start;
+
+	while (cur_field && cur_type_name)
+	{
+		char	   *next_type;
+		int			local_offset;
+
+		/* go to memory of dynamic array */
+		if (element_of_data_of_dynamic_array(cur_type_name, cur_field))
+			cur_ptr = *((char **) cur_ptr);
+
+		local_offset = get_field_offset(cur_type_name, cur_field);
+
+		if (local_offset < 0)
+		{
+			cur_ptr = NULL;
+			break;
+		}
+
+		cur_ptr += local_offset;
+		next_type = get_field_type_name(cur_type_name, cur_field);
+
+		guc_free(cur_type_name);
+		cur_type_name = next_type;
+		cur_field = tokenize_field_path(NULL);
+	}
+
+	guc_free(path);
+	guc_free(cur_type_name);
+
+	return cur_ptr;
+}
+
+static char *
+print_empty_array(bool writing_to_file, bool extend)
+{
+	if (extend)
+	{
+		if (writing_to_file)
+			return guc_strdup(ERROR, "{size: 0, data: []}");
+		else
+			return guc_strdup(ERROR, "{\n\tsize: 0,\n\tdata: []\n}");
+	}
+	else
+		return guc_strdup(ERROR, "[]");
+}
+
+static char *
+array_to_str(const void *data, int size, const char *type, bool writing_to_file, bool extend)
+{
+	const char *tab_prefix = extend ? "\t\t" : "\t";
+	StringInfoData buf;
+	char	   *result = NULL;
+	char	   *element_type;
+
+	/* process empty array */
+	if (!size)
+		print_empty_array(writing_to_file, extend);
+
+	initStringInfo(&buf);
+
+	element_type = get_array_basic_type(type);
+
+	/* write prefix */
+	if (extend)
+	{
+		if (writing_to_file)
+			appendStringInfo(&buf, "{size: %d, data: [", size);
+		else
+			appendStringInfo(&buf, "{\n\tsize: %d,\n\tdata: [\n", size);
+	}
+	else
+	{
+		if (writing_to_file)
+			appendStringInfo(&buf, "[");
+		else
+			appendStringInfo(&buf, "[\n");
+	}
+
+	/* recursive call for each element of array */
+	for (int i = 0; i < size; i++)
+	{
+		char	   *element;
+		int			offset = get_element_offset(type, i);
+
+		if (offset < 0)
+			goto out;
+
+		element = composite_to_str((char *) data + offset, element_type, writing_to_file);
+
+		if (!element)
+			goto out;
+
+		if (writing_to_file)
+		{
+			appendStringInfo(&buf, "%s", element);
+
+			if (i < size - 1)
+				appendStringInfo(&buf, ", ");
+		}
+		else
+		{
+			/*
+			 * if not writing to file, add tab_prefix at the beginning of each
+			 * line
+			 */
+			char	   *str_saveptr;
+			char	   *str_begin = strtok_r(element, "\n", &str_saveptr);
+
+			appendStringInfo(&buf, "%s%s", tab_prefix, str_begin);
+
+			while ((str_begin = strtok_r(NULL, "\n", &str_saveptr)) != NULL)
+				appendStringInfo(&buf, "\n%s%s", tab_prefix, str_begin);
+
+			if (i < size - 1)
+				appendStringInfo(&buf, ",\n");
+			else
+				appendStringInfo(&buf, "\n");
+		}
+
+		guc_free(element);
+	}
+
+	/* write suffix */
+	if (extend)
+	{
+		if (writing_to_file)
+			appendStringInfo(&buf, "]}");
+		else
+			appendStringInfo(&buf, "\t]\n}");
+	}
+	else
+		appendStringInfo(&buf, "]");
+
+	result = guc_strdup(ERROR, buf.data);
+
+out:
+	pfree(buf.data);
+	guc_free(element_type);
+
+	return result;
+}
+
+static char *
+static_array_to_str(const void *structp, const char *type, bool writing_to_file)
+{
+	int			array_size = get_static_array_length(type);
+
+	return array_to_str(structp, array_size, type, writing_to_file, false);
+}
+
+static char *
+dynamic_array_to_str(const void *structp, const char *type, bool writing_to_file)
+{
+	int			array_size = dynamic_array_size(structp);
+
+	/* extended_array_view - global variable (GUC) */
+	return array_to_str(*(void **) structp,
+						array_size,
+						type,
+						writing_to_file,
+						extended_array_view);
+}
+
+static char *
+scalar_to_str(const void *structp, const char *type_name, bool writing_to_file)
+{
+	char	   *buf;
+	char	   *quoted;
+
+	if (strcmp(type_name, "bool") == 0)
+	{
+		int			maxlen = 6;
+
+		buf = (char *) guc_malloc(ERROR, maxlen);
+
+		if (*(bool *) structp)
+			snprintf(buf, maxlen, "%s", "true");
+		else
+			snprintf(buf, maxlen, "%s", "false");
+	}
+	else if (strcmp(type_name, "int") == 0)
+	{
+		int			maxlen = 12;
+
+		buf = (char *) guc_malloc(ERROR, maxlen);
+		snprintf(buf, maxlen, "%d", *(int *) structp);
+	}
+	else if (strcmp(type_name, "real") == 0)
+	{
+		int			maxlen = DBL_MAX_10_EXP + 3;
+
+		buf = (char *) guc_malloc(ERROR, maxlen);
+		snprintf(buf, maxlen, "%lf", *(double *) structp);
+	}
+	else if (strcmp(type_name, "string") == 0)
+	{
+		if (*(char **) structp == NULL)
+			buf = guc_strdup(ERROR, "nil");
+		else
+		{
+			/* escape quotes only if writing to file */
+			if (writing_to_file)
+			{
+				char	   *escaped = escape_single_quotes_ascii(*(char **) structp);
+
+				buf = guc_strdup(ERROR, escaped);
+				free(escaped);
+			}
+			else
+				buf = guc_strdup(ERROR, *(char **) structp);
+		}
+	}
+	else
+		return NULL;
+
+	/*
+	 * add apostrophes: If write to file, add apostrophes for each type Else
+	 * add apostrophes only for strings
+	 */
+	if (writing_to_file || (strcmp(type_name, "string") == 0 && strcmp(buf, "nil") != 0))
+	{
+		int			maxlen = strlen(buf) + 3;
+
+		quoted = (char *) guc_malloc(ERROR, maxlen * sizeof(char));
+		snprintf(quoted, maxlen, "\'%s\'", buf);
+		guc_free(buf);
+	}
+	else
+		quoted = buf;
+
+	return quoted;
+}
+
+bool
+is_scalar_type(const char *type_name)
+{
+	if (strcmp(type_name, "bool") == 0 ||
+		strcmp(type_name, "int") == 0 ||
+		strcmp(type_name, "real") == 0 ||
+		strcmp(type_name, "string") == 0)
+		return true;
+
+	return false;
+}
+
+static char *
+struct_to_str(const void *structp, const char *type, bool writing_to_file)
+{
+	struct type_definition *definition;
+	StringInfoData buf;
+	int			cnt_fields;
+	char	   *result = 0;
+
+	initStringInfo(&buf);
+
+	/* check built-in types */
+	if (is_scalar_type(type))
+		return scalar_to_str(structp, type, writing_to_file);
+
+	/* standard algorithm of preparing structure for writing to file */
+	definition = NULL;
+	if ((definition = get_type_definition(type)) == NULL)
+		return NULL;
+
+	cnt_fields = definition->cnt_fields;
+
+	/* print prefix */
+	appendStringInfo(&buf, "{");
+
+	if (!writing_to_file)
+		appendStringInfo(&buf, "\n");
+
+	/* recurse call for fields */
+	for (int i = 0; i < cnt_fields; i++)
+	{
+		char	   *field;
+		void	   *sptr;
+		int			offset = get_field_offset(definition->type_name,
+											  definition->fields[i].name);
+
+		if (offset < 0)
+			goto out;
+
+		sptr = (char *) structp + offset;
+		field = composite_to_str(sptr, definition->fields[i].type, writing_to_file);
+
+		if (!field)
+			goto out;
+
+		if (writing_to_file)
+		{
+			appendStringInfo(&buf, "%s: %s", definition->fields[i].name, field);
+
+			if (i < cnt_fields - 1)
+				appendStringInfo(&buf, ", ");
+		}
+		else
+		{
+			/* if not write ro file, add tabs at the beginning of each line */
+			char	   *str_saveptr;
+			char	   *str_begin = strtok_r(field, "\n", &str_saveptr);
+
+			appendStringInfo(&buf, "\t%s: %s", definition->fields[i].name, str_begin);
+
+			while ((str_begin = strtok_r(NULL, "\n", &str_saveptr)) != NULL)
+				appendStringInfo(&buf, "\n\t%s", str_begin);
+
+			if (i < cnt_fields - 1)
+				appendStringInfo(&buf, ",\n");
+			else
+				appendStringInfo(&buf, "\n");
+		}
+
+		guc_free(field);
+	}
+
+	/* print suffix */
+	appendStringInfo(&buf, "}");
+	result = guc_strdup(ERROR, buf.data);
+
+out:
+	pfree(buf.data);
+
+	return result;
+}
+
+char *
+composite_to_str(const void *structp, const char *type, bool writing_to_file)
+{
+	if (is_static_array_type(type))
+		return static_array_to_str(structp, type, writing_to_file);
+
+	if (is_dynamic_array_type(type))
+		return dynamic_array_to_str(structp, type, writing_to_file);
+
+	return struct_to_str(structp, type, writing_to_file);
+}
+
+char *
+normalize_composite_value(const char *option_name, const char *value)
+{
+	/*
+	 * Composite value couldn't be wrapped in quotes scalar types must be
+	 * escaped and wrapped in quotes All names related to composite values
+	 * ended with "->"
+	 */
+	bool		is_composite = suffix_is_arrow(option_name);
+	char	   *prepared_val;
+	char	   *str_val;
+
+	/*
+	 * Each value that goes throw this function went throw parser before. If
+	 * value is scalar, it was deescaped, else (if value is composite) it
+	 * wasn't. Function parse_composite always deescapes scalar values.
+	 * Therefore we must escape scalar values for parse_composite
+	 */
+	if (!is_composite)
+	{
+		char	   *escaped = escape_single_quotes_ascii(value);
+		int			len = strlen(escaped) + 3;
+
+		/* escape */
+		prepared_val = guc_malloc(ERROR, len);
+		snprintf(prepared_val, len, "\'%s\'", escaped);
+
+		free(escaped);
+	}
+	else
+		prepared_val = (char *) value;	/* be careful with free */
+
+	str_val = convert_path_to_composite_value(option_name, prepared_val);
+
+	if (prepared_val != value)
+		guc_free(prepared_val);
+
+	return str_val;
+}
+
+static Size
+get_len_serialized_array(const void *structp, const char *type)
+{
+	char	   *element_type = get_array_basic_type(type);
+	int			total_size = 3;
+	void	   *datap = NULL;
+	int			array_size = 0;
+
+	if (is_dynamic_array_type(type))
+	{
+		array_size = dynamic_array_size(structp);
+		datap = *((void **) structp);
+	}
+	else
+	{
+		array_size = get_static_array_length(type);
+		datap = (void *) structp;
+	}
+
+	/* compute length for first element */
+	for (int i = 0; i < array_size; i++)
+	{
+		int			offset = get_element_offset(type, i);
+		int			element_len = get_length_composite_str((char *) datap + offset, element_type);
+
+		total_size += element_len + 2;	/* 2 = len(", ") */
+	}
+
+	guc_free(element_type);
+
+	return total_size;
+}
+
+static Size
+get_len_serialized_struct(const void *structp, const char *type)
+{
+	struct type_definition *definition = NULL;
+	int			total_size = 3;
+
+	if (strcmp(type, "bool") == 0)
+		return 6;
+	else if (strcmp(type, "int") == 0)
+	{
+		if (*(int *) structp < 100)
+			return 4;
+
+		return 12;
+	}
+	else if (strcmp(type, "real") == 0)
+		return 1 + 1 + 1 + REALTYPE_PRECISION + 5;
+	else if (strcmp(type, "string") == 0)
+	{
+		if (*(char **) structp)
+			return strlen(*(char **) structp);
+
+		return 5;				/* len of "\nil" */
+	}
+
+	if ((definition = get_type_definition(type)) == NULL)
+		return 0;
+
+	for (int i = 0; i < definition->cnt_fields; i++)
+	{
+		int			offset = get_field_offset(definition->type_name, definition->fields[i].name);
+		int			field_len = get_length_composite_str((char *) structp + offset, definition->fields[i].type);
+
+		total_size += field_len + 2;
+	}
+
+	return total_size;
+}
+
+Size
+get_length_composite_str(const void *structp, const char *type_name)
+{
+	if (is_static_array_type(type_name) || is_dynamic_array_type(type_name))
+		return get_len_serialized_array(structp, type_name);
+
+	return get_len_serialized_struct(structp, type_name);
+}
+
+char *
+convert_path_to_composite_value(const char *field_path, const char *value)
+{
+	char	   *path = guc_strdup(ERROR, field_path);
+	char	   *cur_field = tokenize_field_path(path);
+	char	   *prefix = guc_strdup(ERROR, "");
+	char	   *suffix = guc_strdup(ERROR, "");
+	char	   *result;
+	int			len;
+
+	/* skip guc name */
+	cur_field = tokenize_field_path(NULL);
+
+	/* for each step in path generate derived braces and name of field */
+	while (cur_field)
+	{
+		int			prefix_len = strlen(prefix);
+		int			suffix_len = strlen(suffix);
+
+		int			prefix_diff_len = 3 + strlen(cur_field) + 1;	/* 3 for "[: ", 1 for
+																	 * '\0' */
+		int			suffix_diff_len = 2;
+
+		char	   *next_prefix = guc_malloc(ERROR, prefix_len + prefix_diff_len);
+		char	   *next_suffix = guc_malloc(ERROR, suffix_len + suffix_diff_len);
+
+		snprintf(next_prefix, prefix_len + 1, "%s", prefix);
+		/* define array or structure */
+		if (isdigit(cur_field[0]))
+		{
+			snprintf(next_prefix + prefix_len, 2, "[");
+			snprintf(next_suffix, 2, "]");
+		}
+		else
+		{
+			snprintf(next_prefix + prefix_len, 2, "{");
+			snprintf(next_suffix, 2, "}");
+		}
+
+		snprintf(next_prefix + prefix_len + 1, prefix_diff_len - 1, "%s: ", cur_field);
+		snprintf(next_suffix + 1, suffix_len + 1, "%s", suffix);
+
+		guc_free(prefix);
+		guc_free(suffix);
+
+		prefix = next_prefix;
+		suffix = next_suffix;
+
+		cur_field = tokenize_field_path(NULL);
+	}
+
+	/* construct result from prefix, suffix and value */
+	len = strlen(prefix) + strlen(value) + strlen(suffix) + 1;
+	result = guc_malloc(ERROR, len);
+	snprintf(result, len, "%s%s%s", prefix, value, suffix);
+
+	guc_free(prefix);
+	guc_free(suffix);
+
+	return result;
+}
+
+static void
+static_array_dup(void *dest_struct, const void *src_struct, const char *type_name)
+{
+	const char *basic_type = get_array_basic_type(type_name);
+	int			arr_size = get_static_array_length(type_name);
+
+	/* recursive duplicate array elements */
+	for (int i = 0; i < arr_size; i++)
+	{
+		int			offset = get_element_offset(type_name, i);
+		void	   *dest_ptr = (char *) dest_struct + offset;
+		void	   *src_ptr = (char *) src_struct + offset;
+
+		composite_dup_impl(dest_ptr, src_ptr, basic_type);
+	}
+}
+
+/*
+ * Beware! src_struct points to structure like DynArrTmp
+ */
+static void
+dynamic_array_dup(void *dest_struct, const void *src_struct, const char *type)
+{
+	void	   *datap;
+	void	  **dstpp;
+	void	   *dstp;
+	const char *basic_type = get_array_basic_type(type);
+	int			arr_mem_size = get_dynamic_array_size(type, src_struct);
+	int			arr_size = dynamic_array_size(src_struct);
+
+	if (!arr_size)
+	{
+		*(void **) dest_struct = NULL;
+		*((void **) dest_struct + 1) = NULL;
+
+		return;
+	}
+
+	datap = *((void **) src_struct);
+	dstpp = (void **) dest_struct;
+	*dstpp = guc_malloc(ERROR, arr_mem_size * sizeof(char));
+	dstp = *dstpp;
+
+	for (int i = 0; i < arr_size; i++)
+	{
+		int			offset = get_element_offset(type, i);
+		void	   *dest_ptr = (char *) dstp + offset;
+		void	   *src_ptr = (char *) datap + offset;
+
+		composite_dup_impl(dest_ptr, src_ptr, basic_type);
+	}
+
+	dynamic_array_size(dest_struct) = arr_size;
+}
+
+static void
+struct_dup(void *dest_struct, const void *src_struct, const char *type_name)
+{
+	struct type_definition *struct_type = NULL;
+
+	if (!(struct_type = get_type_definition(type_name)))
+		return;
+
+	if (is_scalar_type(type_name))
+	{
+		if (!strcmp(type_name, "string"))
+		{
+			if (*(char **) src_struct)
+				*(char **) dest_struct = guc_strdup(ERROR, *(char **) src_struct);
+			else
+				*(char **) dest_struct = NULL;
+
+			return;
+		}
+
+		memcpy(dest_struct, src_struct, struct_type->type_size);
+
+		return;
+	}
+
+	for (int i = 0; i < struct_type->cnt_fields; i++)
+	{
+		const char *field_name = struct_type->fields[i].name;
+		const char *field_type = struct_type->fields[i].type;
+		int			field_offset = get_field_offset(type_name, field_name);
+
+		void	   *dest_ptr = (char *) dest_struct + field_offset;
+		void	   *src_ptr = (char *) src_struct + field_offset;
+
+		composite_dup_impl(dest_ptr, src_ptr, field_type);
+	}
+}
+
+void
+composite_dup_impl(void *dest_struct, const void *src_struct, const char *type_name)
+{
+	if (is_static_array_type(type_name))
+		return static_array_dup(dest_struct, src_struct, type_name);
+	if (is_dynamic_array_type(type_name))
+		return dynamic_array_dup(dest_struct, src_struct, type_name);
+
+	return struct_dup(dest_struct, src_struct, type_name);
+}
+
+void *
+composite_dup(const void *structp, const char *type_name)
+{
+	int			struct_size;
+	void	   *duplicate;
+
+	if (!structp)
+		return NULL;
+
+	struct_size = get_composite_size(type_name);
+	duplicate = guc_malloc(ERROR, struct_size);
+
+	/* recursive bypass and searching string */
+	composite_dup_impl(duplicate, structp, type_name);
+
+	return duplicate;
+}
+
+static int
+array_data_cmp(const void *first, const void *second, const char *type, int size)
+{
+	const char *base_type = get_array_basic_type(type);
+	int			base_type_size = get_composite_size(base_type);
+	int			res = 0;
+
+	/* recursive compare each element */
+	for (int i = 0; i < size; i++)
+	{
+		int			offset = base_type_size * i;
+		void	   *first_element = (char *) first + offset;
+		void	   *second_element = (char *) second + offset;
+
+		res = composite_cmp(first_element, second_element, base_type);
+
+		if (res)
+			break;
+	}
+
+	return res;
+}
+
+static int
+dynamic_array_cmp(const void *first, const void *second, const char *type)
+{
+	void	   *first_data = *((void **) first);
+	void	   *second_data = *((void **) second);
+
+	int			first_size = dynamic_array_size(first);
+	int			second_size = dynamic_array_size(second);
+
+	int			cmp = 0;
+
+	if ((cmp = first_size - second_size))
+		return cmp;
+
+	return array_data_cmp(first_data, second_data, type, first_size);
+}
+
+static int
+struct_cmp(const void *first, const void *second, const char *type_name)
+{
+	int			res;
+
+	/* check type */
+	struct type_definition *struct_type = NULL;
+
+	if (!(struct_type = get_type_definition(type_name)))
+		return 2;				/* error code */
+
+	/* check scalar types like int, real, etc */
+	if (struct_type->cnt_fields == 0)
+	{
+		/* compare string with strcmp, not pointers! */
+		res = 0;
+
+		if (strcmp(type_name, "string") == 0)
+		{
+			if (!*(char **) first && !*(char **) second)
+				return 0;
+
+			if (!*(char **) first)
+				return -1;
+
+			if (!*(char **) second)
+				return 1;
+
+			res = strcmp(*(char **) first, *(char **) second);
+		}
+		else if (strcmp(type_name, "bool") == 0)
+			res = *(bool *) first - *(bool *) second;
+		else if (strcmp(type_name, "int") == 0)
+			res = *(int *) first - *(int *) second;
+		else if (strcmp(type_name, "real") == 0)
+		{
+			double		res = *(double *) first - *(double *) second;
+
+			if (res == 0)
+				return 0;
+
+			if (res > 0)
+				return 1;
+
+			return -1;
+		}
+		else
+			return 2;
+
+		if (res == 0)
+			return 0;
+
+		if (res > 0)
+			return 1;
+
+		return -1;
+	}
+
+	/* recursive comparison of fields */
+	res = 0;
+
+	for (int i = 0; i < struct_type->cnt_fields; i++)
+	{
+		const char *field_name = struct_type->fields[i].name;
+		const char *field_type = struct_type->fields[i].type;
+		int			field_offset = get_field_offset(type_name, field_name);
+
+		void	   *first_field = (char *) first + field_offset;
+		void	   *second_field = (char *) second + field_offset;
+
+		res = composite_cmp(first_field, second_field, field_type);
+
+		if (res)
+			break;
+	}
+
+	return res;
+}
+
+int
+composite_cmp(const void *first, const void *second, const char *type_name)
+{
+	if (is_static_array_type(type_name))
+		return array_data_cmp(first, second, type_name, get_static_array_length(type_name));
+
+	if (is_dynamic_array_type(type_name))
+		return dynamic_array_cmp(first, second, type_name);
+
+	return struct_cmp(first, second, type_name);
+}
+
+static void
+free_static_array(void *delptr, const char *type)
+{
+	const char *base_type = get_array_basic_type(type);
+	int			arr_size = get_static_array_length(type);
+
+	for (int i = 0; i < arr_size; i++)
+	{
+		void	   *element_ptr = (char *) delptr + get_element_offset(type, i);
+
+		free_composite_impl(element_ptr, base_type);
+	}
+}
+
+static void
+free_dynamic_array(void *delptr, const char *type)
+{
+	const char *base_type = get_array_basic_type(type);
+	int			arr_size = get_static_array_length(type);
+	void	  **datapp = NULL;
+
+	for (int i = 0; i < arr_size; i++)
+	{
+		void	   *element_ptr = (char *) delptr + get_element_offset(type, i);
+
+		free_composite_impl(element_ptr, base_type);
+	}
+
+	datapp = (void **) delptr;
+	guc_free(*datapp);
+	*datapp = NULL;
+}
+
+static void
+free_struct(void *delptr, const char *type)
+{
+	struct type_definition *struct_type = NULL;
+
+	if ((struct_type = get_type_definition(type)) == NULL)
+		return;
+
+	if (struct_type->cnt_fields == 0)
+	{
+		if (strcmp(type, "string") == 0)
+		{
+			char	  **strp = (char **) delptr;
+
+			guc_free(*strp);
+			*strp = NULL;
+		}
+
+		return;
+	}
+
+	for (int i = 0; i < struct_type->cnt_fields; i++)
+	{
+		const char *field_name = struct_type->fields[i].name;
+		const char *field_type = struct_type->fields[i].type;
+		int			field_offset = get_field_offset(type, field_name);
+
+		free_composite_impl((char *) delptr + field_offset, field_type);
+	}
+}
+
+void
+free_composite_impl(void *delptr, const char *type_name)
+{
+	if (is_static_array_type(type_name))
+		free_static_array(delptr, type_name);
+
+	if (is_dynamic_array_type(type_name))
+		free_dynamic_array(delptr, type_name);
+
+	free_struct(delptr, type_name);
+}
+
+void
+free_composite(void *delptr, const char *type_name)
+{
+	free_composite_impl(delptr, type_name);
+	guc_free(delptr);
+}
diff --git a/src/backend/utils/misc/guc_composite.h b/src/backend/utils/misc/guc_composite.h
new file mode 100644
index 0000000000..c0ae570438
--- /dev/null
+++ b/src/backend/utils/misc/guc_composite.h
@@ -0,0 +1,84 @@
+/*--------------------------------------------------------------------
+ * guc_composite.h
+ *
+ * Declarations shared between backend/utils/misc/guc.c and
+ * backend/utils/misc/guc_composite.c
+ *
+ * Copyright (c) 2000-2025, PostgreSQL Global Development Group
+ *
+ * src/backend/utils/misc/guc_composite.h
+ *--------------------------------------------------------------------
+ */
+#ifndef GUC_COMPOSITE_H
+#define GUC_COMPOSITE_H
+
+#include "utils/guc.h"
+#include "utils/guc_tables.h"
+#include "utils/hsearch.h"
+
+typedef struct
+{
+	const char *type_name;
+	struct type_definition *definition;
+}			OptionTypeHashEntry;
+
+#define IS_STATUS_OK(val) (val.status == PARSER_OK)
+#define IS_STATUS_FAIL(val) (val.status == PARSER_FAIL)
+#define IS_STATUS_ERR(val) (val.status == PARSER_ERR)
+#define IS_STATUS_NOT_FOUND(val) (val.status == PARSER_NOT_FOUND)
+
+/*
+ * Get size in dynamic array. It places after pointer to data
+ */
+#define dynamic_array_size(ptr) (*(int *)((char *)ptr + sizeof(void *)))
+
+#define suffix_is_arrow(name) (name[strlen(name) - 2] == '-' && name[strlen(name) - 1] == '>')
+
+/*
+ * Tokenized path to nest structures. It replaces '->' to '\0' and
+ * returns pointer to first member name.
+ */
+#define tokenize_field_path(path) strtok(path, "->[]")
+
+extern HTAB *guc_types_hashtab;
+
+extern Size get_length_composite_str(const void *structp, const char *type_name);
+extern void init_type_definition(struct type_definition *definition);
+extern struct type_definition *get_type_definition(const char *type_name);
+extern bool is_static_array_type(const char *type_name);
+extern bool is_dynamic_array_type(const char *type_name);
+extern int	get_static_array_length(const char *type_name);
+extern int	get_array_size(const char *type_name, const int length);
+extern int	get_composite_size(const char *type_name);
+extern void composite_dup_impl(void *dest_struct, const void *src_struct, const char *type_name);
+extern void *composite_dup(const void *structp, const char *type_name);
+extern int	composite_cmp(const void *first, const void *second, const char *type_name);
+extern char *get_field_type_name(const char *type_name, const char *field);
+extern char *get_nested_field_type_name(const char *type_name, const char *field_path);
+extern int	get_field_offset(const char *type_name, const char *field_name);
+extern int	get_element_offset(const char *type_name, int index);
+extern void free_composite_impl(void *delptr, const char *type_name);
+extern void free_composite(void *delptr, const char *type_name);
+extern void *get_nested_field_ptr(const void *composite_start, const char *type_name, const char *field_path);
+extern char *normalize_composite_value(const char *option_name, const char *value);
+extern bool parse_composite(const char *strvalue, const char *type, void **result, const void *prev_val, int flags, const char **hintmsg);
+extern char *convert_path_to_composite_value(const char *field_path, const char *value);
+extern char *get_array_basic_type(const char *array_type_name);
+extern bool is_scalar_type(const char *type_name);
+
+/*
+ * Internal functions for parsing guc_composite grammar,
+ * in guc_composite_gram.y and guc_composite_scan.l
+ */
+union YYSTYPE;
+#ifndef YY_TYPEDEF_YY_SCANNER_T
+#define YY_TYPEDEF_YY_SCANNER_T
+typedef void *yyscan_t;
+#endif
+extern int	guc_composite_yyparse(void *composite_ptr, const char *composite_type, const char **hintmsg, int flags, yyscan_t yyscanner);
+extern void guc_composite_yyerror(void *composite_ptr, const char *composite_type, const char **hintmsg, int flags, yyscan_t yyscanner, const char *message);
+extern int	guc_composite_yylex(union YYSTYPE *yylval_param, const char **hintmsg, yyscan_t yyscanner);
+extern void guc_composite_scanner_init(const char *str, yyscan_t *yyscannerp);
+extern void guc_composite_scanner_finish(yyscan_t yyscanner);
+
+#endif							/* GUC_COMPOSITE_H */
diff --git a/src/backend/utils/misc/guc_composite_gram.y b/src/backend/utils/misc/guc_composite_gram.y
new file mode 100644
index 0000000000..c9a2f52e55
--- /dev/null
+++ b/src/backend/utils/misc/guc_composite_gram.y
@@ -0,0 +1,787 @@
+%{
+/*-------------------------------------------------------------------------
+ *
+ * guc_composite_gram.y				- Parser for all composite guc options
+ *
+ * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *	  src/backend/utils/misc/guc_composite_gram.y
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "nodes/pg_list.h"
+#include "utils/guc.h"
+#include "utils/builtins.h"
+#include "guc_composite.h"
+#include "guc_composite_gram.h"
+#include <string.h>
+#include <stdlib.h>
+
+/*
+ * Bison doesn't allocate anything that needs to live across parser calls,
+ * so we can easily have it use palloc instead of malloc.  This prevents
+ * memory leaks if we error out during parsing.
+ */
+#define YYMALLOC palloc
+#define YYFREE   pfree
+
+extern int	guc_composite_yychar;
+extern int	guc_composite_yynerrs;
+
+#define context(num)    ((parser_ctx *)list_nth(contexts, num))
+#define list_empty(l)   (list_length(l) == 0)
+#define last_context    (*(parser_ctx **)list_tail(contexts))
+
+#define check_error()	do { \
+		if(*hintmsg) \
+			return 1; \
+	} while(0)
+
+/* Stack is needed to memoize valuable data between nested layers in composite object */
+enum name_usage
+{
+	NAME_USAGE_UNKNOWN,
+	NAME_USAGE_ALWAYS,
+	NAME_USAGE_NEVER
+};
+
+/* Stages of setting size for dynamic arrays */
+enum fixed_size
+{
+	FIXED_SIZE_IS_NOT_SETTED,	/* Size was not defined for dynamic array */
+	FIXED_SIZE_IS_BEING_SETTED, /* Current value must be setted as a length of
+								 * dynamic array */
+	FIXED_SIZE_IS_SETTED		/* Indexes in array must be less than size */
+};
+
+typedef struct parser_ctx
+{
+	char	   *type;			/* type of composite object */
+	void	   *start;			/* pointer to start of composite object */
+	int			idx;			/* automatic computed index for current child */
+	bool		has_name;		/* name (index) was parsed before current
+								 * composite object */
+	bool		extended;		/* flag for dynamic arrays. True = extended
+								 * text representation */
+	enum fixed_size fixed_size; /* field for outer context of dynamic array.
+								 * see comments for enum */
+	int			max_idx;		/* field for outer context of dynamic array.
+								 * sets value of max idx in data */
+	enum name_usage name_usage; /* names (indexes) are used for children of
+								 * composite object */
+}			parser_ctx;
+
+static List *contexts = NIL;	/* stack of contexts */
+
+static void init_context(const char *type, void *ptr);
+static void push_context(const char *type, void *start);
+static void free_context(parser_ctx * context);
+static void free_context_list(void);
+static void check_name(yyscan_t yyscanner, const char **hintmsg);
+static void reallocate_dynamic_array(const char *array_type, void *array, int new_len);
+static void check_memory(yyscan_t yyscanner, const char **hintmsg);
+static void prepare_value_context(yyscan_t yyscanner, const char **hintmsg);
+static void prepare_structure(void);
+static void prepare_array(void);
+static void parse_composite_end(void);
+static void parse_field_end(void);
+static int	parse_index(const char *index, yyscan_t yyscanner, const char **hintmsg);
+static void parse_element(const char *index, yyscan_t yyscanner, const char **hintmsg);
+static void parse_field(const char *name, yyscan_t yyscanner, const char **hintmsg);
+static void parse_name(const char *name, yyscan_t yyscanner, const char **hintmsg);
+static void parse_scalar_opt(char *strval, const char *struct_type, void *result, int flags, yyscan_t yyscanner, const char **hintmsg);
+%}
+
+%parse-param {void *composite_ptr}
+%parse-param {const char *composite_type}
+%parse-param {const char **hintmsg}
+%parse-param {int flags}
+%parse-param {yyscan_t yyscanner}
+%lex-param   {const char **hintmsg}
+%lex-param   {yyscan_t yyscanner}
+%pure-parser
+%expect 0
+%name-prefix="guc_composite_yy"
+
+%union
+{
+	char	   *str;
+}
+
+%token <str> IDENT JUNK
+
+%type placeholder_patch_list
+%type composite
+%type list_or_empty
+%type list
+%type item
+
+%start placeholder_patch_list
+
+%initial-action
+{
+	init_context(composite_type, composite_ptr);
+	(void) yynerrs;
+}
+%%
+
+placeholder_patch_list:
+	composite ';'							{
+												init_context(composite_type, composite_ptr);
+												check_error();
+											}
+	placeholder_patch_list
+	| composite
+
+composite:
+	'{'										{
+												prepare_value_context(yyscanner, hintmsg);
+												prepare_structure();
+												check_error();
+											}
+	list_or_empty							{
+												parse_composite_end();
+												check_error();
+											}
+	'}'
+	| '['									{
+												prepare_value_context(yyscanner, hintmsg);
+												check_error();
+												prepare_array();
+												check_error();
+											}
+	list_or_empty							{
+												parse_composite_end();
+												check_error();
+											}
+	']'
+	| IDENT									{
+												prepare_value_context(yyscanner, hintmsg);
+												check_error();
+												parse_scalar_opt($1, context(0)->type, context(0)->start, flags, yyscanner, hintmsg);
+												check_error();
+												parse_composite_end();
+												check_error();
+											}
+	;
+
+list_or_empty:
+	list
+	| %empty
+	;
+
+list:
+	item									{
+												parse_field_end();
+												check_error();
+											}
+	',' list
+	| item
+	;
+
+item:
+	IDENT ':'								{
+												parse_name($1, yyscanner, hintmsg);
+												check_error();
+											}
+	composite
+	| composite
+	;
+%%
+
+
+static void
+init_context(const char *type, void *ptr)
+{
+	free_context_list();
+	push_context(type, ptr);
+}
+
+static void
+push_context(const char *type, void *start)
+{
+	parser_ctx *ctx = palloc(sizeof(parser_ctx));
+
+	if (type)
+		ctx->type = pstrdup(type);
+	else
+		ctx->type = NULL;
+
+	ctx->start = start;
+	ctx->idx = 0;
+	ctx->has_name = false;
+	ctx->extended = false;
+	ctx->fixed_size = FIXED_SIZE_IS_NOT_SETTED;
+	ctx->max_idx = -1;
+	ctx->name_usage = NAME_USAGE_UNKNOWN;
+
+	contexts = lcons(ctx, contexts);
+}
+
+static void
+free_context(parser_ctx * context)
+{
+	if (context->type)
+		pfree(context->type);
+
+	if (context)
+		pfree(context);
+}
+
+static void
+free_context_list(void)
+{
+	while (!list_empty(contexts))
+	{
+		free_context((parser_ctx *) list_nth(contexts, 0));
+		contexts = list_delete_first(contexts);
+	}
+
+	list_free(contexts);
+}
+
+static void
+check_name(yyscan_t yyscanner, const char **hintmsg)
+{
+	/* Indexes in array are exist either for each element or for no one */
+	if (is_static_array_type(context(1)->type) ||
+		(is_dynamic_array_type(context(1)->type) && context(1)->extended != true))
+	{
+		if (context(0)->has_name)
+		{
+			if (context(1)->name_usage == NAME_USAGE_NEVER)
+			{
+				guc_composite_yyerror(last_context->start,
+									  last_context->type,
+									  hintmsg,
+									  0,
+									  yyscanner,
+									  "use indexes for either everyone or no one");
+				return;
+			}
+
+			context(1)->name_usage = NAME_USAGE_ALWAYS;
+		}
+		else
+		{
+			if (context(1)->name_usage == NAME_USAGE_ALWAYS)
+			{
+				guc_composite_yyerror(last_context->start,
+									  last_context->type,
+									  hintmsg,
+									  0,
+									  yyscanner,
+									  "use indexes for either everyone or no one");
+				return;
+			}
+
+			context(1)->name_usage = NAME_USAGE_NEVER;
+
+			if (context(1)->start)
+				context(0)->start = (char *) context(1)->start + get_element_offset(context(1)->type, context(1)->idx);
+		}
+	}
+	else if (!context(0)->has_name) /* fields of structures must be labeled
+									 * always */
+		{
+			guc_composite_yyerror(last_context->start,
+							  last_context->type,
+							  hintmsg,
+							  0,
+							  yyscanner,
+							  "all fields in structure must be labeled");
+			return;
+		}
+}
+
+static void
+reallocate_dynamic_array(const char *array_type, void *array, int new_len)
+{
+	int			new_arr_mem_size = get_array_size(array_type, new_len);
+	int			last_len = dynamic_array_size(array);
+	int			last_arr_mem_size = get_array_size(array_type, last_len);
+	int			min_arr_mem_size = new_arr_mem_size < last_arr_mem_size ? new_arr_mem_size : last_arr_mem_size;
+	void	   *new_data = guc_malloc(ERROR, new_arr_mem_size);
+
+	if (new_len < last_len)
+	{
+		/* free elements from deleted part */
+		char	   *basic_type = get_array_basic_type(array_type);
+
+		for (int i = new_len; i < last_len; i++)
+		{
+			int			offset = get_element_offset(array_type, i);
+
+			free_composite_impl((char *) (*(void **) array) + offset, basic_type);
+		}
+
+		guc_free(basic_type);
+	}
+	else
+		memset((char *) new_data + last_arr_mem_size, 0, new_arr_mem_size - last_arr_mem_size);
+
+	memcpy(new_data, *(void **) array, min_arr_mem_size);
+	guc_free(*(void **) array);
+
+	*(void **) array = new_data;
+	dynamic_array_size(array) = new_len;
+}
+
+static void
+check_memory(yyscan_t yyscanner, const char **hintmsg)
+{
+	int			len = 0;
+	int			idx = 0;
+
+	/*
+	 * next part of function process a case, when we expect parsing elements
+	 * of data of dynamic array therefore context(1) must be not extended
+	 * (i.e. inner context)
+	 *
+	 * If context(1) is extended dynamic array (i.e. outer context), we are
+	 * going to parse "data" or "size" field
+	 */
+	if (!(is_dynamic_array_type(context(1)->type)
+		  && context(1)->extended != true))
+		return;
+
+	len = dynamic_array_size(context(2)->start);
+	idx = context(1)->idx;
+
+	if (idx > context(2)->max_idx)
+		context(2)->max_idx = idx;
+
+	if (idx >= len)
+	{
+		int			offset;
+
+		if (context(2)->fixed_size == FIXED_SIZE_IS_SETTED)
+		{
+			guc_composite_yyerror(last_context->start,
+								last_context->type,
+								hintmsg,
+								0,
+								yyscanner,
+								"there is index greater than array length.\
+								Change \"size\" field");
+			return;
+		}
+
+		reallocate_dynamic_array(context(2)->type, context(2)->start, idx + 1);
+
+		/* update contexts about dynamic array */
+		context(1)->start = *(void **) context(2)->start;
+		offset = get_element_offset(context(1)->type, context(1)->idx);
+		context(0)->start = (char *) context(1)->start + offset;
+	}
+}
+
+/*
+ * Check all conditions related to context: name, allocated memory
+ */
+static void
+prepare_value_context(yyscan_t yyscanner, const char **hintmsg)
+{
+	/* top-level structure always is okey */
+	if (list_length(contexts) == 1)
+		return;
+	check_name(yyscanner, hintmsg);
+	check_memory(yyscanner, hintmsg);
+}
+
+/*
+ * Prepare context for structure. Add new layer in stack that will be filled by parse_name
+ */
+static void
+prepare_structure(void)
+{
+	/* if type is dynamic array => that is extended form of array */
+	if (is_dynamic_array_type(context(0)->type))
+		context(0)->extended = true;
+
+	/* add context for structure field */
+	push_context(NULL, context(0)->start);
+}
+
+/*
+ * Prepare context for array. Add new layer to stack that will be filled by parse_name.
+ * In case of parsing dynamic array we must guarantee that
+ * current context->start points to pointer to data of dynamic array
+ */
+static void
+prepare_array(void)
+{
+	void	   *data = NULL;
+	char	   *basic_type = get_array_basic_type(context(0)->type);
+
+	if (!is_dynamic_array_type(context(0)->type))
+	{
+		push_context(pstrdup(basic_type), context(0)->start);
+
+		goto out;
+	}
+	else
+	{
+		/*
+		 * There are 4 cases that could be: "{data: [" or "[" or "[ [" or "{
+		 * field: [" and only first case don't need creating new stack layer
+		 */
+		if (!(list_length(contexts) > 1 && context(1)->extended))
+		{
+			data = *(void **) context(0)->start;
+
+			/*
+			 * Each dynamic array has 2 contexts: outer context for structure
+			 * {data, size} And inner context for allocated data. Because of
+			 * different ways to set dynamic array (extended and compact
+			 * forms) outer context of dynamic array has flag "extended". When
+			 * we pop from stack, we see this flag and for compact
+			 * representation of array we pop twice (for purpose of popping
+			 * inner and outer contexts at one time)
+			 */
+			if (data)
+				push_context(context(0)->type, data);
+			else
+				push_context(context(0)->type, NULL);
+		}
+
+		data = context(0)->start;
+
+		/*
+		 * Current dynamic array could be empty. In this case create
+		 * fictitious stack layer (NULL in start field means that now we parse
+		 * element of empty dynamic array) for purposes of recursive algorithm
+		 */
+		if (data)
+			push_context(pstrdup(basic_type), data);
+		else
+			push_context(pstrdup(basic_type), NULL);
+	}
+
+out:
+	guc_free(basic_type);
+}
+
+/*
+ * Update context when parser go out of nested
+ * level of structure. So, rewind stack of contexts.
+ */
+static void
+parse_composite_end(void)
+{
+	/*
+	 * is_dynamic is used to not miss in case: Delete context and view not
+	 * extended dynamic array. So that is inner or outer context? If
+	 * is_dynamic == true => that is outer context Else that is inner context
+	 */
+	bool		is_dynamic = false;
+
+	/*
+	 * is_extended is used in cases when we delete outer context of array that
+	 * is nested in dynamic array in compact form
+	 */
+	bool		is_extended = context(0)->extended;
+
+	/*
+	 * type of current context might have NULL type in case than we parse {}
+	 * Then go exactly to deleting context
+	 */
+	if (context(0)->type)
+		is_dynamic = is_dynamic_array_type(context(0)->type);
+
+	free_context((parser_ctx *) list_nth(contexts, 0));
+	contexts = list_delete_first(contexts);
+
+	/*
+	 * If deleted layer was extended, that layer was outer context => return
+	 */
+	if (is_extended)
+		return;
+
+	/*
+	 * Free up outer context for dynamic array in compact form. See comments
+	 * in parse_array function
+	 */
+	if (!list_empty(contexts)
+		&& is_dynamic
+		&& is_dynamic_array_type(context(0)->type)
+		&& context(0)->extended != true)
+	{
+		free_context((parser_ctx *) list_nth(contexts, 0));
+		contexts = list_delete_first(contexts);
+	}
+}
+
+/*
+ * Update context before parser go to parse next field
+ */
+static void
+parse_field_end(void)
+{
+	context(0)->idx++;			/* context of structure/array */
+
+	if (is_dynamic_array_type(context(0)->type) || is_static_array_type(context(0)->type))
+	{
+		void	   *data = *(void **) context(0)->start;
+		char	   *basic_type = get_array_basic_type(context(0)->type);
+
+		if (data)
+			push_context(pstrdup(basic_type), data);
+		else
+			push_context(pstrdup(basic_type), NULL);
+
+		guc_free(basic_type);
+	}
+	else
+		push_context(NULL, context(0)->start);
+
+	context(0)->has_name = false;	/* context of field/element */
+}
+
+static int
+parse_index(const char *index, yyscan_t yyscanner, const char **hintmsg)
+{
+	for (const char *c = index; *c; c++)
+	{
+		if (!isdigit(*c))
+		{
+			guc_composite_yyerror(last_context->start, last_context->type, hintmsg, 0, yyscanner, "incorrect index");
+			return -1;
+		}
+	}
+
+	return atoi(index);
+}
+
+/*
+ * Parse index and compute offset in array. Fill current context
+ */
+static void
+parse_element(const char *index, yyscan_t yyscanner, const char **hintmsg)
+{
+	int			idx = parse_index(index, yyscanner, hintmsg);
+
+	if (*hintmsg)
+		return;
+
+	/* for static array check len */
+	if (is_static_array_type(context(1)->type))
+	{
+		int			len = get_static_array_length(context(1)->type);
+
+		if (idx >= len)
+		{
+			guc_composite_yyerror(last_context->start,
+								  last_context->type,
+								  hintmsg,
+								  0,
+								  yyscanner,
+								  "there is index which is greater than size of array");
+			return;
+		}
+	}
+
+	context(1)->idx = idx;
+	context(0)->start = (char *) context(1)->start + get_element_offset(context(1)->type, idx);
+}
+
+/*
+ * Parse name, check correctness. Fill current context
+ */
+static void
+parse_field(const char *name, yyscan_t yyscanner, const char **hintmsg)
+{
+	char	   *field_type;
+	int			offset = get_field_offset(context(1)->type, name);
+
+	if (offset < 0)
+	{
+		guc_composite_yyerror(last_context->start,
+							  last_context->type,
+							  hintmsg,
+							  0,
+							  yyscanner,
+							  "incorrect field name");
+		return;
+	}
+
+	/* create new context */
+	field_type = get_field_type_name(context(1)->type, name);
+	context(0)->type = pstrdup(field_type);
+	context(0)->start = (char *) context(1)->start + offset;
+	guc_free(field_type);
+
+	/* process fields size and data in extended version of dynamic array */
+	if (context(1)->extended)
+	{
+		if (strcmp(name, "size") == 0)
+			context(1)->fixed_size = FIXED_SIZE_IS_BEING_SETTED;
+		else if (strcmp(name, "data") == 0)
+		{
+			void	   *data = *(void **) context(1)->start;
+
+			if (data)
+				context(0)->start = data;
+			else
+				context(0)->start = NULL;
+		}
+	}
+}
+
+/*
+ * Compute the pointer to field by name of field, fill current context
+ */
+static void
+parse_name(const char *name, yyscan_t yyscanner, const char **hintmsg)
+{
+	/* Name could be an index for arrays */
+	if (is_static_array_type(context(1)->type) ||
+		(is_dynamic_array_type(context(1)->type) && context(1)->extended == false))
+	{
+		if (context(1)->name_usage == NAME_USAGE_NEVER)
+		{
+			guc_composite_yyerror(last_context->start,
+								  last_context->type,
+								  hintmsg,
+								  0,
+								  yyscanner,
+								  "use indexes for either everyone or no one");
+			return;
+		}
+
+		context(1)->name_usage = NAME_USAGE_ALWAYS;
+		parse_element(name, yyscanner, hintmsg);
+	}
+	else
+		parse_field(name, yyscanner, hintmsg);
+
+	context(0)->has_name = true;
+}
+
+static void
+parse_scalar_opt(char *strval, const char *struct_type, void *result, int flags, yyscan_t yyscanner, const char **hintmsg)
+{
+	if (strcmp(struct_type, "bool") == 0)
+	{
+		if (!parse_bool(strval, (bool *) result))
+		{
+			guc_composite_yyerror(last_context->start, last_context->type, hintmsg, 0, yyscanner, "failed to parse bool value, use 'on' and 'off'");
+			return;
+		}
+	}
+	else if (strcmp(struct_type, "int") == 0)
+	{
+		/*
+		 * Block of code for field "size" in dynamic array This block of code
+		 * must be above parse_int(). Because standard parsing will overwrite
+		 * old length, but we need it in reallocate_dynamic_array()
+		 */
+		if (list_length(contexts) > 1 && context(1)->fixed_size == FIXED_SIZE_IS_BEING_SETTED)
+		{
+			int			len = parse_index(strval, yyscanner, hintmsg);
+
+			if (*hintmsg)
+				return;
+
+			if (len <= context(1)->max_idx)
+			{
+				guc_composite_yyerror(last_context->start,
+									  last_context->type,
+									  hintmsg,
+									  0,
+									  yyscanner,
+									  "fixed size of dynamic array less\
+									   or eaual maximum index");
+				return;
+			}
+
+			reallocate_dynamic_array(context(1)->type, context(1)->start, len);
+			context(1)->fixed_size = FIXED_SIZE_IS_SETTED;
+		}
+
+		if (!parse_int(strval, (int *) result, flags, hintmsg))
+		{
+			guc_composite_yyerror(last_context->start,
+								  last_context->type,
+								  hintmsg,
+								  0,
+								  yyscanner,
+								  "failed to parse int value, check units");
+			return;
+		}
+	}
+	else if (strcmp(struct_type, "real") == 0)
+	{
+		if (!parse_real(strval, (double *) result, flags, hintmsg))
+		{
+			guc_composite_yyerror(last_context->start,
+								  last_context->type,
+								  hintmsg,
+								  0,
+								  yyscanner,
+								  "failed to parse real value, check delimiter");
+			return;
+		}
+	}
+	else if (strcmp(struct_type, "string") == 0)
+	{
+		if (strcmp(strval, "\\nil") == 0)
+			*((char **) result) = NULL;
+		else
+		{
+			if (*((char **) result))
+				guc_free(*((char **) result));
+
+			*((char **) result) = guc_strdup(ERROR, strval);
+		}
+	}
+	else
+	{
+		guc_composite_yyerror(last_context->start,
+							  last_context->type,
+							  hintmsg,
+							  0,
+							  yyscanner,
+							  "failed to determine type of simple field");
+		return;
+	}
+}
+
+bool
+parse_composite(const char *strvalue, const char *type, void **result, const void *prev_val, int flags, const char **hintmsg)
+{
+	int			size = 0;
+	yyscan_t	scanner;
+	void	   *val = NULL;
+	int			parser_result = 0;
+	bool		check = true;
+
+	*hintmsg = NULL;
+	size = get_composite_size(type);
+	val = guc_malloc(ERROR, size);
+
+	if (prev_val)
+		composite_dup_impl(val, prev_val, type);
+	else
+		memset(val, 0, size);
+
+	guc_composite_scanner_init(strvalue, &scanner);
+	parser_result = guc_composite_yyparse(val, type, hintmsg, flags, scanner);
+	guc_composite_scanner_finish(scanner);
+	free_context_list();
+	if (parser_result != 0 || *hintmsg)
+	{
+		guc_free(val);
+		*result = NULL;
+		check = false;
+	}
+	else
+		*result = val;
+
+	return check;
+}
diff --git a/src/backend/utils/misc/guc_composite_scan.l b/src/backend/utils/misc/guc_composite_scan.l
new file mode 100644
index 0000000000..1f249ea87a
--- /dev/null
+++ b/src/backend/utils/misc/guc_composite_scan.l
@@ -0,0 +1,132 @@
+%top{
+/*-------------------------------------------------------------------------
+ *
+ * guc_composite_scan.l
+ *	  a lexical scanner for all composite guc values
+ *
+ * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *	  src/backend/utils/misc/guc_composite_scan.l
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+#include "utils/guc.h"
+#include "guc_composite.h"
+#include "guc_composite_gram.h"
+
+#define YY_DECL extern int	guc_composite_yylex(union YYSTYPE *yylval_param, const char **hintmsg, yyscan_t yyscanner)
+}
+%option reentrant
+%option bison-bridge
+%option 8bit
+%option never-interactive
+%option nodefault
+%option noinput
+%option nounput
+%option noyywrap
+%option noyyalloc
+%option noyyrealloc
+%option noyyfree
+%option warn
+%option prefix="guc_composite_yy"
+
+SPACE			[ \t\n\r\f\v]
+
+LETTER_OR_DIGIT	[A-Za-z_0-9\200-\377]
+
+STRING			\'([^'\\\n]|\\.|\'\')*\'
+UNQUOTED_STRING	({LETTER_OR_DIGIT}|\.)*
+
+
+%%
+{SPACE}         /* Eat whitespace */
+
+{STRING}			{
+						/* Deescape string and return value */
+						yylval->str = DeescapeQuotedString(yytext);
+						return IDENT;
+					}
+
+{UNQUOTED_STRING}	{
+						yylval->str = pstrdup(yytext);
+						return IDENT;
+					}
+
+"{"					{ return '{'; }
+"}"					{ return '}'; }
+"["					{ return '['; }
+"]"					{ return ']'; }
+","					{ return ','; }
+":"					{ return ':'; }
+";"					{ return ';'; }
+
+.					{
+						guc_composite_yyerror(NULL, NULL, hintmsg, 0, yyscanner, "illegible lexeme");
+						return JUNK;
+					}
+%%
+
+/* functions  for lexer */
+
+void
+guc_composite_scanner_init(const char *str, yyscan_t *yyscannerp)
+{
+	yyscan_t	yyscanner;
+
+	if (yylex_init(yyscannerp) != 0)
+		elog(ERROR, "yylex_init() failed: %m");
+	yyscanner = *yyscannerp;
+	yy_scan_string(str, yyscanner);
+}
+
+void
+guc_composite_scanner_finish(yyscan_t yyscanner)
+{
+	yylex_destroy(yyscanner);
+}
+
+void
+guc_composite_yyerror(void *composite_ptr, const char *composite_type, const char **hintmsg, int flags, yyscan_t yyscanner, const char *message)
+{
+	struct yyguts_t *yyg = (struct yyguts_t *) yyscanner;	/* needed for yytext
+															 * macro */
+
+	/* report only the first error in a parse operation */
+	if (*hintmsg)
+		return;
+	if (yytext[0])
+		*hintmsg = psprintf("%s at or near \"%s\"", message, yytext);
+	else
+		*hintmsg = psprintf("%s at the end of input", message);
+}
+
+/*
+ * Interface functions to make flex use palloc() instead of malloc().
+ * It'd be better to make these static, but flex insists otherwise.
+ */
+
+void *
+yyalloc(yy_size_t size, yyscan_t yyscanner)
+{
+	return palloc(size);
+}
+
+void *
+yyrealloc(void *ptr, yy_size_t size, yyscan_t yyscanner)
+{
+	if (ptr)
+		return repalloc(ptr, size);
+	else
+		return palloc(size);
+}
+
+void
+yyfree(void *ptr, yyscan_t yyscanner)
+{
+	if (ptr)
+		pfree(ptr);
+}
diff --git a/src/backend/utils/misc/guc_funcs.c b/src/backend/utils/misc/guc_funcs.c
index b9e26982ab..a724fec0fc 100644
--- a/src/backend/utils/misc/guc_funcs.c
+++ b/src/backend/utils/misc/guc_funcs.c
@@ -18,6 +18,7 @@
 #include <sys/stat.h>
 #include <unistd.h>
 
+#include "utils/guc.h"
 #include "access/xact.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_authid.h"
@@ -55,15 +56,26 @@ ExecSetVariableStmt(VariableSetStmt *stmt, bool isTopLevel)
 
 	switch (stmt->kind)
 	{
+			char	   *prepared_name;
 		case VAR_SET_VALUE:
 		case VAR_SET_CURRENT:
+			if (stmt_has_serialized_composite(stmt))
+			{
+				int name_len = strlen(stmt->name) + 3;
+				prepared_name = guc_malloc(ERROR, name_len);
+				snprintf(prepared_name, name_len, "%s->", stmt->name);
+			}
+			else
+				prepared_name = guc_strdup(ERROR, stmt->name);
+
 			if (stmt->is_local)
 				WarnNoTransactionBlock(isTopLevel, "SET LOCAL");
-			(void) set_config_option(stmt->name,
+			(void) set_config_option(prepared_name,
 									 ExtractSetVariableArgs(stmt),
 									 (superuser() ? PGC_SUSET : PGC_USERSET),
 									 PGC_S_SESSION,
 									 action, true, 0, false);
+			guc_free(prepared_name);
 			break;
 		case VAR_SET_MULTI:
 
@@ -157,6 +169,42 @@ ExecSetVariableStmt(VariableSetStmt *stmt, bool isTopLevel)
 									ACL_SET, stmt->kind, false);
 }
 
+/*
+ * Get arguments of set statement and check if some arguments are
+ * serialized composite options
+ */
+bool
+stmt_has_serialized_composite(VariableSetStmt *stmt)
+{
+	List	   *args;
+	ListCell   *l;
+
+	switch (stmt->kind)
+	{
+		case VAR_SET_VALUE:
+			args = stmt->args;
+			/* Fast path if just DEFAULT */
+			if (args == NIL)
+				return false;
+			/* go throw  args list and check nodeTags */
+			foreach(l, args)
+			{
+				A_Const    *con;
+				Node	   *arg = (Node *) lfirst(l);
+
+				if (!IsA(arg, A_Const))
+					elog(ERROR, "unrecognized node type: %d", (int) nodeTag(arg));
+				con = (A_Const *) arg;
+
+				if (nodeTag(&con->val) == T_SerializedComposite)
+					return true;
+			}
+			return false;
+		default:
+			return false;
+	}
+}
+
 /*
  * Get the value to assign for a VariableSetStmt, or NULL if it's RESET.
  * The result is palloc'd.
@@ -295,6 +343,10 @@ flatten_set_variable_args(const char *name, List *args)
 						appendStringInfoString(&buf, val);
 				}
 				break;
+			case T_SerializedComposite:
+				val = compositeVal(&con->val);
+				appendStringInfoString(&buf, val);
+				break;
 			default:
 				elog(ERROR, "unrecognized node type: %d",
 					 (int) nodeTag(&con->val));
@@ -618,8 +670,6 @@ GetConfigOptionValues(struct config_generic *conf, const char **values)
 	/* context */
 	values[6] = GucContext_Names[conf->context];
 
-	/* vartype */
-	values[7] = config_type_names[conf->vartype];
 
 	/* source */
 	values[8] = GucSource_Names[conf->source];
@@ -631,6 +681,9 @@ GetConfigOptionValues(struct config_generic *conf, const char **values)
 			{
 				struct config_bool *lconf = (struct config_bool *) conf;
 
+				/* vartype */
+				values[7] = pstrdup(config_type_names[conf->vartype]);
+
 				/* min_val */
 				values[9] = NULL;
 
@@ -652,6 +705,9 @@ GetConfigOptionValues(struct config_generic *conf, const char **values)
 			{
 				struct config_int *lconf = (struct config_int *) conf;
 
+				/* vartype */
+				values[7] = pstrdup(config_type_names[conf->vartype]);
+
 				/* min_val */
 				snprintf(buffer, sizeof(buffer), "%d", lconf->min);
 				values[9] = pstrdup(buffer);
@@ -677,6 +733,9 @@ GetConfigOptionValues(struct config_generic *conf, const char **values)
 			{
 				struct config_real *lconf = (struct config_real *) conf;
 
+				/* vartype */
+				values[7] = pstrdup(config_type_names[conf->vartype]);
+
 				/* min_val */
 				snprintf(buffer, sizeof(buffer), "%g", lconf->min);
 				values[9] = pstrdup(buffer);
@@ -702,6 +761,9 @@ GetConfigOptionValues(struct config_generic *conf, const char **values)
 			{
 				struct config_string *lconf = (struct config_string *) conf;
 
+				/* vartype */
+				values[7] = pstrdup(config_type_names[conf->vartype]);
+
 				/* min_val */
 				values[9] = NULL;
 
@@ -729,6 +791,9 @@ GetConfigOptionValues(struct config_generic *conf, const char **values)
 			{
 				struct config_enum *lconf = (struct config_enum *) conf;
 
+				/* vartype */
+				values[7] = pstrdup(config_type_names[conf->vartype]);
+
 				/* min_val */
 				values[9] = NULL;
 
@@ -754,6 +819,48 @@ GetConfigOptionValues(struct config_generic *conf, const char **values)
 			}
 			break;
 
+		case PGC_COMPOSITE:
+			{
+				struct config_composite *lconf = (struct config_composite *) conf;
+				int			len = (strlen(lconf->type_name) + 8);
+
+				/* vartype */
+				values[7] = palloc(len);
+				snprintf((char *) values[7], len, "%s %s", config_type_names[conf->vartype], lconf->type_name);
+
+				/* min_val */
+				values[9] = NULL;
+
+				/* max_val */
+				values[10] = NULL;
+
+				/* enumvals */
+				values[11] = NULL;
+				/* boot_val */
+				if (lconf->boot_val == NULL)
+					values[12] = NULL;
+				else
+				{
+					bool		not_write_to_file = false;
+					char	   *boot_v = composite_to_str(lconf->boot_val, lconf->type_name, not_write_to_file);
+
+					values[12] = pstrdup(boot_v);
+					guc_free(boot_v);
+				}
+				/* reset_val */
+				if (lconf->reset_val == NULL)
+					values[13] = NULL;
+				else
+				{
+					bool		not_write_to_file = false;
+					char	   *reset = composite_to_str(lconf->reset_val, lconf->type_name, not_write_to_file);
+
+					values[13] = pstrdup(reset);
+					guc_free(reset);
+				}
+			}
+			break;
+
 		default:
 			{
 				/*
diff --git a/src/backend/utils/misc/guc_parameters.dat b/src/backend/utils/misc/guc_parameters.dat
index 6bc6be13d2..c392d00972 100644
--- a/src/backend/utils/misc/guc_parameters.dat
+++ b/src/backend/utils/misc/guc_parameters.dat
@@ -917,6 +917,12 @@
   boot_val => 'true',
 },
 
+{ name => 'extended_guc_arrays', type => 'bool', context => 'PGC_USERSET', group => 'CUSTOM_OPTIONS',
+  short_desc => 'Enables expanded view of dynamic arrays in GUC',
+  variable => 'extended_array_view',
+  boot_val => 'false',
+},
+
 { name => 'archive_timeout', type => 'int', context => 'PGC_SIGHUP', group => 'WAL_ARCHIVING',
   short_desc => 'Sets the amount of time to wait before forcing a switch to the next WAL file.',
   long_desc => '0 disables the timeout.',
diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c
index 00c8376cf4..c45be54f3e 100644
--- a/src/backend/utils/misc/guc_tables.c
+++ b/src/backend/utils/misc/guc_tables.c
@@ -755,10 +755,72 @@ const char *const config_type_names[] =
 	[PGC_REAL] = "real",
 	[PGC_STRING] = "string",
 	[PGC_ENUM] = "enum",
+	[PGC_COMPOSITE] = "composite",
 };
 
-StaticAssertDecl(lengthof(config_type_names) == (PGC_ENUM + 1),
+StaticAssertDecl(lengthof(config_type_names) == (PGC_COMPOSITE + 1),
 				 "array length mismatch");
 
+/*
+ * Table of user composite types
+ *
+ * See src/backend/utils/misc/README for design notes.
+ *
+ * TO ADD NEW DEFINITION OF COMPOSITE TYPE:
+ *
+ * 1.    Decide on a type name
+ *
+ * 2.    Add a record below.
+ *   	 Signature (second field of structure) must be in format:
+ *    	  	"<type_1> <name_1>;<type_2> <name_2>; ... ; <type_K> <name_K>"
+ *    	 	where type_N - one of { bool, int, real, string, <custom_type>}
+ *		 	(custom_type - already defined composite type,
+ *			 array[<number>] - static array,
+ *			 array[] - dynamic array)
+ *
+ * HINT. Do not fill 3 - 6 fields, they will be filled automatically
+ *       in init_type_definition function
+ *
+ * NOTE. You must change set of functions in guc_composite.c to add
+ *		 a scalar type. See how built-in types are implemented.
+ */
+struct type_definition UserDefinedConfigureTypes[] = {
+	{
+		"bool",
+		NULL,
+		0,
+		sizeof(bool),
+		sizeof(bool),
+		NULL
+	},
+	{
+		"int",
+		NULL,
+		0,
+		sizeof(int),
+		sizeof(int),
+		NULL
+	},
+	{
+		"real",
+		NULL,
+		0,
+		sizeof(double),
+		sizeof(double),
+		NULL
+	},
+	{
+		"string",
+		NULL,
+		0,
+		sizeof(char *),
+		sizeof(char *),
+		NULL
+	},
+	/* End-of-list marker */
+	{
+		NULL, NULL
+	}
+};
 
 #include "utils/guc_tables.inc.c"
diff --git a/src/backend/utils/misc/help_config.c b/src/backend/utils/misc/help_config.c
index 55c36ddf05..2722baa262 100644
--- a/src/backend/utils/misc/help_config.c
+++ b/src/backend/utils/misc/help_config.c
@@ -35,6 +35,7 @@ typedef union
 	struct config_int integer;
 	struct config_string string;
 	struct config_enum _enum;
+	struct config_composite _composite;
 } mixedStruct;
 
 
@@ -125,6 +126,18 @@ printMixedStruct(mixedStruct *structToPrint)
 											   structToPrint->_enum.boot_val));
 			break;
 
+		case PGC_COMPOSITE:
+			{
+				bool		not_write_to_file = false;
+				char	   *valstr = NULL;
+
+				valstr = composite_to_str(structToPrint->_composite.boot_val,
+										  structToPrint->_composite.type_name,
+										  not_write_to_file);
+				printf("COMPSITE %s\t%s\t\t\t", structToPrint->_composite.type_name, valstr);
+				guc_free(valstr);
+				break;
+			}
 		default:
 			write_stderr("internal error: unrecognized run-time parameter type\n");
 			break;
diff --git a/src/backend/utils/misc/meson.build b/src/backend/utils/misc/meson.build
index 9e389a00d0..3a9695af60 100644
--- a/src/backend/utils/misc/meson.build
+++ b/src/backend/utils/misc/meson.build
@@ -2,6 +2,7 @@
 
 backend_sources += files(
   'conffiles.c',
+  'guc_composite.c',
   'guc.c',
   'guc_funcs.c',
   'guc_tables.c',
@@ -20,6 +21,31 @@ backend_sources += files(
   'tzparser.c',
 )
 
+# see ../../parser/meson.build
+guc_composite_parser_sources = []
+
+guc_composite_scan = custom_target('guc_composite_scan',
+  input: 'guc_composite_scan.l',
+  output: 'guc_composite_scan.c',
+  command: flex_cmd,
+)
+generated_sources += guc_composite_scan
+guc_composite_parser_sources += guc_composite_scan
+
+guc_composite_gram = custom_target('guc_composite_gram',
+  input: 'guc_composite_gram.y',
+  kwargs: bison_kw,
+)
+generated_sources += guc_composite_gram.to_list()
+guc_composite_parser_sources += guc_composite_gram
+
+backend_link_with += static_library('guc_composite_parser',
+  guc_composite_parser_sources,
+  dependencies: [backend_code],
+  include_directories: include_directories('.'),
+  kwargs: internal_lib_args,
+)
+
 guc_scan = custom_target('guc_scan',
   input: 'guc-file.l',
   output: 'guc-file.c',
diff --git a/src/include/nodes/makefuncs.h b/src/include/nodes/makefuncs.h
index 5473ce9a28..5e5552b3c7 100644
--- a/src/include/nodes/makefuncs.h
+++ b/src/include/nodes/makefuncs.h
@@ -102,6 +102,7 @@ extern IndexInfo *makeIndexInfo(int numattrs, int numkeyattrs, Oid amoid,
 								bool summarizing, bool withoutoverlaps);
 
 extern Node *makeStringConst(char *str, int location);
+extern Node *makeSerializedCompositeConst(char *str, int location);
 extern DefElem *makeDefElem(char *name, Node *arg, int location);
 extern DefElem *makeDefElemExtended(char *nameSpace, char *name, Node *arg,
 									DefElemAction defaction, int location);
diff --git a/src/include/nodes/value.h b/src/include/nodes/value.h
index 3ee3b976b8..6a1b31c90f 100644
--- a/src/include/nodes/value.h
+++ b/src/include/nodes/value.h
@@ -76,15 +76,25 @@ typedef struct BitString
 	char	   *bsval;
 } BitString;
 
+typedef struct SerializedComposite
+{
+	pg_node_attr(special_read_write)
+
+	NodeTag		type;
+	char	   *sval;
+} SerializedComposite;
+
 #define intVal(v)		(castNode(Integer, v)->ival)
 #define floatVal(v)		atof(castNode(Float, v)->fval)
 #define boolVal(v)		(castNode(Boolean, v)->boolval)
 #define strVal(v)		(castNode(String, v)->sval)
+#define compositeVal(v)	(castNode(SerializedComposite, v)->sval)
 
 extern Integer *makeInteger(int i);
 extern Float *makeFloat(char *numericStr);
 extern Boolean *makeBoolean(bool val);
 extern String *makeString(char *str);
 extern BitString *makeBitString(char *str);
+extern SerializedComposite *makeSerializedComposite(char *str);
 
 #endif							/* VALUE_H */
diff --git a/src/include/utils/guc.h b/src/include/utils/guc.h
index f21ec37da8..b197a81f28 100644
--- a/src/include/utils/guc.h
+++ b/src/include/utils/guc.h
@@ -185,12 +185,14 @@ typedef bool (*GucIntCheckHook) (int *newval, void **extra, GucSource source);
 typedef bool (*GucRealCheckHook) (double *newval, void **extra, GucSource source);
 typedef bool (*GucStringCheckHook) (char **newval, void **extra, GucSource source);
 typedef bool (*GucEnumCheckHook) (int *newval, void **extra, GucSource source);
+typedef bool (*GucCompositeCheckHook) (void *newval, void **extra, GucSource source);
 
 typedef void (*GucBoolAssignHook) (bool newval, void *extra);
 typedef void (*GucIntAssignHook) (int newval, void *extra);
 typedef void (*GucRealAssignHook) (double newval, void *extra);
 typedef void (*GucStringAssignHook) (const char *newval, void *extra);
 typedef void (*GucEnumAssignHook) (int newval, void *extra);
+typedef void (*GucCompositeAssignHook) (void *newval, void *extra);
 
 typedef const char *(*GucShowHook) (void);
 
@@ -243,6 +245,11 @@ typedef enum
 
 #define GUC_UNIT			 (GUC_UNIT_MEMORY | GUC_UNIT_TIME)
 
+/*
+ * Precision with which REAL type guc values are to be printed for GUC
+ * serialization.
+ */
+#define REALTYPE_PRECISION 17
 
 /* GUC vars that are actually defined in guc_tables.c, rather than elsewhere */
 extern PGDLLIMPORT bool Debug_print_plan;
@@ -325,6 +332,8 @@ extern PGDLLIMPORT char *role_string;
 extern PGDLLIMPORT bool in_hot_standby_guc;
 extern PGDLLIMPORT bool trace_sort;
 
+extern PGDLLIMPORT bool extended_array_view;
+
 #ifdef DEBUG_BOUNDED_SORT
 extern PGDLLIMPORT bool optimize_bounded_sort;
 #endif
@@ -413,6 +422,20 @@ extern void DefineCustomEnumVariable(const char *name,
 									 GucEnumAssignHook assign_hook,
 									 GucShowHook show_hook) pg_attribute_nonnull(1, 4);
 
+extern void DefineCustomCompositeVariable(const char *name,
+										  const char *short_desc,
+										  const char *long_desc,
+										  const char *type_name,
+										  void *valueAddr,
+										  const void *bootValueAddr,
+										  GucContext context,
+										  int flags,
+										  GucCompositeCheckHook check_hook,
+										  GucCompositeAssignHook assign_hook,
+										  GucShowHook show_hook);
+
+extern void DefineCustomCompositeType(const char *type_name, const char *signature);
+
 extern void MarkGUCPrefixReserved(const char *className);
 
 /* old name for MarkGUCPrefixReserved, for backwards compatibility: */
@@ -473,6 +496,8 @@ pg_nodiscard extern void *guc_realloc(int elevel, void *old, size_t size);
 extern char *guc_strdup(int elevel, const char *src);
 extern void guc_free(void *ptr);
 
+char	   *composite_to_str(const void *structp, const char *type, bool writing_to_file);
+
 #ifdef EXEC_BACKEND
 extern void write_nondefault_variables(GucContext context);
 extern void read_nondefault_variables(void);
@@ -486,6 +511,7 @@ extern void RestoreGUCState(void *gucstate);
 /* Functions exported by guc_funcs.c */
 extern void ExecSetVariableStmt(VariableSetStmt *stmt, bool isTopLevel);
 extern char *ExtractSetVariableArgs(VariableSetStmt *stmt);
+extern bool stmt_has_serialized_composite(VariableSetStmt *stmt);
 extern void SetPGVariable(const char *name, List *args, bool is_local);
 extern void GetPGVariable(const char *name, DestReceiver *dest);
 extern TupleDesc GetPGVariableResultDesc(const char *name);
diff --git a/src/include/utils/guc_tables.h b/src/include/utils/guc_tables.h
index f72ce944d7..efe85fd80f 100644
--- a/src/include/utils/guc_tables.h
+++ b/src/include/utils/guc_tables.h
@@ -27,6 +27,7 @@ enum config_type
 	PGC_REAL,
 	PGC_STRING,
 	PGC_ENUM,
+	PGC_COMPOSITE,
 };
 
 union config_var_val
@@ -36,6 +37,7 @@ union config_var_val
 	double		realval;
 	char	   *stringval;
 	int			enumval;
+	void	   *compositeval;
 };
 
 /*
@@ -298,18 +300,56 @@ struct config_enum
 	void	   *reset_extra;
 };
 
+/* work with type signatures */
+
+typedef struct struct_field
+{
+	char	   *type;
+	char	   *name;
+}			struct_field;
+
+struct type_definition
+{
+	/* constant fields */
+	const char *type_name;
+	const char *signature;
+	int			cnt_fields;
+	int			type_size;
+	int			offset;
+	struct_field *fields;
+};
+
+struct config_composite
+{
+	struct config_generic gen;
+	/* constant fields, must be set correctly in initial value: */
+	const char *type_name;
+	void	   *variable;
+	const void *boot_val;
+	GucCompositeCheckHook check_hook;
+	GucCompositeAssignHook assign_hook;
+	GucShowHook show_hook;
+	/* variable fields, initialized at runtime: */
+	void	   *reset_val;
+	void	   *reset_extra;
+	const struct type_definition *definition;
+};
+
 /* constant tables corresponding to enums above and in guc.h */
 extern PGDLLIMPORT const char *const config_group_names[];
 extern PGDLLIMPORT const char *const config_type_names[];
 extern PGDLLIMPORT const char *const GucContext_Names[];
 extern PGDLLIMPORT const char *const GucSource_Names[];
 
+/* array defining all the built-in composite types for GUC variables */
+extern PGDLLIMPORT struct type_definition UserDefinedConfigureTypes[];
 /* data arrays defining all the built-in GUC variables */
 extern PGDLLIMPORT struct config_bool ConfigureNamesBool[];
 extern PGDLLIMPORT struct config_int ConfigureNamesInt[];
 extern PGDLLIMPORT struct config_real ConfigureNamesReal[];
 extern PGDLLIMPORT struct config_string ConfigureNamesString[];
 extern PGDLLIMPORT struct config_enum ConfigureNamesEnum[];
+extern PGDLLIMPORT struct config_composite ConfigureNamesComposite[];
 
 /* lookup GUC variables, returning config_generic pointers */
 extern struct config_generic *find_option(const char *name,
diff --git a/src/interfaces/ecpg/preproc/parse.pl b/src/interfaces/ecpg/preproc/parse.pl
index f22ca213c2..3608453e6d 100644
--- a/src/interfaces/ecpg/preproc/parse.pl
+++ b/src/interfaces/ecpg/preproc/parse.pl
@@ -260,6 +260,10 @@ sub main
 		s/{/ { /g;
 		s/}/ } /g;
 
+		# Make exclusion for '{' and '}'
+		s/' \{ '/'{'/g;
+		s/' \} '/'}'/g;
+
 		# Likewise for comment start/end markers
 		s|\/\*| /* |g;
 		s|\*\/| */ |g;
-- 
2.48.1

