Hi PostgreSQL Community,
Recently I have been working on foreign servers regarding my project and
wanted to add some extensions in server options to support query pushdown.
For this, suppose I had 20 extensions in the beginning I used ALTER SERVER
srv OPTIONS (ADD EXTENSIONS 'all 20 extensions'), then again, I had to add
a few or drop some, I had to write names of all the 20 extensions
including/excluding some.
I wonder why we can't have some sort of INCLUDE / EXCLUDE option for this
use case that can be useful for other options as well which have
comma-separated values. I believe this is a useful feature to have for the
users.
Since I needed that support, I took the initiative to contribute to the
community. In addition, I have improved the documentation too as currently
while reading the documentation it looks like ADD can be used multiple
times even to include some values on top of existing values.
Attached is the patch for the same. Looking forward to your feedback.

Regards
Ayush Vatsa
Amazon Web Services (AWS)
From 322e932d21345a178ccc254e940aaaa4dea816e5 Mon Sep 17 00:00:00 2001
From: Ayush Vatsa <ayuvatsa@amazon.com>
Date: Fri, 26 Apr 2024 00:30:00 +0530
Subject: [PATCH v1] Enhance CREATE/ALTER statement with extended support for
 INCLUDE and EXCLUDE actions to modify existing options

---
 .../postgres_fdw/expected/postgres_fdw.out    | 10 +++
 contrib/postgres_fdw/sql/postgres_fdw.sql     |  3 +
 .../sgml/ref/alter_foreign_data_wrapper.sgml  | 51 ++++++++++--
 doc/src/sgml/ref/alter_foreign_table.sgml     | 43 ++++++++--
 doc/src/sgml/ref/alter_server.sgml            | 59 ++++++++++++--
 doc/src/sgml/ref/alter_user_mapping.sgml      | 42 ++++++++--
 src/backend/commands/foreigncmds.c            | 81 ++++++++++++++++++-
 src/backend/parser/gram.y                     | 10 +++
 src/include/nodes/parsenodes.h                |  2 +
 9 files changed, 269 insertions(+), 32 deletions(-)

diff --git a/contrib/postgres_fdw/expected/postgres_fdw.out b/contrib/postgres_fdw/expected/postgres_fdw.out
index 078b8a966f..6cd01544c6 100644
--- a/contrib/postgres_fdw/expected/postgres_fdw.out
+++ b/contrib/postgres_fdw/expected/postgres_fdw.out
@@ -178,10 +178,20 @@ ALTER SERVER testserver1 OPTIONS (
 -- Error, invalid list syntax
 ALTER SERVER testserver1 OPTIONS (ADD extensions 'foo; bar');
 ERROR:  parameter "extensions" must be a list of extension names
+ALTER SERVER testserver1 OPTIONS (INCLUDE extensions 'foo; bar');
+ERROR:  parameter "extensions" must be a list of extension names
 -- OK but gets a warning
 ALTER SERVER testserver1 OPTIONS (ADD extensions 'foo, bar');
 WARNING:  extension "foo" is not installed
 WARNING:  extension "bar" is not installed
+ALTER SERVER testserver1 OPTIONS (INCLUDE extensions 'ext1, ext2');
+WARNING:  extension "foo" is not installed
+WARNING:  extension "bar" is not installed
+WARNING:  extension "ext1" is not installed
+WARNING:  extension "ext2" is not installed
+ALTER SERVER testserver1 OPTIONS (EXCLUDE extensions 'foo, ext2, ext3');
+WARNING:  extension "bar" is not installed
+WARNING:  extension "ext1" is not installed
 ALTER SERVER testserver1 OPTIONS (DROP extensions);
 ALTER USER MAPPING FOR public SERVER testserver1
 	OPTIONS (DROP user, DROP password);
diff --git a/contrib/postgres_fdw/sql/postgres_fdw.sql b/contrib/postgres_fdw/sql/postgres_fdw.sql
index 09ba234e43..a7c4a50a5d 100644
--- a/contrib/postgres_fdw/sql/postgres_fdw.sql
+++ b/contrib/postgres_fdw/sql/postgres_fdw.sql
@@ -192,9 +192,12 @@ ALTER SERVER testserver1 OPTIONS (
 
 -- Error, invalid list syntax
 ALTER SERVER testserver1 OPTIONS (ADD extensions 'foo; bar');
+ALTER SERVER testserver1 OPTIONS (INCLUDE extensions 'foo; bar');
 
 -- OK but gets a warning
 ALTER SERVER testserver1 OPTIONS (ADD extensions 'foo, bar');
+ALTER SERVER testserver1 OPTIONS (INCLUDE extensions 'ext1, ext2');
+ALTER SERVER testserver1 OPTIONS (EXCLUDE extensions 'foo, ext2, ext3');
 ALTER SERVER testserver1 OPTIONS (DROP extensions);
 
 ALTER USER MAPPING FOR public SERVER testserver1
diff --git a/doc/src/sgml/ref/alter_foreign_data_wrapper.sgml b/doc/src/sgml/ref/alter_foreign_data_wrapper.sgml
index dc0957d965..e1353ec275 100644
--- a/doc/src/sgml/ref/alter_foreign_data_wrapper.sgml
+++ b/doc/src/sgml/ref/alter_foreign_data_wrapper.sgml
@@ -24,7 +24,7 @@ PostgreSQL documentation
 ALTER FOREIGN DATA WRAPPER <replaceable class="parameter">name</replaceable>
     [ HANDLER <replaceable class="parameter">handler_function</replaceable> | NO HANDLER ]
     [ VALIDATOR <replaceable class="parameter">validator_function</replaceable> | NO VALIDATOR ]
-    [ OPTIONS ( [ ADD | SET | DROP ] <replaceable class="parameter">option</replaceable> ['<replaceable class="parameter">value</replaceable>'] [, ... ]) ]
+    [ OPTIONS ( [ ADD | SET | DROP | INCLUDE | EXCLUDE ] <replaceable class="parameter">option</replaceable> ['<replaceable class="parameter">value</replaceable>'] [, ... ]) ]
 ALTER FOREIGN DATA WRAPPER <replaceable class="parameter">name</replaceable> OWNER TO { <replaceable>new_owner</replaceable> | CURRENT_ROLE | CURRENT_USER | SESSION_USER }
 ALTER FOREIGN DATA WRAPPER <replaceable class="parameter">name</replaceable> RENAME TO <replaceable>new_name</replaceable>
 </synopsis>
@@ -113,15 +113,43 @@ ALTER FOREIGN DATA WRAPPER <replaceable class="parameter">name</replaceable> REN
    </varlistentry>
 
    <varlistentry>
-    <term><literal>OPTIONS ( [ ADD | SET | DROP ] <replaceable class="parameter">option</replaceable> ['<replaceable class="parameter">value</replaceable>'] [, ... ] )</literal></term>
+    <term><literal>OPTIONS ( [ ADD | SET | DROP | INCLUDE | EXCLUDE ] <replaceable class="parameter">option</replaceable> ['<replaceable class="parameter">value</replaceable>'] [, ... ] )</literal></term>
     <listitem>
      <para>
       Change options for the foreign-data
-      wrapper.  <literal>ADD</literal>, <literal>SET</literal>, and <literal>DROP</literal>
-      specify the action to be performed.  <literal>ADD</literal> is assumed
-      if no operation is explicitly specified.  Option names must be
-      unique; names and values are also validated using the foreign
-      data wrapper's validator function, if any.
+      wrapper.  <literal>ADD</literal>, <literal>SET</literal>, <literal>DROP</literal>,
+      <literal>INCLUDE</literal> and <literal>EXCLUDE</literal> specify the action to be
+      performed.  <literal>ADD</literal> is assumed if no operation is explicitly
+      specified.  Option names must be unique; names and values are also validated
+      using the foreign data wrapper's validator function, if any.
+     </para>
+
+     <para>
+      <literal>ADD</literal> can only be used for option names that don't already exist.
+      It sets the specified name's value as provided.
+     </para>
+
+     <para>
+      <literal>SET</literal> can only be used for option names that already exist.
+      It overwrites the entire existing value for the specified option.
+     </para>
+
+     <para>
+      <literal>DROP</literal> can only be used for option names that already exist.
+      It removes the specified name from the list of OPTIONS.
+     </para>
+
+     <para>
+      <literal>INCLUDE</literal> can be used regardless of whether the option name exists or not.
+      If the option name doesn't exist, it acts like <literal>ADD</literal>.
+      If it does exist, it adds the provided values to the existing ones.
+     </para>
+
+     <para>
+      <literal>EXCLUDE</literal> can only be used for option names that already exist.
+      It removes the provided values from the values of the specified option name.
+      If some provided values don't exist in the option values, they won't have any effect.
+      Only the values that exist in the options will be removed.
      </para>
     </listitem>
    </varlistentry>
@@ -157,6 +185,15 @@ ALTER FOREIGN DATA WRAPPER dbi OPTIONS (ADD foo '1', DROP bar);
 </programlisting>
   </para>
 
+  <para>
+   Change a foreign-data wrapper <literal>dbi</literal>, include
+   values in option <literal>foo</literal>, and exclude values from
+   option <literal>bar</literal>:
+<programlisting>
+ALTER FOREIGN DATA WRAPPER dbi OPTIONS (INCLUDE foo '1, 2, 3', EXCLUDE bar '1, 2');
+</programlisting>
+  </para>
+
   <para>
    Change the foreign-data wrapper <literal>dbi</literal> validator
    to <literal>bob.myvalidator</literal>:
diff --git a/doc/src/sgml/ref/alter_foreign_table.sgml b/doc/src/sgml/ref/alter_foreign_table.sgml
index 3cb6f08fcf..104d2dc5ed 100644
--- a/doc/src/sgml/ref/alter_foreign_table.sgml
+++ b/doc/src/sgml/ref/alter_foreign_table.sgml
@@ -42,7 +42,7 @@ ALTER FOREIGN TABLE [ IF EXISTS ] <replaceable class="parameter">name</replaceab
     ALTER [ COLUMN ] <replaceable class="parameter">column_name</replaceable> SET ( <replaceable class="parameter">attribute_option</replaceable> = <replaceable class="parameter">value</replaceable> [, ... ] )
     ALTER [ COLUMN ] <replaceable class="parameter">column_name</replaceable> RESET ( <replaceable class="parameter">attribute_option</replaceable> [, ... ] )
     ALTER [ COLUMN ] <replaceable class="parameter">column_name</replaceable> SET STORAGE { PLAIN | EXTERNAL | EXTENDED | MAIN | DEFAULT }
-    ALTER [ COLUMN ] <replaceable class="parameter">column_name</replaceable> OPTIONS ( [ ADD | SET | DROP ] <replaceable class="parameter">option</replaceable> ['<replaceable class="parameter">value</replaceable>'] [, ... ])
+    ALTER [ COLUMN ] <replaceable class="parameter">column_name</replaceable> OPTIONS ( [ ADD | SET | DROP | INCLUDE | EXCLUDE ] <replaceable class="parameter">option</replaceable> ['<replaceable class="parameter">value</replaceable>'] [, ... ])
     ADD <replaceable class="parameter">table_constraint</replaceable> [ NOT VALID ]
     VALIDATE CONSTRAINT <replaceable class="parameter">constraint_name</replaceable>
     DROP CONSTRAINT [ IF EXISTS ]  <replaceable class="parameter">constraint_name</replaceable> [ RESTRICT | CASCADE ]
@@ -54,7 +54,7 @@ ALTER FOREIGN TABLE [ IF EXISTS ] <replaceable class="parameter">name</replaceab
     INHERIT <replaceable class="parameter">parent_table</replaceable>
     NO INHERIT <replaceable class="parameter">parent_table</replaceable>
     OWNER TO { <replaceable class="parameter">new_owner</replaceable> | CURRENT_ROLE | CURRENT_USER | SESSION_USER }
-    OPTIONS ( [ ADD | SET | DROP ] <replaceable class="parameter">option</replaceable> ['<replaceable class="parameter">value</replaceable>'] [, ... ])
+    OPTIONS ( [ ADD | SET | DROP | INCLUDE | EXCLUDE ] <replaceable class="parameter">option</replaceable> ['<replaceable class="parameter">value</replaceable>'] [, ... ])
 </synopsis>
  </refsynopsisdiv>
 
@@ -266,17 +266,46 @@ ALTER FOREIGN TABLE [ IF EXISTS ] <replaceable class="parameter">name</replaceab
    </varlistentry>
 
    <varlistentry>
-    <term><literal>OPTIONS ( [ ADD | SET | DROP ] <replaceable class="parameter">option</replaceable> ['<replaceable class="parameter">value</replaceable>'] [, ... ] )</literal></term>
+    <term><literal>OPTIONS ( [ ADD | SET | DROP | INCLUDE | EXCLUDE ] <replaceable class="parameter">option</replaceable> ['<replaceable class="parameter">value</replaceable>'] [, ... ] )</literal></term>
     <listitem>
      <para>
       Change options for the foreign table or one of its columns.
-      <literal>ADD</literal>, <literal>SET</literal>, and <literal>DROP</literal>
-      specify the action to be performed.  <literal>ADD</literal> is assumed
-      if no operation is explicitly specified.  Duplicate option names are not
+      <literal>ADD</literal>, <literal>SET</literal>, <literal>DROP</literal>,
+      <literal>INCLUDE</literal> and <literal>EXCLUDE</literal> specify the action to be
+      performed.  <literal>ADD</literal> is assumed if no operation is explicitly
+      specified.  Duplicate option names are not
       allowed (although it's OK for a table option and a column option to have
       the same name).  Option names and values are also validated using the
       foreign data wrapper library.
      </para>
+
+     <para>
+      <literal>ADD</literal> can only be used for option names that don't already exist.
+      It sets the specified name's value as provided.
+     </para>
+
+     <para>
+      <literal>SET</literal> can only be used for option names that already exist.
+      It overwrites the entire existing value for the specified option.
+     </para>
+
+     <para>
+      <literal>DROP</literal> can only be used for option names that already exist.
+      It removes the specified name from the list of OPTIONS.
+     </para>
+
+     <para>
+      <literal>INCLUDE</literal> can be used regardless of whether the option name exists or not.
+      If the option name doesn't exist, it acts like <literal>ADD</literal>.
+      If it does exist, it adds the provided values to the existing ones.
+     </para>
+
+     <para>
+      <literal>EXCLUDE</literal> can only be used for option names that already exist.
+      It removes the provided values from the values of the specified option name.
+      If some provided values don't exist in the option values, they won't have any effect.
+      Only the values that exist in the options will be removed.
+     </para>
     </listitem>
    </varlistentry>
 
@@ -521,7 +550,7 @@ ALTER FOREIGN TABLE distributors ALTER COLUMN street SET NOT NULL;
   <para>
    To change options of a foreign table:
 <programlisting>
-ALTER FOREIGN TABLE myschema.distributors OPTIONS (ADD opt1 'value', SET opt2 'value2', DROP opt3);
+ALTER FOREIGN TABLE myschema.distributors OPTIONS (ADD opt1 'value', SET opt2 'value2', DROP opt3, INCLUDE opt4 'value3, value4', EXCLUDE opt5 'value5, value6');
 </programlisting></para>
 
  </refsect1>
diff --git a/doc/src/sgml/ref/alter_server.sgml b/doc/src/sgml/ref/alter_server.sgml
index 467bf85589..660140ae0b 100644
--- a/doc/src/sgml/ref/alter_server.sgml
+++ b/doc/src/sgml/ref/alter_server.sgml
@@ -22,7 +22,7 @@ PostgreSQL documentation
  <refsynopsisdiv>
 <synopsis>
 ALTER SERVER <replaceable class="parameter">name</replaceable> [ VERSION '<replaceable class="parameter">new_version</replaceable>' ]
-    [ OPTIONS ( [ ADD | SET | DROP ] <replaceable class="parameter">option</replaceable> ['<replaceable class="parameter">value</replaceable>'] [, ... ] ) ]
+    [ OPTIONS ( [ ADD | SET | DROP | INCLUDE | EXCLUDE ] <replaceable class="parameter">option</replaceable> ['<replaceable class="parameter">value</replaceable>'] [, ... ] ) ]
 ALTER SERVER <replaceable class="parameter">name</replaceable> OWNER TO { <replaceable>new_owner</replaceable> | CURRENT_ROLE | CURRENT_USER | SESSION_USER }
 ALTER SERVER <replaceable class="parameter">name</replaceable> RENAME TO <replaceable>new_name</replaceable>
 </synopsis>
@@ -71,15 +71,43 @@ ALTER SERVER <replaceable class="parameter">name</replaceable> RENAME TO <replac
    </varlistentry>
 
    <varlistentry>
-    <term><literal>OPTIONS ( [ ADD | SET | DROP ] <replaceable class="parameter">option</replaceable> ['<replaceable class="parameter">value</replaceable>'] [, ... ] )</literal></term>
+    <term><literal>OPTIONS ( [ ADD | SET | DROP | INCLUDE | EXCLUDE ] <replaceable class="parameter">option</replaceable> ['<replaceable class="parameter">value</replaceable>'] [, ... ] )</literal></term>
     <listitem>
      <para>
       Change options for the
-      server.  <literal>ADD</literal>, <literal>SET</literal>, and <literal>DROP</literal>
-      specify the action to be performed.  <literal>ADD</literal> is assumed
-      if no operation is explicitly specified.  Option names must be
-      unique; names and values are also validated using the server's
-      foreign-data wrapper library.
+      server.  <literal>ADD</literal>, <literal>SET</literal>, <literal>DROP</literal>,
+      <literal>INCLUDE</literal> and <literal>EXCLUDE</literal> specify the action to be
+      performed.  <literal>ADD</literal> is assumed if no operation is explicitly
+      specified.  Option names must be unique; names and values are also validated
+      using the server's foreign-data wrapper library.
+     </para>
+
+     <para>
+      <literal>ADD</literal> can only be used for option names that don't already exist.
+      It sets the specified name's value as provided.
+     </para>
+
+     <para>
+      <literal>SET</literal> can only be used for option names that already exist.
+      It overwrites the entire existing value for the specified option.
+     </para>
+
+     <para>
+      <literal>DROP</literal> can only be used for option names that already exist.
+      It removes the specified name from the list of OPTIONS.
+     </para>
+
+     <para>
+      <literal>INCLUDE</literal> can be used regardless of whether the option name exists or not.
+      If the option name doesn't exist, it acts like <literal>ADD</literal>.
+      If it does exist, it adds the provided values to the existing ones.
+     </para>
+
+     <para>
+      <literal>EXCLUDE</literal> can only be used for option names that already exist.
+      It removes the provided values from the values of the specified option name.
+      If some provided values don't exist in the option values, they won't have any effect.
+      Only the values that exist in the options will be removed.
      </para>
     </listitem>
    </varlistentry>
@@ -114,6 +142,23 @@ ALTER SERVER foo OPTIONS (host 'foo', dbname 'foodb');
 </programlisting>
   </para>
 
+  <para>
+   Suppose a server already contains few extensions then to add 
+   more extensions into existing values use
+   Alter server <literal>foo</literal>, include extensions:
+<programlisting>
+ALTER SERVER foo OPTIONS (INCLUDE extensions 'ext1, ext2, ext3');
+</programlisting>
+  </para>
+
+  <para>
+   To remove some selected values from extensions use
+   Alter server <literal>foo</literal>, exclude extensions:
+<programlisting>
+ALTER SERVER foo OPTIONS (EXCLUDE extensions 'ext1, ext3');
+</programlisting>
+  </para>
+
   <para>
    Alter server <literal>foo</literal>, change version,
    change <literal>host</literal> option:
diff --git a/doc/src/sgml/ref/alter_user_mapping.sgml b/doc/src/sgml/ref/alter_user_mapping.sgml
index ee5aee9bc9..4f2260b6e2 100644
--- a/doc/src/sgml/ref/alter_user_mapping.sgml
+++ b/doc/src/sgml/ref/alter_user_mapping.sgml
@@ -23,7 +23,7 @@ PostgreSQL documentation
 <synopsis>
 ALTER USER MAPPING FOR { <replaceable class="parameter">user_name</replaceable> | USER | CURRENT_ROLE | CURRENT_USER | SESSION_USER | PUBLIC }
     SERVER <replaceable class="parameter">server_name</replaceable>
-    OPTIONS ( [ ADD | SET | DROP ] <replaceable class="parameter">option</replaceable> ['<replaceable class="parameter">value</replaceable>'] [, ... ] )
+    OPTIONS ( [ ADD | SET | DROP | INCLUDE | EXCLUDE ] <replaceable class="parameter">option</replaceable> ['<replaceable class="parameter">value</replaceable>'] [, ... ] )
 </synopsis>
  </refsynopsisdiv>
 
@@ -69,16 +69,44 @@ ALTER USER MAPPING FOR { <replaceable class="parameter">user_name</replaceable>
    </varlistentry>
 
    <varlistentry>
-    <term><literal>OPTIONS ( [ ADD | SET | DROP ] <replaceable class="parameter">option</replaceable> ['<replaceable class="parameter">value</replaceable>'] [, ... ] )</literal></term>
+    <term><literal>OPTIONS ( [ ADD | SET | DROP | INCLUDE | EXCLUDE ] <replaceable class="parameter">option</replaceable> ['<replaceable class="parameter">value</replaceable>'] [, ... ] )</literal></term>
     <listitem>
      <para>
       Change options for the user mapping. The new options override
       any previously specified
-      options.  <literal>ADD</literal>, <literal>SET</literal>, and <literal>DROP</literal>
-      specify the action to be performed.  <literal>ADD</literal> is assumed
-      if no operation is explicitly specified.  Option names must be
-      unique; options are also validated by the server's foreign-data
-      wrapper.
+      options.  <literal>ADD</literal>, <literal>SET</literal>, <literal>DROP</literal>,
+      <literal>INCLUDE</literal> and <literal>EXCLUDE</literal> specify the action to be
+      performed.  <literal>ADD</literal> is assumed if no operation is explicitly
+      specified.  Option names must be unique; options are also validated
+      by the server's foreign-data wrapper.
+     </para>
+
+     <para>
+      <literal>ADD</literal> can only be used for option names that don't already exist.
+      It sets the specified name's value as provided.
+     </para>
+
+     <para>
+      <literal>SET</literal> can only be used for option names that already exist.
+      It overwrites the entire existing value for the specified option.
+     </para>
+
+     <para>
+      <literal>DROP</literal> can only be used for option names that already exist.
+      It removes the specified name from the list of OPTIONS.
+     </para>
+
+     <para>
+      <literal>INCLUDE</literal> can be used regardless of whether the option name exists or not.
+      If the option name doesn't exist, it acts like <literal>ADD</literal>.
+      If it does exist, it adds the provided values to the existing ones.
+     </para>
+
+     <para>
+      <literal>EXCLUDE</literal> can only be used for option names that already exist.
+      It removes the provided values from the values of the specified option name.
+      If some provided values don't exist in the option values, they won't have any effect.
+      Only the values that exist in the options will be removed.
      </para>
     </listitem>
    </varlistentry>
diff --git a/src/backend/commands/foreigncmds.c b/src/backend/commands/foreigncmds.c
index cf61bbac1f..c63a814bdb 100644
--- a/src/backend/commands/foreigncmds.c
+++ b/src/backend/commands/foreigncmds.c
@@ -38,6 +38,7 @@
 #include "utils/lsyscache.h"
 #include "utils/rel.h"
 #include "utils/syscache.h"
+#include "utils/varlena.h"
 
 
 typedef struct
@@ -95,10 +96,10 @@ optionListToArray(List *options)
 
 /*
  * Transform a list of DefElem into text array format.  This is substantially
- * the same thing as optionListToArray(), except we recognize SET/ADD/DROP
- * actions for modifying an existing list of options, which is passed in
- * Datum form as oldOptions.  Also, if fdwvalidator isn't InvalidOid
- * it specifies a validator function to call on the result.
+ * the same thing as optionListToArray(), except we recognize
+ * SET/ADD/DROP/INCLUDE/EXCLUDE actions for modifying an existing list of
+ * options, which is passed in Datum form as oldOptions.  Also, if fdwvalidator
+ * isn't InvalidOid it specifies a validator function to call on the result.
  *
  * Returns the array in the form of a Datum, or PointerGetDatum(NULL)
  * if the list is empty.
@@ -159,6 +160,78 @@ transformGenericOptions(Oid catalogId,
 				lfirst(cell) = od;
 				break;
 
+			case DEFELEM_INCLUDE:
+				if (!cell)
+					resultOptions = lappend(resultOptions, od);
+				else
+				{
+					StringInfo	s;
+
+					s = makeStringInfo();
+
+					appendStringInfoString(s, defGetString(lfirst(cell)));
+
+					if (strlen(s->data) && strlen(defGetString(od)))
+						appendStringInfoChar(s, ',');
+
+					appendStringInfoString(s, defGetString(od));
+
+					((String *) od->arg)->sval = s->data;
+					lfirst(cell) = od;
+				}
+				break;
+
+			case DEFELEM_EXCLUDE:
+				if (!cell)
+					ereport(ERROR,
+							(errcode(ERRCODE_UNDEFINED_OBJECT),
+							 errmsg("option \"%s\" not found",
+									od->defname)));
+				else
+				{
+					StringInfo	s;
+					List	   *currentValues;
+					List	   *discardValues;
+					ListCell   *ev;
+					ListCell   *dv;
+
+					SplitIdentifierString(defGetString(lfirst(cell)), ',', &currentValues);
+					SplitIdentifierString(defGetString(od), ',', &discardValues);
+
+					foreach(ev, currentValues)
+					{
+						foreach(dv, discardValues)
+						{
+							if (strcmp((char *) lfirst(ev), (char *) lfirst(dv)) == 0)
+							{
+								currentValues = foreach_delete_current(currentValues, ev);
+								break;
+							}
+						}
+					}
+
+					s = makeStringInfo();
+
+					foreach(ev, currentValues)
+					{
+						appendStringInfoString(s, (char *) lfirst(ev));
+						appendStringInfoChar(s, ',');
+					}
+
+					if (s->len > 0)
+					{
+						s->data[s->len - 1] = '\0';
+						s->len = s->len - 1;
+					}
+
+					list_free(currentValues);
+					list_free(discardValues);
+
+					((String *) od->arg)->sval = s->data;
+					lfirst(cell) = od;
+				}
+				break;
+
 			case DEFELEM_ADD:
 			case DEFELEM_UNSPEC:
 				if (cell)
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index e8b619926e..b38dfebf40 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -5531,6 +5531,16 @@ alter_generic_option_elem:
 				{
 					$$ = makeDefElemExtended(NULL, $2, NULL, DEFELEM_DROP, @2);
 				}
+			| INCLUDE generic_option_elem
+				{
+					$$ = $2;
+					$$->defaction = DEFELEM_INCLUDE;
+				}
+			| EXCLUDE generic_option_elem
+				{
+					$$ = $2;
+					$$->defaction = DEFELEM_EXCLUDE;
+				}
 		;
 
 generic_option_elem:
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index af80a5d38e..7a8ac63c6e 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -806,6 +806,8 @@ typedef enum DefElemAction
 	DEFELEM_SET,
 	DEFELEM_ADD,
 	DEFELEM_DROP,
+	DEFELEM_INCLUDE,
+	DEFELEM_EXCLUDE
 } DefElemAction;
 
 typedef struct DefElem
-- 
2.41.0

Reply via email to