Proposal to have INCLUDE/EXCLUDE options for altering option values
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)
Attachments:
v1-0001-Enhance-CREATE-ALTER-statement-with-extended-supp.patchapplication/x-patch; name=v1-0001-Enhance-CREATE-ALTER-statement-with-extended-supp.patchDownload
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)), ',', ¤tValues);
+ 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
Added a CF entry for the same - https://commitfest.postgresql.org/48/4955/
Regards
Ayush Vatsa
Amazon Web Services (AWS)
On Fri, 26 Apr 2024 at 11:05, Ayush Vatsa <ayushvatsa1810@gmail.com> wrote:
Show quoted text
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)
Hi PostgreSQL Community,
I would like to provide an update on the patch I previously submitted,
along with a clearer explanation of the issue it addresses
and the improvements it introduces.
Current Issue:
PostgreSQL currently supports several options with actions like ADD, SET,
and DROP for foreign servers, user mappings,
foreign tables, etc. For example, the syntax for modifying a server option
is as follows:
ALTER SERVER name [ VERSION 'new_version' ]
[ OPTIONS ( [ ADD | SET | DROP ] option ['value'] [, ... ] ) ]
However, there is a limitations with the current approach:
If a user wants to add new values to an existing option, they can use:
ALTER SERVER foo OPTIONS (ADD extensions 'ext1,ext2');
But when modifying existing values, users must repeat all the existing
values along with the new ones:
ALTER SERVER foo OPTIONS (ADD extensions 'ext1,ext2');
ALTER SERVER foo OPTIONS (SET extensions 'ext1,ext2,ext3');
This repetition can be cumbersome and error-prone when there are large
number of comma separated values.
Proposed Solution:
To address this, I propose introducing two new actions: APPEND and REMOVE.
These will allow users to modify existing
values without needing to repeat all current entries.
ALTER SERVER foo OPTIONS (APPEND extensions 'ext4,ext5,ext6');
--extensions will be like 'ext1,ext2,ext3,ext4,ext5,ext6'
ALTER SERVER foo OPTIONS (REMOVE extensions 'ext1');
--extensions will be like 'ext2,ext4,ext5,ext6'
I had an off-site discussion with Nathan Bossart (bossartn) and have
incorporated his feedback about changing actions
name to be more clear into the updated patch. Furthermore, I noticed that
the documentation for the existing actions
could be clearer, so I have revised it as well. The documentation for the
newly proposed actions is included in a separate patch.
Looking forward to your comments and feedback.
Regards
Ayush Vatsa
AWS
Attachments:
v2-0001-Enhance-ALTER-statement-with-extended-support-for.patchapplication/octet-stream; name=v2-0001-Enhance-ALTER-statement-with-extended-support-for.patchDownload
From 3c33c51d5b87beb5daa0c4104628622c0393bcf7 Mon Sep 17 00:00:00 2001
From: Ayush Vatsa <ayuvatsa@amazon.com>
Date: Mon, 12 Aug 2024 01:28:13 +0530
Subject: [PATCH v2 1/2] Enhance ALTER statement with extended support for
APPEND and REMOVE actions to modify existing options
---
.../postgres_fdw/expected/postgres_fdw.out | 10 +++
contrib/postgres_fdw/sql/postgres_fdw.sql | 3 +
src/backend/commands/foreigncmds.c | 85 +++++++++++++++++--
src/backend/parser/gram.y | 18 +++-
src/common/stringinfo.c | 10 +++
src/include/lib/stringinfo.h | 8 ++
src/include/nodes/parsenodes.h | 9 +-
src/include/parser/kwlist.h | 2 +
8 files changed, 132 insertions(+), 13 deletions(-)
diff --git a/contrib/postgres_fdw/expected/postgres_fdw.out b/contrib/postgres_fdw/expected/postgres_fdw.out
index 212434711e..85281cf6f8 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 (APPEND 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 (APPEND 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 (REMOVE 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 371e131933..8a5ef92fd4 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 (APPEND extensions 'foo; bar');
-- OK but gets a warning
ALTER SERVER testserver1 OPTIONS (ADD extensions 'foo, bar');
+ALTER SERVER testserver1 OPTIONS (APPEND extensions 'ext1, ext2');
+ALTER SERVER testserver1 OPTIONS (REMOVE extensions 'foo, ext2, ext3');
ALTER SERVER testserver1 OPTIONS (DROP extensions);
ALTER USER MAPPING FOR public SERVER testserver1
diff --git a/src/backend/commands/foreigncmds.c b/src/backend/commands/foreigncmds.c
index cf61bbac1f..8df8774339 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/APPEND/REMOVE 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.
@@ -134,10 +135,10 @@ transformGenericOptions(Oid catalogId,
}
/*
- * It is possible to perform multiple SET/DROP actions on the same
- * option. The standard permits this, as long as the options to be
- * added are unique. Note that an unspecified action is taken to be
- * ADD.
+ * It is possible to perform multiple SET/DROP/APPEND/REMOVE actions
+ * on the same option. The standard permits this, as long as the
+ * options to be added are unique. Note that an unspecified action is
+ * taken to be ADD.
*/
switch (od->defaction)
{
@@ -159,6 +160,74 @@ transformGenericOptions(Oid catalogId,
lfirst(cell) = od;
break;
+ case DEFELEM_APPEND:
+ /* Should act like action ADD */
+ if (!cell)
+ resultOptions = lappend(resultOptions, od);
+ else
+ {
+ StringInfo str = makeStringInfo();
+
+ appendStringInfoString(str, defGetString(lfirst(cell)));
+
+ if (strlen(str->data) && strlen(defGetString(od)))
+ appendStringInfoChar(str, ',');
+
+ appendStringInfoString(str, defGetString(od));
+
+ strVal(od->arg) = str->data;
+ lfirst(cell) = od;
+ }
+ break;
+
+ case DEFELEM_REMOVE:
+ if (!cell)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("option \"%s\" not found",
+ od->defname)));
+ else
+ {
+ StringInfo str;
+ List *currentValues;
+ List *discardValues;
+ ListCell *cv;
+ ListCell *dv;
+
+ SplitIdentifierString(defGetString(lfirst(cell)), ',', ¤tValues);
+ SplitIdentifierString(defGetString(od), ',', &discardValues);
+
+ foreach(cv, currentValues)
+ {
+ foreach(dv, discardValues)
+ {
+ if (strcmp((char *) lfirst(cv), (char *) lfirst(dv)) == 0)
+ {
+ currentValues = foreach_delete_current(currentValues, cv);
+ break;
+ }
+ }
+ }
+
+ str = makeStringInfo();
+
+ foreach(cv, currentValues)
+ {
+ appendStringInfoString(str, (char *) lfirst(cv));
+ appendStringInfoChar(str, ',');
+ }
+
+ /* Removing last added , */
+ trimLastNCharsStringInfo(str, 1);
+
+ list_free(currentValues);
+ list_free(discardValues);
+
+ strVal(od->arg) = str->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 a043fd4c66..8f55863944 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -706,7 +706,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
/* ordinary key words in alphabetical order */
%token <keyword> ABORT_P ABSENT ABSOLUTE_P ACCESS ACTION ADD_P ADMIN AFTER
- AGGREGATE ALL ALSO ALTER ALWAYS ANALYSE ANALYZE AND ANY ARRAY AS ASC
+ AGGREGATE ALL ALSO ALTER ALWAYS ANALYSE ANALYZE AND ANY APPEND ARRAY AS ASC
ASENSITIVE ASSERTION ASSIGNMENT ASYMMETRIC ATOMIC AT ATTACH ATTRIBUTE AUTHORIZATION
BACKWARD BEFORE BEGIN_P BETWEEN BIGINT BINARY BIT
@@ -771,7 +771,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
QUOTE QUOTES
RANGE READ REAL REASSIGN RECHECK RECURSIVE REF_P REFERENCES REFERENCING
- REFRESH REINDEX RELATIVE_P RELEASE RENAME REPEATABLE REPLACE REPLICA
+ REFRESH REINDEX RELATIVE_P RELEASE REMOVE RENAME REPEATABLE REPLACE REPLICA
RESET RESTART RESTRICT RETURN RETURNING RETURNS REVOKE RIGHT ROLE ROLLBACK ROLLUP
ROUTINE ROUTINES ROW ROWS RULE
@@ -5485,6 +5485,16 @@ alter_generic_option_elem:
{
$$ = makeDefElemExtended(NULL, $2, NULL, DEFELEM_DROP, @2);
}
+ | APPEND generic_option_elem
+ {
+ $$ = $2;
+ $$->defaction = DEFELEM_APPEND;
+ }
+ | REMOVE generic_option_elem
+ {
+ $$ = $2;
+ $$->defaction = DEFELEM_REMOVE;
+ }
;
generic_option_elem:
@@ -17576,6 +17586,7 @@ unreserved_keyword:
| ALSO
| ALTER
| ALWAYS
+ | APPEND
| ASENSITIVE
| ASSERTION
| ASSIGNMENT
@@ -17792,6 +17803,7 @@ unreserved_keyword:
| REINDEX
| RELATIVE_P
| RELEASE
+ | REMOVE
| RENAME
| REPEATABLE
| REPLACE
@@ -18121,6 +18133,7 @@ bare_label_keyword:
| ANALYZE
| AND
| ANY
+ | APPEND
| ASC
| ASENSITIVE
| ASSERTION
@@ -18423,6 +18436,7 @@ bare_label_keyword:
| REINDEX
| RELATIVE_P
| RELEASE
+ | REMOVE
| RENAME
| REPEATABLE
| REPLACE
diff --git a/src/common/stringinfo.c b/src/common/stringinfo.c
index eb9d6502fc..93d403a7af 100644
--- a/src/common/stringinfo.c
+++ b/src/common/stringinfo.c
@@ -85,6 +85,16 @@ resetStringInfo(StringInfo str)
str->cursor = 0;
}
+void
+trimLastNCharsStringInfo(StringInfo str, const int n)
+{
+ /* don't allow trimming of read-only StringInfos */
+ Assert(str->maxlen != 0);
+
+ str->len = (str->len >= n) ? (str->len - n) : 0;
+ str->data[str->len] = '\0';
+}
+
/*
* appendStringInfo
*
diff --git a/src/include/lib/stringinfo.h b/src/include/lib/stringinfo.h
index cd9632e3fc..ac7e62033c 100644
--- a/src/include/lib/stringinfo.h
+++ b/src/include/lib/stringinfo.h
@@ -162,6 +162,14 @@ initStringInfoFromString(StringInfo str, char *data, int len)
*/
extern void resetStringInfo(StringInfo str);
+/*------------------------
+ * trimLastNCharsStringInfo
+ * Clears the last 'n' characters of the StringInfo. If the
+ * length of the StringInfo is less than 'n', the entire StringInfo
+ * is cleared. The StringInfo object remains valid after this operation.
+ */
+extern void trimLastNCharsStringInfo(StringInfo str, const int n);
+
/*------------------------
* appendStringInfo
* Format text data under the control of fmt (an sprintf-style format string)
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 85a62b538e..d928dacd62 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -794,8 +794,8 @@ typedef struct IndexElem
* DefElem - a generic "name = value" option definition
*
* In some contexts the name can be qualified. Also, certain SQL commands
- * allow a SET/ADD/DROP action to be attached to option settings, so it's
- * convenient to carry a field for that too. (Note: currently, it is our
+ * allow a SET/ADD/DROP/APPEND/REMOVE action to be attached to option settings,
+ * so it's convenient to carry a field for that too. (Note: currently, it is our
* practice that the grammar allows namespace and action only in statements
* where they are relevant; C code can just ignore those fields in other
* statements.)
@@ -806,6 +806,8 @@ typedef enum DefElemAction
DEFELEM_SET,
DEFELEM_ADD,
DEFELEM_DROP,
+ DEFELEM_APPEND,
+ DEFELEM_REMOVE
} DefElemAction;
typedef struct DefElem
@@ -815,7 +817,8 @@ typedef struct DefElem
char *defname;
Node *arg; /* typically Integer, Float, String, or
* TypeName */
- DefElemAction defaction; /* unspecified action, or SET/ADD/DROP */
+ DefElemAction defaction; /* unspecified action, or
+ * SET/ADD/DROP/APPEND/REMOVE */
ParseLoc location; /* token location, or -1 if unknown */
} DefElem;
diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h
index f7fe834cf4..c7c465e1e5 100644
--- a/src/include/parser/kwlist.h
+++ b/src/include/parser/kwlist.h
@@ -42,6 +42,7 @@ PG_KEYWORD("analyse", ANALYSE, RESERVED_KEYWORD, BARE_LABEL) /* British spellin
PG_KEYWORD("analyze", ANALYZE, RESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("and", AND, RESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("any", ANY, RESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("append", APPEND, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("array", ARRAY, RESERVED_KEYWORD, AS_LABEL)
PG_KEYWORD("as", AS, RESERVED_KEYWORD, AS_LABEL)
PG_KEYWORD("asc", ASC, RESERVED_KEYWORD, BARE_LABEL)
@@ -372,6 +373,7 @@ PG_KEYWORD("refresh", REFRESH, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("reindex", REINDEX, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("relative", RELATIVE_P, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("release", RELEASE, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("remove", REMOVE, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("rename", RENAME, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("repeatable", REPEATABLE, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("replace", REPLACE, UNRESERVED_KEYWORD, BARE_LABEL)
--
2.41.0
v2-0002-fix-alter_server-alter_foreign_data_wrapper-alter.patchapplication/octet-stream; name=v2-0002-fix-alter_server-alter_foreign_data_wrapper-alter.patchDownload
From a27af2ef1cd72ae1eeac635823fc2036051c7213 Mon Sep 17 00:00:00 2001
From: Ayush Vatsa <ayuvatsa@amazon.com>
Date: Mon, 12 Aug 2024 01:29:46 +0530
Subject: [PATCH v2 2/2] fix alter_server, alter_foreign_data_wrapper,
alter_foreign_table and alter_user_mapping documentation
---
.../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 ++++++++++---
4 files changed, 167 insertions(+), 28 deletions(-)
diff --git a/doc/src/sgml/ref/alter_foreign_data_wrapper.sgml b/doc/src/sgml/ref/alter_foreign_data_wrapper.sgml
index dc0957d965..c20cc479d9 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 | APPEND | REMOVE ] <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 | APPEND | REMOVE ] <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>APPEND</literal> and <literal>REMOVE</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>APPEND</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>REMOVE</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>, add
+ values in option <literal>foo</literal>, and remove values from
+ option <literal>bar</literal>:
+<programlisting>
+ALTER FOREIGN DATA WRAPPER dbi OPTIONS (APPEND foo '1, 2, 3', REMOVE 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..b417f74026 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 | APPEND | REMOVE ] <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 | APPEND | REMOVE ] <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 | APPEND | REMOVE ] <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>APPEND</literal> and <literal>REMOVE</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>APPEND</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>REMOVE</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, APPEND opt4 'value3, value4', REMOVE 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..953fdf1bc2 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 | APPEND | REMOVE ] <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 | APPEND | REMOVE ] <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>APPEND</literal> and <literal>REMOVE</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>APPEND</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>REMOVE</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>, append extensions:
+<programlisting>
+ALTER SERVER foo OPTIONS (APPEND extensions 'ext1, ext2, ext3');
+</programlisting>
+ </para>
+
+ <para>
+ To remove some selected values from extensions use
+ Alter server <literal>foo</literal>, remove extensions:
+<programlisting>
+ALTER SERVER foo OPTIONS (REMOVE 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..ff420f9c72 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 | APPEND | REMOVE ] <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 | APPEND | REMOVE ] <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>APPEND</literal> and <literal>REMOVE</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>APPEND</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>REMOVE</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>
--
2.41.0
Hello PostgreSQL Community,
I noticed that my last commit needs rebase through cfbot -
http://cfbot.cputube.org/ayush-vatsa.html
PFA the rebased patch for the same.
Regards
Ayush Vatsa
AWS
Attachments:
v3-0002-fix-alter_server-alter_foreign_data_wrapper-alter.patchapplication/octet-stream; name=v3-0002-fix-alter_server-alter_foreign_data_wrapper-alter.patchDownload
From 5e5bac6b06d5ad6887710faa402365d7a361e8f8 Mon Sep 17 00:00:00 2001
From: Ayush Vatsa <ayuvatsa@amazon.com>
Date: Mon, 12 Aug 2024 01:29:46 +0530
Subject: [PATCH v3 2/2] fix alter_server, alter_foreign_data_wrapper,
alter_foreign_table and alter_user_mapping documentation
---
.../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 ++++++++++---
4 files changed, 167 insertions(+), 28 deletions(-)
diff --git a/doc/src/sgml/ref/alter_foreign_data_wrapper.sgml b/doc/src/sgml/ref/alter_foreign_data_wrapper.sgml
index dc0957d965..c20cc479d9 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 | APPEND | REMOVE ] <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 | APPEND | REMOVE ] <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>APPEND</literal> and <literal>REMOVE</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>APPEND</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>REMOVE</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>, add
+ values in option <literal>foo</literal>, and remove values from
+ option <literal>bar</literal>:
+<programlisting>
+ALTER FOREIGN DATA WRAPPER dbi OPTIONS (APPEND foo '1, 2, 3', REMOVE 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..b417f74026 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 | APPEND | REMOVE ] <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 | APPEND | REMOVE ] <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 | APPEND | REMOVE ] <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>APPEND</literal> and <literal>REMOVE</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>APPEND</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>REMOVE</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, APPEND opt4 'value3, value4', REMOVE 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..953fdf1bc2 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 | APPEND | REMOVE ] <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 | APPEND | REMOVE ] <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>APPEND</literal> and <literal>REMOVE</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>APPEND</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>REMOVE</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>, append extensions:
+<programlisting>
+ALTER SERVER foo OPTIONS (APPEND extensions 'ext1, ext2, ext3');
+</programlisting>
+ </para>
+
+ <para>
+ To remove some selected values from extensions use
+ Alter server <literal>foo</literal>, remove extensions:
+<programlisting>
+ALTER SERVER foo OPTIONS (REMOVE 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..ff420f9c72 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 | APPEND | REMOVE ] <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 | APPEND | REMOVE ] <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>APPEND</literal> and <literal>REMOVE</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>APPEND</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>REMOVE</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>
--
2.41.0
v3-0001-Enhance-ALTER-statement-with-extended-support-for.patchapplication/octet-stream; name=v3-0001-Enhance-ALTER-statement-with-extended-support-for.patchDownload
From 1df5566f7666fe32045f8ec1cb693f1f9b77af31 Mon Sep 17 00:00:00 2001
From: Ayush Vatsa <ayuvatsa@amazon.com>
Date: Mon, 12 Aug 2024 01:28:13 +0530
Subject: [PATCH v3 1/2] Enhance ALTER statement with extended support for
APPEND and REMOVE actions to modify existing options
---
.../postgres_fdw/expected/postgres_fdw.out | 10 +++
contrib/postgres_fdw/sql/postgres_fdw.sql | 3 +
src/backend/commands/foreigncmds.c | 85 +++++++++++++++++--
src/backend/parser/gram.y | 18 +++-
src/common/stringinfo.c | 10 +++
src/include/lib/stringinfo.h | 8 ++
src/include/nodes/parsenodes.h | 9 +-
src/include/parser/kwlist.h | 2 +
8 files changed, 132 insertions(+), 13 deletions(-)
diff --git a/contrib/postgres_fdw/expected/postgres_fdw.out b/contrib/postgres_fdw/expected/postgres_fdw.out
index f3eb055e2c..dfe84aacab 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 (APPEND 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 (APPEND 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 (REMOVE 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 0734716ad9..67b9390aba 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 (APPEND extensions 'foo; bar');
-- OK but gets a warning
ALTER SERVER testserver1 OPTIONS (ADD extensions 'foo, bar');
+ALTER SERVER testserver1 OPTIONS (APPEND extensions 'ext1, ext2');
+ALTER SERVER testserver1 OPTIONS (REMOVE extensions 'foo, ext2, ext3');
ALTER SERVER testserver1 OPTIONS (DROP extensions);
ALTER USER MAPPING FOR public SERVER testserver1
diff --git a/src/backend/commands/foreigncmds.c b/src/backend/commands/foreigncmds.c
index cf61bbac1f..8df8774339 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/APPEND/REMOVE 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.
@@ -134,10 +135,10 @@ transformGenericOptions(Oid catalogId,
}
/*
- * It is possible to perform multiple SET/DROP actions on the same
- * option. The standard permits this, as long as the options to be
- * added are unique. Note that an unspecified action is taken to be
- * ADD.
+ * It is possible to perform multiple SET/DROP/APPEND/REMOVE actions
+ * on the same option. The standard permits this, as long as the
+ * options to be added are unique. Note that an unspecified action is
+ * taken to be ADD.
*/
switch (od->defaction)
{
@@ -159,6 +160,74 @@ transformGenericOptions(Oid catalogId,
lfirst(cell) = od;
break;
+ case DEFELEM_APPEND:
+ /* Should act like action ADD */
+ if (!cell)
+ resultOptions = lappend(resultOptions, od);
+ else
+ {
+ StringInfo str = makeStringInfo();
+
+ appendStringInfoString(str, defGetString(lfirst(cell)));
+
+ if (strlen(str->data) && strlen(defGetString(od)))
+ appendStringInfoChar(str, ',');
+
+ appendStringInfoString(str, defGetString(od));
+
+ strVal(od->arg) = str->data;
+ lfirst(cell) = od;
+ }
+ break;
+
+ case DEFELEM_REMOVE:
+ if (!cell)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("option \"%s\" not found",
+ od->defname)));
+ else
+ {
+ StringInfo str;
+ List *currentValues;
+ List *discardValues;
+ ListCell *cv;
+ ListCell *dv;
+
+ SplitIdentifierString(defGetString(lfirst(cell)), ',', ¤tValues);
+ SplitIdentifierString(defGetString(od), ',', &discardValues);
+
+ foreach(cv, currentValues)
+ {
+ foreach(dv, discardValues)
+ {
+ if (strcmp((char *) lfirst(cv), (char *) lfirst(dv)) == 0)
+ {
+ currentValues = foreach_delete_current(currentValues, cv);
+ break;
+ }
+ }
+ }
+
+ str = makeStringInfo();
+
+ foreach(cv, currentValues)
+ {
+ appendStringInfoString(str, (char *) lfirst(cv));
+ appendStringInfoChar(str, ',');
+ }
+
+ /* Removing last added , */
+ trimLastNCharsStringInfo(str, 1);
+
+ list_free(currentValues);
+ list_free(discardValues);
+
+ strVal(od->arg) = str->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 84cef57a70..ce7abee78d 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -703,7 +703,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
/* ordinary key words in alphabetical order */
%token <keyword> ABORT_P ABSENT ABSOLUTE_P ACCESS ACTION ADD_P ADMIN AFTER
- AGGREGATE ALL ALSO ALTER ALWAYS ANALYSE ANALYZE AND ANY ARRAY AS ASC
+ AGGREGATE ALL ALSO ALTER ALWAYS ANALYSE ANALYZE AND ANY APPEND ARRAY AS ASC
ASENSITIVE ASSERTION ASSIGNMENT ASYMMETRIC ATOMIC AT ATTACH ATTRIBUTE AUTHORIZATION
BACKWARD BEFORE BEGIN_P BETWEEN BIGINT BINARY BIT
@@ -768,7 +768,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
QUOTE QUOTES
RANGE READ REAL REASSIGN RECURSIVE REF_P REFERENCES REFERENCING
- REFRESH REINDEX RELATIVE_P RELEASE RENAME REPEATABLE REPLACE REPLICA
+ REFRESH REINDEX RELATIVE_P RELEASE REMOVE RENAME REPEATABLE REPLACE REPLICA
RESET RESTART RESTRICT RETURN RETURNING RETURNS REVOKE RIGHT ROLE ROLLBACK ROLLUP
ROUTINE ROUTINES ROW ROWS RULE
@@ -5433,6 +5433,16 @@ alter_generic_option_elem:
{
$$ = makeDefElemExtended(NULL, $2, NULL, DEFELEM_DROP, @2);
}
+ | APPEND generic_option_elem
+ {
+ $$ = $2;
+ $$->defaction = DEFELEM_APPEND;
+ }
+ | REMOVE generic_option_elem
+ {
+ $$ = $2;
+ $$->defaction = DEFELEM_REMOVE;
+ }
;
generic_option_elem:
@@ -17506,6 +17516,7 @@ unreserved_keyword:
| ALSO
| ALTER
| ALWAYS
+ | APPEND
| ASENSITIVE
| ASSERTION
| ASSIGNMENT
@@ -17720,6 +17731,7 @@ unreserved_keyword:
| REINDEX
| RELATIVE_P
| RELEASE
+ | REMOVE
| RENAME
| REPEATABLE
| REPLACE
@@ -18048,6 +18060,7 @@ bare_label_keyword:
| ANALYZE
| AND
| ANY
+ | APPEND
| ASC
| ASENSITIVE
| ASSERTION
@@ -18348,6 +18361,7 @@ bare_label_keyword:
| REINDEX
| RELATIVE_P
| RELEASE
+ | REMOVE
| RENAME
| REPEATABLE
| REPLACE
diff --git a/src/common/stringinfo.c b/src/common/stringinfo.c
index eb9d6502fc..93d403a7af 100644
--- a/src/common/stringinfo.c
+++ b/src/common/stringinfo.c
@@ -85,6 +85,16 @@ resetStringInfo(StringInfo str)
str->cursor = 0;
}
+void
+trimLastNCharsStringInfo(StringInfo str, const int n)
+{
+ /* don't allow trimming of read-only StringInfos */
+ Assert(str->maxlen != 0);
+
+ str->len = (str->len >= n) ? (str->len - n) : 0;
+ str->data[str->len] = '\0';
+}
+
/*
* appendStringInfo
*
diff --git a/src/include/lib/stringinfo.h b/src/include/lib/stringinfo.h
index cd9632e3fc..ac7e62033c 100644
--- a/src/include/lib/stringinfo.h
+++ b/src/include/lib/stringinfo.h
@@ -162,6 +162,14 @@ initStringInfoFromString(StringInfo str, char *data, int len)
*/
extern void resetStringInfo(StringInfo str);
+/*------------------------
+ * trimLastNCharsStringInfo
+ * Clears the last 'n' characters of the StringInfo. If the
+ * length of the StringInfo is less than 'n', the entire StringInfo
+ * is cleared. The StringInfo object remains valid after this operation.
+ */
+extern void trimLastNCharsStringInfo(StringInfo str, const int n);
+
/*------------------------
* appendStringInfo
* Format text data under the control of fmt (an sprintf-style format string)
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 124d853e49..933e9e4b0d 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -794,8 +794,8 @@ typedef struct IndexElem
* DefElem - a generic "name = value" option definition
*
* In some contexts the name can be qualified. Also, certain SQL commands
- * allow a SET/ADD/DROP action to be attached to option settings, so it's
- * convenient to carry a field for that too. (Note: currently, it is our
+ * allow a SET/ADD/DROP/APPEND/REMOVE action to be attached to option settings,
+ * so it's convenient to carry a field for that too. (Note: currently, it is our
* practice that the grammar allows namespace and action only in statements
* where they are relevant; C code can just ignore those fields in other
* statements.)
@@ -806,6 +806,8 @@ typedef enum DefElemAction
DEFELEM_SET,
DEFELEM_ADD,
DEFELEM_DROP,
+ DEFELEM_APPEND,
+ DEFELEM_REMOVE
} DefElemAction;
typedef struct DefElem
@@ -815,7 +817,8 @@ typedef struct DefElem
char *defname;
Node *arg; /* typically Integer, Float, String, or
* TypeName */
- DefElemAction defaction; /* unspecified action, or SET/ADD/DROP */
+ DefElemAction defaction; /* unspecified action, or
+ * SET/ADD/DROP/APPEND/REMOVE */
ParseLoc location; /* token location, or -1 if unknown */
} DefElem;
diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h
index f8659078ce..23acc3f2b2 100644
--- a/src/include/parser/kwlist.h
+++ b/src/include/parser/kwlist.h
@@ -42,6 +42,7 @@ PG_KEYWORD("analyse", ANALYSE, RESERVED_KEYWORD, BARE_LABEL) /* British spellin
PG_KEYWORD("analyze", ANALYZE, RESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("and", AND, RESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("any", ANY, RESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("append", APPEND, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("array", ARRAY, RESERVED_KEYWORD, AS_LABEL)
PG_KEYWORD("as", AS, RESERVED_KEYWORD, AS_LABEL)
PG_KEYWORD("asc", ASC, RESERVED_KEYWORD, BARE_LABEL)
@@ -370,6 +371,7 @@ PG_KEYWORD("refresh", REFRESH, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("reindex", REINDEX, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("relative", RELATIVE_P, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("release", RELEASE, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("remove", REMOVE, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("rename", RENAME, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("repeatable", REPEATABLE, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("replace", REPLACE, UNRESERVED_KEYWORD, BARE_LABEL)
--
2.41.0
On Mon, Aug 26, 2024 at 12:34 PM Ayush Vatsa <ayushvatsa1810@gmail.com> wrote:
I noticed that my last commit needs rebase through cfbot - http://cfbot.cputube.org/ayush-vatsa.html
PFA the rebased patch for the same.
Hi Ayush,
Thanks for working on this. One problem that I notice is that your
documentation changes seem to suppose that all options are lists, but
actually most of them aren't, and these new variants wouldn't be
applicable to non-list cases. They also suppose that everybody's using
comma-separated lists specifically, but that's not required and some
extensions might be doing something different. Also, I'm not convinced
that this problem would arise often enough in practice that it's worth
adding a feature to address it. A user who has this problem can pretty
easily do some scripting to address it - e.g. SELECT the current
option value, split it on commas, add or remove whatever, and then SET
the new option value. If that were something lots of users were doing
all the time, then I think it might make a lot of sense to have a
built-in solution to make it easier, but I doubt that's the case.
--
Robert Haas
EDB: http://www.enterprisedb.com
On Mon, Aug 26, 2024, at 1:34 PM, Ayush Vatsa wrote:
I noticed that my last commit needs rebase through cfbot - http://cfbot.cputube.org/ayush-vatsa.html
PFA the rebased patch for the same.
There is no list concept for OPTIONS. What happen if you use it in a non-list
value?
ALTER SERVER foo OPTIONS(ADD bar '1');
ALTER SERVER foo OPTIONS(REMOVE bar '1');
Error? Remove option 'bar'?
This proposal is not idempotent. It means that at the end of the SQL commands,
the final state is not predictable. That's disappointed since some tools rely on
this property to create migration scripts.
The syntax is not SQL standard. It does not mean that we cannot extend the
standard but sometimes it is a sign that it is not very useful or the current syntax
already covers all cases.
AFAICS this proposal also represents a tiny use case. The options to define a
foreign server don't generally include a list as value. And if so, it is not
common to change the foreign server options.
I also think that REMOVE is a synonym for DROP. It would be confuse to explain
that REMOVE is for list elements and DROP is for the list.
I'm not convinced that this new syntax improves the user experience.
--
Euler Taveira
EDB https://www.enterprisedb.com/
I'm not convinced
that this problem would arise often enough in practice that it's worth
adding a feature to address it. A user who has this problem can pretty
easily do some scripting to address it
AFAICS this proposal also represents a tiny use case.
Thanks Robert and Euler for the feedback.
Ack, Initially while working on the solution I thought this can be a common
problem. But I agree if the use case is small we can postpone supporting
such syntax for the future.
I can then withdraw the patch.
Regards
Ayush Vatsa
AWS