From 322e932d21345a178ccc254e940aaaa4dea816e5 Mon Sep 17 00:00:00 2001 From: Ayush Vatsa 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 name [ HANDLER handler_function | NO HANDLER ] [ VALIDATOR validator_function | NO VALIDATOR ] - [ OPTIONS ( [ ADD | SET | DROP ] option ['value'] [, ... ]) ] + [ OPTIONS ( [ ADD | SET | DROP | INCLUDE | EXCLUDE ] option ['value'] [, ... ]) ] ALTER FOREIGN DATA WRAPPER name OWNER TO { new_owner | CURRENT_ROLE | CURRENT_USER | SESSION_USER } ALTER FOREIGN DATA WRAPPER name RENAME TO new_name @@ -113,15 +113,43 @@ ALTER FOREIGN DATA WRAPPER name REN - OPTIONS ( [ ADD | SET | DROP ] option ['value'] [, ... ] ) + OPTIONS ( [ ADD | SET | DROP | INCLUDE | EXCLUDE ] option ['value'] [, ... ] ) Change options for the foreign-data - wrapper. ADD, SET, and DROP - specify the action to be performed. ADD 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. ADD, SET, DROP, + INCLUDE and EXCLUDE specify the action to be + performed. ADD 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. + + + + ADD can only be used for option names that don't already exist. + It sets the specified name's value as provided. + + + + SET can only be used for option names that already exist. + It overwrites the entire existing value for the specified option. + + + + DROP can only be used for option names that already exist. + It removes the specified name from the list of OPTIONS. + + + + INCLUDE can be used regardless of whether the option name exists or not. + If the option name doesn't exist, it acts like ADD. + If it does exist, it adds the provided values to the existing ones. + + + + EXCLUDE 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. @@ -157,6 +185,15 @@ ALTER FOREIGN DATA WRAPPER dbi OPTIONS (ADD foo '1', DROP bar); + + Change a foreign-data wrapper dbi, include + values in option foo, and exclude values from + option bar: + +ALTER FOREIGN DATA WRAPPER dbi OPTIONS (INCLUDE foo '1, 2, 3', EXCLUDE bar '1, 2'); + + + Change the foreign-data wrapper dbi validator to bob.myvalidator: 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 ] namecolumn_name SET ( attribute_option = value [, ... ] ) ALTER [ COLUMN ] column_name RESET ( attribute_option [, ... ] ) ALTER [ COLUMN ] column_name SET STORAGE { PLAIN | EXTERNAL | EXTENDED | MAIN | DEFAULT } - ALTER [ COLUMN ] column_name OPTIONS ( [ ADD | SET | DROP ] option ['value'] [, ... ]) + ALTER [ COLUMN ] column_name OPTIONS ( [ ADD | SET | DROP | INCLUDE | EXCLUDE ] option ['value'] [, ... ]) ADD table_constraint [ NOT VALID ] VALIDATE CONSTRAINT constraint_name DROP CONSTRAINT [ IF EXISTS ] constraint_name [ RESTRICT | CASCADE ] @@ -54,7 +54,7 @@ ALTER FOREIGN TABLE [ IF EXISTS ] nameparent_table NO INHERIT parent_table OWNER TO { new_owner | CURRENT_ROLE | CURRENT_USER | SESSION_USER } - OPTIONS ( [ ADD | SET | DROP ] option ['value'] [, ... ]) + OPTIONS ( [ ADD | SET | DROP | INCLUDE | EXCLUDE ] option ['value'] [, ... ]) @@ -266,17 +266,46 @@ ALTER FOREIGN TABLE [ IF EXISTS ] name - OPTIONS ( [ ADD | SET | DROP ] option ['value'] [, ... ] ) + OPTIONS ( [ ADD | SET | DROP | INCLUDE | EXCLUDE ] option ['value'] [, ... ] ) Change options for the foreign table or one of its columns. - ADD, SET, and DROP - specify the action to be performed. ADD is assumed - if no operation is explicitly specified. Duplicate option names are not + ADD, SET, DROP, + INCLUDE and EXCLUDE specify the action to be + performed. ADD 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. + + + ADD can only be used for option names that don't already exist. + It sets the specified name's value as provided. + + + + SET can only be used for option names that already exist. + It overwrites the entire existing value for the specified option. + + + + DROP can only be used for option names that already exist. + It removes the specified name from the list of OPTIONS. + + + + INCLUDE can be used regardless of whether the option name exists or not. + If the option name doesn't exist, it acts like ADD. + If it does exist, it adds the provided values to the existing ones. + + + + EXCLUDE 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. + @@ -521,7 +550,7 @@ ALTER FOREIGN TABLE distributors ALTER COLUMN street SET NOT NULL; To change options of a foreign table: -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'); 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 ALTER SERVER name [ VERSION 'new_version' ] - [ OPTIONS ( [ ADD | SET | DROP ] option ['value'] [, ... ] ) ] + [ OPTIONS ( [ ADD | SET | DROP | INCLUDE | EXCLUDE ] option ['value'] [, ... ] ) ] ALTER SERVER name OWNER TO { new_owner | CURRENT_ROLE | CURRENT_USER | SESSION_USER } ALTER SERVER name RENAME TO new_name @@ -71,15 +71,43 @@ ALTER SERVER name RENAME TO - OPTIONS ( [ ADD | SET | DROP ] option ['value'] [, ... ] ) + OPTIONS ( [ ADD | SET | DROP | INCLUDE | EXCLUDE ] option ['value'] [, ... ] ) Change options for the - server. ADD, SET, and DROP - specify the action to be performed. ADD 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. ADD, SET, DROP, + INCLUDE and EXCLUDE specify the action to be + performed. ADD 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. + + + + ADD can only be used for option names that don't already exist. + It sets the specified name's value as provided. + + + + SET can only be used for option names that already exist. + It overwrites the entire existing value for the specified option. + + + + DROP can only be used for option names that already exist. + It removes the specified name from the list of OPTIONS. + + + + INCLUDE can be used regardless of whether the option name exists or not. + If the option name doesn't exist, it acts like ADD. + If it does exist, it adds the provided values to the existing ones. + + + + EXCLUDE 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. @@ -114,6 +142,23 @@ ALTER SERVER foo OPTIONS (host 'foo', dbname 'foodb'); + + Suppose a server already contains few extensions then to add + more extensions into existing values use + Alter server foo, include extensions: + +ALTER SERVER foo OPTIONS (INCLUDE extensions 'ext1, ext2, ext3'); + + + + + To remove some selected values from extensions use + Alter server foo, exclude extensions: + +ALTER SERVER foo OPTIONS (EXCLUDE extensions 'ext1, ext3'); + + + Alter server foo, change version, change host 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 ALTER USER MAPPING FOR { user_name | USER | CURRENT_ROLE | CURRENT_USER | SESSION_USER | PUBLIC } SERVER server_name - OPTIONS ( [ ADD | SET | DROP ] option ['value'] [, ... ] ) + OPTIONS ( [ ADD | SET | DROP | INCLUDE | EXCLUDE ] option ['value'] [, ... ] ) @@ -69,16 +69,44 @@ ALTER USER MAPPING FOR { user_name - OPTIONS ( [ ADD | SET | DROP ] option ['value'] [, ... ] ) + OPTIONS ( [ ADD | SET | DROP | INCLUDE | EXCLUDE ] option ['value'] [, ... ] ) Change options for the user mapping. The new options override any previously specified - options. ADD, SET, and DROP - specify the action to be performed. ADD 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. ADD, SET, DROP, + INCLUDE and EXCLUDE specify the action to be + performed. ADD is assumed if no operation is explicitly + specified. Option names must be unique; options are also validated + by the server's foreign-data wrapper. + + + + ADD can only be used for option names that don't already exist. + It sets the specified name's value as provided. + + + + SET can only be used for option names that already exist. + It overwrites the entire existing value for the specified option. + + + + DROP can only be used for option names that already exist. + It removes the specified name from the list of OPTIONS. + + + + INCLUDE can be used regardless of whether the option name exists or not. + If the option name doesn't exist, it acts like ADD. + If it does exist, it adds the provided values to the existing ones. + + + + EXCLUDE 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. 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