diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index 8504555..d1225e1 100644
*** a/doc/src/sgml/catalogs.sgml
--- b/doc/src/sgml/catalogs.sgml
***************
*** 1152,1157 ****
--- 1152,1166 ----
+
+ attfdwoptions
+ text[]
+
+
+ Attribute-level generic options, as keyword=value> strings
+
+
+
diff --git a/doc/src/sgml/information_schema.sgml b/doc/src/sgml/information_schema.sgml
index 6df69db..539f573 100644
*** a/doc/src/sgml/information_schema.sgml
--- b/doc/src/sgml/information_schema.sgml
***************
*** 958,963 ****
--- 958,1026 ----
+
+ column_options
+
+
+ The view column_options contains all the
+ options defined for foreign table columns in the current database. Only
+ those foreign table columns are shown that the current user has access to
+ (by way of being the owner or having some privilege).
+
+
+
+ column_options Columns
+
+
+
+
+ Name
+ Data Type
+ Description
+
+
+
+
+
+ table_catalog
+ sql_identifier
+ Name of the database that contains the foreign table (always the current database)
+
+
+
+ table_schema
+ sql_identifier
+ Name of the schema that contains the foreign table
+
+
+
+ table_name
+ sql_identifier
+ Name of the foreign table
+
+
+
+ column_name
+ sql_identifier
+ Name of the column
+
+
+
+ option_name
+ sql_identifier
+ Name of an option
+
+
+
+ option_value
+ character_data
+ Value of the option
+
+
+
+
+
+
column_privileges
diff --git a/doc/src/sgml/ref/alter_foreign_table.sgml b/doc/src/sgml/ref/alter_foreign_table.sgml
index a45df02..95ae02a 100644
*** a/doc/src/sgml/ref/alter_foreign_table.sgml
--- b/doc/src/sgml/ref/alter_foreign_table.sgml
*************** ALTER FOREIGN TABLE column [ RESTRICT | CASCADE ]
ALTER [ COLUMN ] column [ SET DATA ] TYPE type
ALTER [ COLUMN ] column { SET | DROP } NOT NULL
+ ALTER [ COLUMN ] column OPTIONS ( [ ADD | SET | DROP ] option ['value'] [, ... ])
OWNER TO new_owner
OPTIONS ( [ ADD | SET | DROP ] option ['value'] [, ... ])
*************** ALTER FOREIGN TABLE option ['value'] [, ... ] )
! Change options for the foreign table.
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 library.
--- 126,137 ----
OPTIONS ( [ ADD | SET | DROP ] option ['value'] [, ... ] )
! Change options for the foreign table or the column of the foreign table.
ADD>, SET>, and DROP>
specify the action to be performed. ADD> is assumed
! if no operation is explicitly specified. Option names must be unique
! in each associated object; names and values are also validated using the
! foreign data wrapper library.
diff --git a/doc/src/sgml/ref/create_foreign_table.sgml b/doc/src/sgml/ref/create_foreign_table.sgml
index ad91072..8863386 100644
*** a/doc/src/sgml/ref/create_foreign_table.sgml
--- b/doc/src/sgml/ref/create_foreign_table.sgml
***************
*** 19,25 ****
CREATE FOREIGN TABLE [ IF NOT EXISTS ] table_name ( [
! { column_namedata_type [ NULL | NOT NULL ] }
[, ... ]
] )
SERVER server_name
--- 19,25 ----
CREATE FOREIGN TABLE [ IF NOT EXISTS ] table_name ( [
! { column_namedata_type [ OPTIONS ( option 'value' [, ... ] ) ] [ NULL | NOT NULL ] }
[, ... ]
] )
SERVER server_name
*************** CREATE FOREIGN TABLE [ IF NOT EXISTS ] <
*** 138,147 ****
OPTIONS ( option 'value' [, ...] )
! Options to be associated with the new foreign table.
The allowed option names and values are specific to each foreign
data wrapper and are validated using the foreign-data wrapper's
! validator function. Option names must be unique.
--- 138,149 ----
OPTIONS ( option 'value' [, ...] )
! Options to be associated with the new foreign table or the column of
! the foreign table.
The allowed option names and values are specific to each foreign
data wrapper and are validated using the foreign-data wrapper's
! validator function. Option names must be unique in each associated
! object.
diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml
index eaf901d..e9e9a61 100644
*** a/doc/src/sgml/ref/psql-ref.sgml
--- b/doc/src/sgml/ref/psql-ref.sgml
*************** testdb=>
*** 1109,1114 ****
--- 1109,1128 ----
+ \dec[+] [ pattern ]
+
+
+ Lists foreign table columns (mnemonic: external columns).
+ If pattern is
+ specified, only entries whose table name or schema name matches
+ the pattern are listed. If the form \dec+
+ is used, generic options are also displayed.
+
+
+
+
+
+ \deu[+] [ pattern ]
diff --git a/src/backend/access/common/tupdesc.c b/src/backend/access/common/tupdesc.c
index 16979c4..4ffedab 100644
*** a/src/backend/access/common/tupdesc.c
--- b/src/backend/access/common/tupdesc.c
*************** equalTupleDescs(TupleDesc tupdesc1, Tupl
*** 362,368 ****
return false;
if (attr1->attcollation != attr2->attcollation)
return false;
! /* attacl and attoptions are not even present... */
}
if (tupdesc1->constr != NULL)
--- 362,368 ----
return false;
if (attr1->attcollation != attr2->attcollation)
return false;
! /* attacl, attoptions and attfdwoptions are not even present... */
}
if (tupdesc1->constr != NULL)
*************** TupleDescInitEntry(TupleDesc desc,
*** 482,488 ****
att->attisdropped = false;
att->attislocal = true;
att->attinhcount = 0;
! /* attacl and attoptions are not present in tupledescs */
tuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(oidtypeid));
if (!HeapTupleIsValid(tuple))
--- 482,488 ----
att->attisdropped = false;
att->attislocal = true;
att->attinhcount = 0;
! /* attacl, attoptions and attfdwoptions are not present in tupledescs */
tuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(oidtypeid));
if (!HeapTupleIsValid(tuple))
diff --git a/src/backend/catalog/genbki.pl b/src/backend/catalog/genbki.pl
index 0aeaf5b..d91af52 100644
*** a/src/backend/catalog/genbki.pl
--- b/src/backend/catalog/genbki.pl
*************** sub emit_pgattr_row
*** 369,375 ****
attislocal => 't',
attinhcount => '0',
attacl => '_null_',
! attoptions => '_null_'
);
return {%PGATTR_DEFAULTS, %row};
}
--- 369,376 ----
attislocal => 't',
attinhcount => '0',
attacl => '_null_',
! attoptions => '_null_',
! attfdwoptions => '_null_'
);
return {%PGATTR_DEFAULTS, %row};
}
*************** sub emit_schemapg_row
*** 400,405 ****
--- 401,407 ----
# Only the fixed-size portions of the descriptors are ever used.
delete $row->{attacl};
delete $row->{attoptions};
+ delete $row->{attfdwoptions};
# Expand booleans from 'f'/'t' to 'false'/'true'.
# Some values might be other macros (eg FLOAT4PASSBYVAL), don't change.
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index a6e541d..f3090e2 100644
*** a/src/backend/catalog/heap.c
--- b/src/backend/catalog/heap.c
*************** static List *insert_ordered_unique_oid(L
*** 126,132 ****
*/
/*
! * The initializers below do not include the attoptions or attacl fields,
* but that's OK - we're never going to reference anything beyond the
* fixed-size portion of the structure anyway.
*/
--- 126,132 ----
*/
/*
! * The initializers below do not include trailing variable length fields,
* but that's OK - we're never going to reference anything beyond the
* fixed-size portion of the structure anyway.
*/
*************** InsertPgAttributeTuple(Relation pg_attri
*** 607,612 ****
--- 607,614 ----
/* start out with empty permissions and empty options */
nulls[Anum_pg_attribute_attacl - 1] = true;
nulls[Anum_pg_attribute_attoptions - 1] = true;
+ /* FIXME use content of OPTIONS (...) if any. */
+ nulls[Anum_pg_attribute_attfdwoptions - 1] = true;
tup = heap_form_tuple(RelationGetDescr(pg_attribute_rel), values, nulls);
diff --git a/src/backend/catalog/information_schema.sql b/src/backend/catalog/information_schema.sql
index 452a0ea..a8e8a12 100644
*** a/src/backend/catalog/information_schema.sql
--- b/src/backend/catalog/information_schema.sql
*************** GRANT SELECT ON element_types TO PUBLIC;
*** 2449,2454 ****
--- 2449,2487 ----
-- SQL/MED views; these use section numbers from part 9 of the standard.
+ /* Base view for foreign table columns */
+ CREATE VIEW _pg_foreign_table_columns AS
+ SELECT n.nspname,
+ c.relname,
+ a.attname,
+ a.attfdwoptions
+ FROM pg_foreign_table t, pg_authid u, pg_namespace n, pg_class c,
+ pg_attribute a
+ WHERE u.oid = c.relowner
+ AND (pg_has_role(c.relowner, 'USAGE')
+ OR has_column_privilege(c.oid, a.attnum, 'SELECT, INSERT, UPDATE, REFERENCES'))
+ AND n.oid = c.relnamespace
+ AND c.oid = t.ftrelid
+ AND c.relkind = 'f'
+ AND a.attrelid = c.oid
+ AND a.attnum > 0;
+
+ /*
+ * 24.2
+ * COLUMN_OPTIONS view
+ */
+ CREATE VIEW column_options AS
+ SELECT CAST(current_database() AS sql_identifier) AS table_catalog,
+ c.nspname AS table_schema,
+ c.relname AS table_name,
+ c.attname AS column_name,
+ CAST((pg_options_to_table(c.attfdwoptions)).option_name AS sql_identifier) AS option_name,
+ CAST((pg_options_to_table(c.attfdwoptions)).option_value AS character_data) AS option_value
+ FROM _pg_foreign_table_columns c;
+
+ GRANT SELECT ON column_options TO PUBLIC;
+
+
/* Base view for foreign-data wrappers */
CREATE VIEW _pg_foreign_data_wrappers AS
SELECT w.oid,
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 2c9f855..d604273 100644
*** a/src/backend/commands/tablecmds.c
--- b/src/backend/commands/tablecmds.c
*************** static void ATPrepAlterColumnType(List *
*** 343,348 ****
--- 343,349 ----
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,
*************** AlterTableGetLockLevel(List *cmds)
*** 2606,2611 ****
--- 2607,2613 ----
case AT_DropNotNull: /* may change some SQL plans */
case AT_SetNotNull:
case AT_GenericOptions:
+ case AT_AlterColumnGenericOptions:
cmd_lockmode = AccessExclusiveLock;
break;
*************** ATPrepCmd(List **wqueue, Relation rel, A
*** 2880,2885 ****
--- 2882,2893 ----
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 */
*************** ATExecCmd(List **wqueue, AlteredTableInf
*** 3113,3118 ****
--- 3121,3129 ----
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),
*************** ATExecAlterColumnType(AlteredTableInfo *
*** 7162,7167 ****
--- 7173,7272 ----
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
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index c9133dd..148110f 100644
*** a/src/backend/nodes/copyfuncs.c
--- b/src/backend/nodes/copyfuncs.c
*************** _copyColumnDef(ColumnDef *from)
*** 2312,2317 ****
--- 2312,2318 ----
COPY_NODE_FIELD(collClause);
COPY_SCALAR_FIELD(collOid);
COPY_NODE_FIELD(constraints);
+ COPY_NODE_FIELD(fdwoptions);
return newnode;
}
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 681f5f8..cb9be13 100644
*** a/src/backend/nodes/outfuncs.c
--- b/src/backend/nodes/outfuncs.c
*************** _outColumnDef(StringInfo str, ColumnDef
*** 2101,2106 ****
--- 2101,2107 ----
WRITE_NODE_FIELD(collClause);
WRITE_OID_FIELD(collOid);
WRITE_NODE_FIELD(constraints);
+ WRITE_NODE_FIELD(fdwoptions);
}
static void
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 1d39674..c6c3c39 100644
*** a/src/backend/parser/gram.y
--- b/src/backend/parser/gram.y
*************** alter_table_cmd:
*** 1760,1765 ****
--- 1760,1774 ----
def->raw_default = $8;
$$ = (Node *)n;
}
+ /* ALTER FOREIGN TABLE ALTER [COLUMN] 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 ADD CONSTRAINT ... */
| ADD_P TableConstraint
{
*************** TypedTableElement:
*** 2488,2494 ****
| TableConstraint { $$ = $1; }
;
! columnDef: ColId Typename ColQualList
{
ColumnDef *n = makeNode(ColumnDef);
n->colname = $1;
--- 2497,2503 ----
| TableConstraint { $$ = $1; }
;
! columnDef: ColId Typename create_generic_options ColQualList
{
ColumnDef *n = makeNode(ColumnDef);
n->colname = $1;
*************** columnDef: ColId Typename ColQualList
*** 2501,2507 ****
n->raw_default = NULL;
n->cooked_default = NULL;
n->collOid = InvalidOid;
! SplitColQualList($3, &n->constraints, &n->collClause,
yyscanner);
$$ = (Node *)n;
}
--- 2510,2517 ----
n->raw_default = NULL;
n->cooked_default = NULL;
n->collOid = InvalidOid;
! n->fdwoptions = $3;
! SplitColQualList($4, &n->constraints, &n->collClause,
yyscanner);
$$ = (Node *)n;
}
*************** AlterFdwStmt: ALTER FOREIGN DATA_P WRAPP
*** 3681,3687 ****
/* Options definition for CREATE FDW, SERVER and USER MAPPING */
create_generic_options:
OPTIONS '(' generic_option_list ')' { $$ = $3; }
! | /*EMPTY*/ { $$ = NIL; }
;
generic_option_list:
--- 3691,3697 ----
/* Options definition for CREATE FDW, SERVER and USER MAPPING */
create_generic_options:
OPTIONS '(' generic_option_list ')' { $$ = $3; }
! | /*EMPTY*/ { $$ = NIL }
;
generic_option_list:
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index efb8fdd..8deb7af 100644
*** a/src/backend/parser/parse_utilcmd.c
--- b/src/backend/parser/parse_utilcmd.c
*************** transformColumnDefinition(CreateStmtCont
*** 548,553 ****
--- 548,585 ----
break;
}
}
+
+ /*
+ * Generate ALTER FOREIGN TABLE ALTER COLUMN statement which adds
+ * per-column generic options for this column.
+ */
+ if (column->fdwoptions != NIL)
+ {
+ AlterTableStmt *stmt;
+ AlterTableCmd *cmd;
+
+ cmd = makeNode(AlterTableCmd);
+ cmd->subtype = AT_AlterColumnGenericOptions;
+ cmd->name = column->colname;
+ cmd->def = (Node *) column->fdwoptions;
+ cmd->behavior = DROP_RESTRICT;
+ cmd->missing_ok = false;
+ cmd->validated = true;
+
+ stmt = makeNode(AlterTableStmt);
+ stmt->relation = cxt->relation;
+ stmt->cmds = NIL;
+ stmt->relkind = OBJECT_FOREIGN_TABLE;
+ stmt->cmds = lappend(stmt->cmds, cmd);
+
+ cxt->alist = lappend(cxt->alist, stmt);
+
+ foreach (clist, column->fdwoptions)
+ {
+ DefElem *option = (DefElem *) lfirst(clist);
+ elog(DEBUG3, "%s=%s", option->defname, strVal(option->arg));
+ }
+ }
}
/*
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index 378330b..bd1ec80 100644
*** a/src/bin/psql/command.c
--- b/src/bin/psql/command.c
*************** exec_command(const char *cmd,
*** 493,498 ****
--- 493,501 ----
case 't':
success = listForeignTables(pattern, show_verbose);
break;
+ case 'c':
+ success = listForeignTableColumns(pattern, show_verbose);
+ break;
default:
status = PSQL_CMD_UNKNOWN;
break;
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index b2c54b5..c0860f9 100644
*** a/src/bin/psql/describe.c
--- b/src/bin/psql/describe.c
*************** listForeignTables(const char *pattern, b
*** 3817,3822 ****
--- 3817,3885 ----
}
/*
+ * \dec
+ *
+ * Describes foreign table columns.
+ */
+ bool
+ listForeignTableColumns(const char *pattern, bool verbose)
+ {
+ PQExpBufferData buf;
+ PGresult *res;
+ printQueryOpt myopt = pset.popt;
+
+ /* FIXME should be modified to 90200 before posting patch */
+ if (pset.sversion < 90100)
+ {
+ fprintf(stderr, _("The server (version %d.%d) does not support foreign table column options.\n"),
+ pset.sversion / 10000, (pset.sversion / 100) % 100);
+ return true;
+ }
+
+ initPQExpBuffer(&buf);
+ printfPQExpBuffer(&buf,
+ "SELECT n.nspname AS \"%s\",\n"
+ " c.relname AS \"%s\",\n"
+ " a.attname AS \"%s\"",
+ gettext_noop("Schema"),
+ gettext_noop("Table"),
+ gettext_noop("Column"));
+
+ if (verbose)
+ appendPQExpBuffer(&buf,
+ ",\n a.attfdwoptions AS \"%s\"",
+ gettext_noop("Options"));
+
+ 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_attribute a\n");
+ appendPQExpBuffer(&buf, "\nWHERE c.oid = ft.ftrelid");
+ appendPQExpBuffer(&buf, "\nAND n.oid = c.relnamespace\n");
+ appendPQExpBuffer(&buf, "\nAND a.attrelid= c.oid\n");
+ appendPQExpBuffer(&buf, "\nAND a.attnum > 0\n");
+
+ processSQLNamePattern(pset.db, &buf, pattern, true, false,
+ NULL, "n.nspname", "c.relname", NULL);
+
+ appendPQExpBuffer(&buf, "ORDER BY 1, 2, a.attnum;");
+
+ res = PSQLexec(buf.data, false);
+ termPQExpBuffer(&buf);
+ if (!res)
+ return false;
+
+ myopt.nullPrint = NULL;
+ myopt.title = _("List of foreign table columns");
+ myopt.translate_header = true;
+
+ printQuery(res, &myopt, pset.queryFout, pset.logfile);
+
+ PQclear(res);
+ return true;
+ }
+
+ /*
* \dx
*
* Briefly describes installed extensions.
diff --git a/src/bin/psql/describe.h b/src/bin/psql/describe.h
index fb86d1e..b6bc472 100644
*** a/src/bin/psql/describe.h
--- b/src/bin/psql/describe.h
*************** extern bool listUserMappings(const char
*** 87,92 ****
--- 87,95 ----
/* \det */
extern bool listForeignTables(const char *pattern, bool verbose);
+ /* \dec */
+ extern bool listForeignTableColumns(const char *pattern, bool verbose);
+
/* \dL */
extern bool listLanguages(const char *pattern, bool verbose, bool showSystem);
diff --git a/src/bin/psql/help.c b/src/bin/psql/help.c
index ac5edca..0c8d011 100644
*** a/src/bin/psql/help.c
--- b/src/bin/psql/help.c
*************** slashUsage(unsigned short int pager)
*** 200,205 ****
--- 200,206 ----
fprintf(output, _(" \\ddp [PATTERN] list default privileges\n"));
fprintf(output, _(" \\dD[S] [PATTERN] list domains\n"));
fprintf(output, _(" \\det[+] [PATTERN] list foreign tables\n"));
+ fprintf(output, _(" \\dec[+] [PATTERN] list foreign table columns\n"));
fprintf(output, _(" \\des[+] [PATTERN] list foreign servers\n"));
fprintf(output, _(" \\deu[+] [PATTERN] list user mappings\n"));
fprintf(output, _(" \\dew[+] [PATTERN] list foreign-data wrappers\n"));
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index 0200a81..52e99a8 100644
*** a/src/include/catalog/catversion.h
--- b/src/include/catalog/catversion.h
***************
*** 53,58 ****
*/
/* yyyymmddN */
! #define CATALOG_VERSION_NO 201105231
#endif
--- 53,58 ----
*/
/* yyyymmddN */
! #define CATALOG_VERSION_NO 201106141
#endif
diff --git a/src/include/catalog/pg_attribute.h b/src/include/catalog/pg_attribute.h
index 409d6ea..3ea87e8 100644
*** a/src/include/catalog/pg_attribute.h
--- b/src/include/catalog/pg_attribute.h
*************** CATALOG(pg_attribute,1249) BKI_BOOTSTRAP
*** 156,161 ****
--- 156,164 ----
/* Column-level options */
text attoptions[1];
+
+ /* Column-level FDW options */
+ text attfdwoptions[1];
} FormData_pg_attribute;
/*
*************** typedef FormData_pg_attribute *Form_pg_a
*** 179,185 ****
* ----------------
*/
! #define Natts_pg_attribute 20
#define Anum_pg_attribute_attrelid 1
#define Anum_pg_attribute_attname 2
#define Anum_pg_attribute_atttypid 3
--- 182,188 ----
* ----------------
*/
! #define Natts_pg_attribute 21
#define Anum_pg_attribute_attrelid 1
#define Anum_pg_attribute_attname 2
#define Anum_pg_attribute_atttypid 3
*************** typedef FormData_pg_attribute *Form_pg_a
*** 200,205 ****
--- 203,209 ----
#define Anum_pg_attribute_attcollation 18
#define Anum_pg_attribute_attacl 19
#define Anum_pg_attribute_attoptions 20
+ #define Anum_pg_attribute_attfdwoptions 21
/* ----------------
diff --git a/src/include/catalog/pg_class.h b/src/include/catalog/pg_class.h
index ffcce3c..caa285e 100644
*** a/src/include/catalog/pg_class.h
--- b/src/include/catalog/pg_class.h
*************** typedef FormData_pg_class *Form_pg_class
*** 132,138 ****
/* Note: "3" in the relfrozenxid column stands for FirstNormalTransactionId */
DATA(insert OID = 1247 ( pg_type PGNSP 71 0 PGUID 0 0 0 0 0 0 0 f f p r 29 0 t f f f f 3 _null_ _null_ ));
DESCR("");
! DATA(insert OID = 1249 ( pg_attribute PGNSP 75 0 PGUID 0 0 0 0 0 0 0 f f p r 20 0 f f f f f 3 _null_ _null_ ));
DESCR("");
DATA(insert OID = 1255 ( pg_proc PGNSP 81 0 PGUID 0 0 0 0 0 0 0 f f p r 25 0 t f f f f 3 _null_ _null_ ));
DESCR("");
--- 132,138 ----
/* Note: "3" in the relfrozenxid column stands for FirstNormalTransactionId */
DATA(insert OID = 1247 ( pg_type PGNSP 71 0 PGUID 0 0 0 0 0 0 0 f f p r 29 0 t f f f f 3 _null_ _null_ ));
DESCR("");
! DATA(insert OID = 1249 ( pg_attribute PGNSP 75 0 PGUID 0 0 0 0 0 0 0 f f p r 21 0 f f f f f 3 _null_ _null_ ));
DESCR("");
DATA(insert OID = 1255 ( pg_proc PGNSP 81 0 PGUID 0 0 0 0 0 0 0 f f p r 25 0 t f f f f 3 _null_ _null_ ));
DESCR("");
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 14937d4..5e82220 100644
*** a/src/include/nodes/parsenodes.h
--- b/src/include/nodes/parsenodes.h
*************** typedef struct ColumnDef
*** 500,505 ****
--- 500,506 ----
CollateClause *collClause; /* untransformed COLLATE spec, if any */
Oid collOid; /* collation OID (InvalidOid if not set) */
List *constraints; /* other constraints on column */
+ List *fdwoptions; /* per-column FDW options */
} ColumnDef;
/*
*************** typedef enum AlterTableType
*** 1196,1201 ****
--- 1197,1203 ----
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 */
diff --git a/src/test/regress/expected/foreign_data.out b/src/test/regress/expected/foreign_data.out
index e18eed8..d447a2c 100644
*** a/src/test/regress/expected/foreign_data.out
--- b/src/test/regress/expected/foreign_data.out
*************** ERROR: syntax error at or near "WITH OI
*** 646,653 ****
LINE 1: CREATE FOREIGN TABLE ft1 () SERVER sc WITH OIDS;
^
CREATE FOREIGN TABLE ft1 (
! c1 integer NOT NULL,
! c2 text,
c3 date
) SERVER sc OPTIONS (delimiter ',', quote '"');
COMMENT ON FOREIGN TABLE ft1 IS 'ft1';
--- 646,653 ----
LINE 1: CREATE FOREIGN TABLE ft1 () SERVER sc WITH OIDS;
^
CREATE FOREIGN TABLE ft1 (
! c1 integer OPTIONS (param1 'val1') NOT NULL,
! c2 text OPTIONS (param2 'val2', param3 'val3'),
c3 date
) SERVER sc OPTIONS (delimiter ',', quote '"');
COMMENT ON FOREIGN TABLE ft1 IS 'ft1';
*************** ALTER FOREIGN TABLE ft1 ADD COLUMN c6 in
*** 687,693 ****
ALTER FOREIGN TABLE ft1 ADD COLUMN c7 integer NOT NULL;
ALTER FOREIGN TABLE ft1 ADD COLUMN c8 integer;
ALTER FOREIGN TABLE ft1 ADD COLUMN c9 integer;
! ALTER FOREIGN TABLE ft1 ADD COLUMN c10 integer;
ALTER FOREIGN TABLE ft1 ALTER COLUMN c4 SET DEFAULT 0; -- ERROR
ERROR: "ft1" is not a table or view
ALTER FOREIGN TABLE ft1 ALTER COLUMN c5 DROP DEFAULT; -- ERROR
--- 687,693 ----
ALTER FOREIGN TABLE ft1 ADD COLUMN c7 integer NOT NULL;
ALTER FOREIGN TABLE ft1 ADD COLUMN c8 integer;
ALTER FOREIGN TABLE ft1 ADD COLUMN c9 integer;
! ALTER FOREIGN TABLE ft1 ADD COLUMN c10 integer OPTIONS (p1 'v1');
ALTER FOREIGN TABLE ft1 ALTER COLUMN c4 SET DEFAULT 0; -- ERROR
ERROR: "ft1" is not a table or view
ALTER FOREIGN TABLE ft1 ALTER COLUMN c5 DROP DEFAULT; -- ERROR
*************** ALTER FOREIGN TABLE ft1 ALTER COLUMN c8
*** 698,703 ****
--- 698,723 ----
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 | {param1=val1}
+ public | ft1 | c2 | {param2=val2,param3=val3}
+ 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 | {p1=v1}
+ (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
diff --git a/src/test/regress/sql/foreign_data.sql b/src/test/regress/sql/foreign_data.sql
index d323921..80d8d6b 100644
*** a/src/test/regress/sql/foreign_data.sql
--- b/src/test/regress/sql/foreign_data.sql
*************** CREATE FOREIGN TABLE ft1 () SERVER no_se
*** 264,271 ****
CREATE FOREIGN TABLE ft1 (c1 serial) SERVER sc; -- ERROR
CREATE FOREIGN TABLE ft1 () SERVER sc WITH OIDS; -- ERROR
CREATE FOREIGN TABLE ft1 (
! c1 integer NOT NULL,
! c2 text,
c3 date
) SERVER sc OPTIONS (delimiter ',', quote '"');
COMMENT ON FOREIGN TABLE ft1 IS 'ft1';
--- 264,271 ----
CREATE FOREIGN TABLE ft1 (c1 serial) SERVER sc; -- ERROR
CREATE FOREIGN TABLE ft1 () SERVER sc WITH OIDS; -- ERROR
CREATE FOREIGN TABLE ft1 (
! c1 integer OPTIONS (param1 'val1') NOT NULL,
! c2 text OPTIONS (param2 'val2', param3 'val3'),
c3 date
) SERVER sc OPTIONS (delimiter ',', quote '"');
COMMENT ON FOREIGN TABLE ft1 IS 'ft1';
*************** ALTER FOREIGN TABLE ft1 ADD COLUMN c6 in
*** 288,294 ****
ALTER FOREIGN TABLE ft1 ADD COLUMN c7 integer NOT NULL;
ALTER FOREIGN TABLE ft1 ADD COLUMN c8 integer;
ALTER FOREIGN TABLE ft1 ADD COLUMN c9 integer;
! ALTER FOREIGN TABLE ft1 ADD COLUMN c10 integer;
ALTER FOREIGN TABLE ft1 ALTER COLUMN c4 SET DEFAULT 0; -- ERROR
ALTER FOREIGN TABLE ft1 ALTER COLUMN c5 DROP DEFAULT; -- ERROR
--- 288,294 ----
ALTER FOREIGN TABLE ft1 ADD COLUMN c7 integer NOT NULL;
ALTER FOREIGN TABLE ft1 ADD COLUMN c8 integer;
ALTER FOREIGN TABLE ft1 ADD COLUMN c9 integer;
! ALTER FOREIGN TABLE ft1 ADD COLUMN c10 integer OPTIONS (p1 'v1');
ALTER FOREIGN TABLE ft1 ALTER COLUMN c4 SET DEFAULT 0; -- ERROR
ALTER FOREIGN TABLE ft1 ALTER COLUMN c5 DROP DEFAULT; -- ERROR
*************** ALTER FOREIGN TABLE ft1 ALTER COLUMN c7
*** 297,302 ****
--- 297,307 ----
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