Support ALTER FOREIGN TABLE ALTER COLUMN col OPTIONS (...).
authorShigeru Hanada <hanada@metrosystems.co.jp>
Tue, 7 Jun 2011 03:00:31 +0000 (12:00 +0900)
committerShigeru Hanada <shigeru.hanada@gmail.com>
Mon, 20 Jun 2011 04:32:03 +0000 (13:32 +0900)
Defining per-column generic options hasn't been implemented yet.

src/backend/commands/tablecmds.c
src/backend/parser/gram.y
src/bin/psql/describe.c
src/include/nodes/parsenodes.h
src/test/regress/expected/foreign_data.out
src/test/regress/sql/foreign_data.sql

index 912f45c052a902f1403b47cbb9496ed0bbb676eb..0dbe56f4a3adb72718e7d046428ea59fa408e2e0 100644 (file)
@@ -343,6 +343,7 @@ static void ATPrepAlterColumnType(List **wqueue,
 static bool ATColumnChangeRequiresRewrite(Node *expr, AttrNumber varattno);
 static void ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
                      AlterTableCmd *cmd, LOCKMODE lockmode);
+static void ATExecAlterColumnGenericOptions(Relation rel, const char *colName, List *options, LOCKMODE lockmode);
 static void ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab, LOCKMODE lockmode);
 static void ATPostAlterTypeParse(char *cmd, List **wqueue, LOCKMODE lockmode);
 static void change_owner_recurse_to_sequences(Oid relationOid,
@@ -2606,6 +2607,7 @@ AlterTableGetLockLevel(List *cmds)
            case AT_DropNotNull:        /* may change some SQL plans */
            case AT_SetNotNull:
            case AT_GenericOptions:
+           case AT_AlterColumnGenericOptions:
                cmd_lockmode = AccessExclusiveLock;
                break;
 
@@ -2880,6 +2882,12 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
            ATPrepAlterColumnType(wqueue, tab, rel, recurse, recursing, cmd, lockmode);
            pass = AT_PASS_ALTER_TYPE;
            break;
+       case AT_AlterColumnGenericOptions:
+           ATSimplePermissions(rel, ATT_FOREIGN_TABLE);
+           /* This command never recurses */
+           /* No command-specific prep needed */
+           pass = AT_PASS_MISC;
+           break;
        case AT_ChangeOwner:    /* ALTER OWNER */
            /* This command never recurses */
            /* No command-specific prep needed */
@@ -3113,6 +3121,9 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
        case AT_AlterColumnType:        /* ALTER COLUMN TYPE */
            ATExecAlterColumnType(tab, rel, cmd, lockmode);
            break;
+       case AT_AlterColumnGenericOptions:  /* ALTER COLUMN OPTIONS */
+           ATExecAlterColumnGenericOptions(rel, cmd->name, (List *) cmd->def, lockmode);
+           break;
        case AT_ChangeOwner:    /* ALTER OWNER */
            ATExecChangeOwner(RelationGetRelid(rel),
                              get_role_oid(cmd->name, false),
@@ -7163,6 +7174,100 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
    heap_freetuple(heapTup);
 }
 
+static void
+ATExecAlterColumnGenericOptions(Relation rel,
+                               const char *colName,
+                               List *options,
+                               LOCKMODE lockmode)
+{
+   Relation    ftrel;
+   Relation    attrel;
+   ForeignServer *server;
+   ForeignDataWrapper *fdw;
+   HeapTuple   tuple;
+   HeapTuple   newtuple;
+   bool        isnull;
+   Datum       repl_val[Natts_pg_attribute];
+   bool        repl_null[Natts_pg_attribute];
+   bool        repl_repl[Natts_pg_attribute];
+   Datum       datum;
+   Form_pg_foreign_table fttableform;
+   Form_pg_attribute atttableform;
+
+   if (options == NIL)
+       return;
+
+   /* First, determine FDW validator associated to the foreign table. */
+   ftrel = heap_open(ForeignTableRelationId, AccessShareLock);
+   tuple = SearchSysCache1(FOREIGNTABLEREL, rel->rd_id);
+   if (!HeapTupleIsValid(tuple))
+       ereport(ERROR,
+               (errcode(ERRCODE_UNDEFINED_OBJECT),
+                errmsg("foreign table \"%s\" does not exist",
+                       RelationGetRelationName(rel))));
+   fttableform = (Form_pg_foreign_table) GETSTRUCT(tuple);
+   server = GetForeignServer(fttableform->ftserver);
+   fdw = GetForeignDataWrapper(server->fdwid);
+
+   heap_close(ftrel, AccessShareLock);
+   ReleaseSysCache(tuple);
+
+   attrel = heap_open(AttributeRelationId, RowExclusiveLock);
+   tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
+   if (!HeapTupleIsValid(tuple))
+       ereport(ERROR,
+               (errcode(ERRCODE_UNDEFINED_COLUMN),
+                errmsg("column \"%s\" of relation \"%s\" does not exist",
+                       colName, RelationGetRelationName(rel))));
+
+   /* Prevent them from altering a system attribute */
+   atttableform = (Form_pg_attribute) GETSTRUCT(tuple);
+   if (atttableform->attnum <= 0)
+       ereport(ERROR,
+               (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                errmsg("cannot alter system column \"%s\"", colName)));
+
+
+   /* Initialize buffers for new tuple values */
+   memset(repl_val, 0, sizeof(repl_val));
+   memset(repl_null, false, sizeof(repl_null));
+   memset(repl_repl, false, sizeof(repl_repl));
+
+   /* Extract the current options */
+   datum = SysCacheGetAttr(ATTNAME,
+                           tuple,
+                           Anum_pg_attribute_attfdwoptions,
+                           &isnull);
+   if (isnull)
+       datum = PointerGetDatum(NULL);
+
+   /* Transform the options */
+   datum = transformGenericOptions(AttributeRelationId,
+                                   datum,
+                                   options,
+                                   fdw->fdwvalidator);
+
+   if (PointerIsValid(DatumGetPointer(datum)))
+       repl_val[Anum_pg_attribute_attfdwoptions - 1] = datum;
+   else
+       repl_null[Anum_pg_attribute_attfdwoptions - 1] = true;
+
+   repl_repl[Anum_pg_attribute_attfdwoptions - 1] = true;
+
+   /* Everything looks good - update the tuple */
+
+   newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrel),
+                                repl_val, repl_null, repl_repl);
+   ReleaseSysCache(tuple);
+
+   simple_heap_update(attrel, &newtuple->t_self, newtuple);
+   CatalogUpdateIndexes(attrel, newtuple);
+
+   heap_close(attrel, RowExclusiveLock);
+
+   heap_freetuple(newtuple);
+}
+
 /*
  * Cleanup after we've finished all the ALTER TYPE operations for a
  * particular relation.  We have to drop and recreate all the indexes
index c7e5c42f503245edc1c4f079a8fad23a7ee1f30e..5b1c143c78ce380ad05b3201770690832c19661e 100644 (file)
@@ -1770,6 +1770,15 @@ alter_table_cmd:
                    def->raw_default = $8;
                    $$ = (Node *)n;
                }
+           /* ALTER FOREIGN TABLE <name> ALTER [COLUMN] <colname> OPTIONS */
+           | ALTER opt_column ColId alter_generic_options
+               {
+                   AlterTableCmd *n = makeNode(AlterTableCmd);
+                   n->subtype = AT_AlterColumnGenericOptions;
+                   n->name = $3;
+                   n->def = (Node *) $4;
+                   $$ = (Node *)n;
+               }
            /* ALTER TABLE <name> ADD CONSTRAINT ... */
            | ADD_P TableConstraint
                {
index 1008c035d668beff35ee20e66654b829dd50a76f..6f08333df46dac9cc2ca146978f34e550be764d8 100644 (file)
@@ -3847,12 +3847,10 @@ listForeignTableColumns(const char *pattern, bool verbose)
    printfPQExpBuffer(&buf,
                      "SELECT n.nspname AS \"%s\",\n"
                      "  c.relname AS \"%s\",\n"
-                     "  a.attname AS \"%s\","
-                     "  s.srvname AS \"%s\"",
+                     "  a.attname AS \"%s\"",
                      gettext_noop("Schema"),
                      gettext_noop("Table"),
-                     gettext_noop("Column"),
-                     gettext_noop("Server"));
+                     gettext_noop("Column"));
 
    if (verbose)
        appendPQExpBuffer(&buf,
@@ -3862,10 +3860,8 @@ listForeignTableColumns(const char *pattern, bool verbose)
    appendPQExpBuffer(&buf, "\nFROM pg_catalog.pg_foreign_table ft,");
    appendPQExpBuffer(&buf, "\n pg_catalog.pg_class c,");
    appendPQExpBuffer(&buf, "\n pg_catalog.pg_namespace n,");
-   appendPQExpBuffer(&buf, "\n pg_catalog.pg_foreign_server s,");
    appendPQExpBuffer(&buf, "\n pg_catalog.pg_attribute a\n");
    appendPQExpBuffer(&buf, "\nWHERE c.oid = ft.ftrelid");
-   appendPQExpBuffer(&buf, "\nAND s.oid = ft.ftserver\n");
    appendPQExpBuffer(&buf, "\nAND n.oid = c.relnamespace\n");
    appendPQExpBuffer(&buf, "\nAND a.attrelid= c.oid\n");
    appendPQExpBuffer(&buf, "\nAND a.attnum > 0\n");
index c65e3cd6e8c6285ed3661935fc0817bbef78d242..970240cf2816a22afb5d0a16d91569b81e2a6db5 100644 (file)
@@ -1196,6 +1196,7 @@ typedef enum AlterTableType
    AT_DropConstraint,          /* drop constraint */
    AT_DropConstraintRecurse,   /* internal to commands/tablecmds.c */
    AT_AlterColumnType,         /* alter column type */
+   AT_AlterColumnGenericOptions,   /* alter column OPTIONS (...) */
    AT_ChangeOwner,             /* change owner */
    AT_ClusterOn,               /* CLUSTER ON */
    AT_DropCluster,             /* SET WITHOUT CLUSTER */
index e18eed8c1cf04ffc3988fda27d25ea492139f1e9..d513bf8b15c1a6d6c19180586f858ff402e0e434 100644 (file)
@@ -698,6 +698,26 @@ ALTER FOREIGN TABLE ft1 ALTER COLUMN c8 TYPE char(10) using '0'; -- ERROR
 ERROR:  ALTER TYPE USING is only supported on plain tables
 ALTER FOREIGN TABLE ft1 ALTER COLUMN c8 TYPE char(10);
 ALTER FOREIGN TABLE ft1 ALTER COLUMN c8 SET DATA TYPE text;
+ALTER FOREIGN TABLE ft1 ALTER COLUMN xmin OPTIONS (ADD p1 'v1'); -- ERROR
+ERROR:  cannot alter system column "xmin"
+ALTER FOREIGN TABLE ft1 ALTER COLUMN c7 OPTIONS (ADD p1 'v1', ADD p2 'v2'),
+                        ALTER COLUMN c8 OPTIONS (ADD p1 'v1', ADD p2 'v2');
+ALTER FOREIGN TABLE ft1 ALTER COLUMN c8 OPTIONS (SET p2 'V2', DROP p1);
+\dec+
+      List of foreign table columns
+ Schema | Table | Column |    Options    
+--------+-------+--------+---------------
+ public | ft1   | c1     | 
+ public | ft1   | c2     | 
+ public | ft1   | c3     | 
+ public | ft1   | c4     | 
+ public | ft1   | c6     | 
+ public | ft1   | c7     | {p1=v1,p2=v2}
+ public | ft1   | c8     | {p2=V2}
+ public | ft1   | c9     | 
+ public | ft1   | c10    | 
+(9 rows)
+
 -- can't change the column type if it's used elsewhere
 CREATE TABLE use_ft1_column_type (x ft1);
 ALTER FOREIGN TABLE ft1 ALTER COLUMN c8 SET DATA TYPE integer; -- ERROR
index d3239216c10f8e9e3f6589b1fa8adf8e8dd8e5b8..cafc23092949e240685f91ffde95173994e82c54 100644 (file)
@@ -297,6 +297,11 @@ ALTER FOREIGN TABLE ft1 ALTER COLUMN c7 DROP NOT NULL;
 ALTER FOREIGN TABLE ft1 ALTER COLUMN c8 TYPE char(10) using '0'; -- ERROR
 ALTER FOREIGN TABLE ft1 ALTER COLUMN c8 TYPE char(10);
 ALTER FOREIGN TABLE ft1 ALTER COLUMN c8 SET DATA TYPE text;
+ALTER FOREIGN TABLE ft1 ALTER COLUMN xmin OPTIONS (ADD p1 'v1'); -- ERROR
+ALTER FOREIGN TABLE ft1 ALTER COLUMN c7 OPTIONS (ADD p1 'v1', ADD p2 'v2'),
+                        ALTER COLUMN c8 OPTIONS (ADD p1 'v1', ADD p2 'v2');
+ALTER FOREIGN TABLE ft1 ALTER COLUMN c8 OPTIONS (SET p2 'V2', DROP p1);
+\dec+
 -- can't change the column type if it's used elsewhere
 CREATE TABLE use_ft1_column_type (x ft1);
 ALTER FOREIGN TABLE ft1 ALTER COLUMN c8 SET DATA TYPE integer; -- ERROR