Allow per-column foreign data wrapper options.

Shigeru Hanada, with fairly minor editing by me.
This commit is contained in:
Robert Haas 2011-08-05 13:24:03 -04:00
parent 68cbb9f4e7
commit c4096c7639
23 changed files with 407 additions and 47 deletions

View File

@ -1157,6 +1157,15 @@
</entry>
</row>
<row>
<entry><structfield>attfdwoptions</structfield></entry>
<entry><type>text[]</type></entry>
<entry></entry>
<entry>
Attribute-level foreign data wrapper options, as <quote>keyword=value</> strings
</entry>
</row>
</tbody>
</tgroup>
</table>

View File

@ -1018,6 +1018,69 @@
</table>
</sect1>
<sect1 id="infoschema-column-options">
<title><literal>column_options</literal></title>
<para>
The view <literal>column_options</literal> 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).
</para>
<table>
<title><literal>column_options</literal> Columns</title>
<tgroup cols="3">
<thead>
<row>
<entry>Name</entry>
<entry>Data Type</entry>
<entry>Description</entry>
</row>
</thead>
<tbody>
<row>
<entry><literal>table_catalog</literal></entry>
<entry><type>sql_identifier</type></entry>
<entry>Name of the database that contains the foreign table (always the current database)</entry>
</row>
<row>
<entry><literal>table_schema</literal></entry>
<entry><type>sql_identifier</type></entry>
<entry>Name of the schema that contains the foreign table</entry>
</row>
<row>
<entry><literal>table_name</literal></entry>
<entry><type>sql_identifier</type></entry>
<entry>Name of the foreign table</entry>
</row>
<row>
<entry><literal>column_name</literal></entry>
<entry><type>sql_identifier</type></entry>
<entry>Name of the column</entry>
</row>
<row>
<entry><literal>option_name</literal></entry>
<entry><type>sql_identifier</type></entry>
<entry>Name of an option</entry>
</row>
<row>
<entry><literal>option_value</literal></entry>
<entry><type>character_data</type></entry>
<entry>Value of the option</entry>
</row>
</tbody>
</tgroup>
</table>
</sect1>
<sect1 id="infoschema-column-privileges">
<title><literal>column_privileges</literal></title>

View File

@ -36,6 +36,7 @@ ALTER FOREIGN TABLE <replaceable class="PARAMETER">name</replaceable>
DROP [ COLUMN ] [ IF EXISTS ] <replaceable class="PARAMETER">column</replaceable> [ RESTRICT | CASCADE ]
ALTER [ COLUMN ] <replaceable class="PARAMETER">column</replaceable> [ SET DATA ] TYPE <replaceable class="PARAMETER">type</replaceable>
ALTER [ COLUMN ] <replaceable class="PARAMETER">column</replaceable> { SET | DROP } NOT NULL
ALTER [ COLUMN ] <replaceable class="PARAMETER">column</replaceable> OPTIONS ( [ ADD | SET | DROP ] <replaceable class="PARAMETER">option</replaceable> ['<replaceable class="PARAMETER">value</replaceable>'] [, ... ])
OWNER TO <replaceable class="PARAMETER">new_owner</replaceable>
OPTIONS ( [ ADD | SET | DROP ] <replaceable class="PARAMETER">option</replaceable> ['<replaceable class="PARAMETER">value</replaceable>'] [, ... ])
</synopsis>
@ -125,12 +126,13 @@ ALTER FOREIGN TABLE <replaceable class="PARAMETER">name</replaceable>
<term><literal>OPTIONS ( [ ADD | SET | DROP ] <replaceable class="PARAMETER">option</replaceable> ['<replaceable class="PARAMETER">value</replaceable>'] [, ... ] )</literal></term>
<listitem>
<para>
Change options for the foreign table.
Change options for the foreign table or one of its columns.
<literal>ADD</>, <literal>SET</>, and <literal>DROP</>
specify the action to be performed. <literal>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.
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>
</listitem>
</varlistentry>

View File

@ -19,7 +19,7 @@
<refsynopsisdiv>
<synopsis>
CREATE FOREIGN TABLE [ IF NOT EXISTS ] <replaceable class="PARAMETER">table_name</replaceable> ( [
{ <replaceable class="PARAMETER">column_name</replaceable> <replaceable class="PARAMETER">data_type</replaceable> [ NULL | NOT NULL ] }
{ <replaceable class="PARAMETER">column_name</replaceable> <replaceable class="PARAMETER">data_type</replaceable> [ OPTIONS ( <replaceable class="PARAMETER">option</replaceable> '<replaceable class="PARAMETER">value</replaceable>' [, ... ] ) ] [ NULL | NOT NULL ] }
[, ... ]
] )
SERVER <replaceable class="parameter">server_name</replaceable>
@ -138,10 +138,12 @@ CREATE FOREIGN TABLE [ IF NOT EXISTS ] <replaceable class="PARAMETER">table_name
<term><literal>OPTIONS ( <replaceable class="PARAMETER">option</replaceable> '<replaceable class="PARAMETER">value</replaceable>' [, ...] )</literal></term>
<listitem>
<para>
Options to be associated with the new foreign table.
Options to be associated with the new foreign table or one of its
columns.
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.
validator function. Duplicate option names are not allowed (although
it's OK for a table option and a column option to have the same name).
</para>
</listitem>
</varlistentry>

View File

@ -891,6 +891,12 @@ testdb=&gt;
below.)
</para>
<para>
For some types of relation, <literal>\d</> shows additional information
for each column: column values for sequences, indexed expression for
indexes and per-column foreign data wrapper options for foreign tables.
</para>
<para>
The command form <literal>\d+</literal> is identical, except that
more information is displayed: any comments associated with the

View File

@ -363,7 +363,7 @@ equalTupleDescs(TupleDesc tupdesc1, TupleDesc tupdesc2)
return false;
if (attr1->attcollation != attr2->attcollation)
return false;
/* attacl and attoptions are not even present... */
/* attacl, attoptions and attfdwoptions are not even present... */
}
if (tupdesc1->constr != NULL)
@ -483,7 +483,7 @@ TupleDescInitEntry(TupleDesc desc,
att->attisdropped = false;
att->attislocal = true;
att->attinhcount = 0;
/* attacl and attoptions are not present in tupledescs */
/* attacl, attoptions and attfdwoptions are not present in tupledescs */
tuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(oidtypeid));
if (!HeapTupleIsValid(tuple))

View File

@ -369,7 +369,8 @@ sub emit_pgattr_row
attislocal => 't',
attinhcount => '0',
attacl => '_null_',
attoptions => '_null_'
attoptions => '_null_',
attfdwoptions => '_null_'
);
return {%PGATTR_DEFAULTS, %row};
}
@ -400,6 +401,7 @@ sub emit_schemapg_row
# 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.

View File

@ -126,7 +126,7 @@ static List *insert_ordered_unique_oid(List *list, Oid datum);
*/
/*
* The initializers below do not include the attoptions or attacl fields,
* 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.
*/
@ -620,6 +620,7 @@ InsertPgAttributeTuple(Relation pg_attribute_rel,
/* start out with empty permissions and empty options */
nulls[Anum_pg_attribute_attacl - 1] = true;
nulls[Anum_pg_attribute_attoptions - 1] = true;
nulls[Anum_pg_attribute_attfdwoptions - 1] = true;
tup = heap_form_tuple(RelationGetDescr(pg_attribute_rel), values, nulls);

View File

@ -2534,6 +2534,39 @@ GRANT SELECT ON element_types TO PUBLIC;
-- 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,

View File

@ -346,6 +346,8 @@ 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(Oid oldId, char *cmd,
List **wqueue, LOCKMODE lockmode, bool rewrite);
@ -2648,6 +2650,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;
@ -2925,6 +2928,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 */
@ -3169,6 +3178,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),
@ -7397,6 +7409,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

View File

@ -2312,6 +2312,7 @@ _copyColumnDef(ColumnDef *from)
COPY_NODE_FIELD(collClause);
COPY_SCALAR_FIELD(collOid);
COPY_NODE_FIELD(constraints);
COPY_NODE_FIELD(fdwoptions);
return newnode;
}

View File

@ -2102,6 +2102,7 @@ _outColumnDef(StringInfo str, ColumnDef *node)
WRITE_NODE_FIELD(collClause);
WRITE_OID_FIELD(collOid);
WRITE_NODE_FIELD(constraints);
WRITE_NODE_FIELD(fdwoptions);
}
static void

View File

@ -1769,6 +1769,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
{
@ -2497,7 +2506,7 @@ TypedTableElement:
| TableConstraint { $$ = $1; }
;
columnDef: ColId Typename ColQualList
columnDef: ColId Typename create_generic_options ColQualList
{
ColumnDef *n = makeNode(ColumnDef);
n->colname = $1;
@ -2510,7 +2519,8 @@ columnDef: ColId Typename ColQualList
n->raw_default = NULL;
n->cooked_default = NULL;
n->collOid = InvalidOid;
SplitColQualList($3, &n->constraints, &n->collClause,
n->fdwoptions = $3;
SplitColQualList($4, &n->constraints, &n->collClause,
yyscanner);
$$ = (Node *)n;
}

View File

@ -559,6 +559,31 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column)
break;
}
}
/*
* Generate ALTER FOREIGN TABLE ALTER COLUMN statement which adds
* per-column foreign data wrapper 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;
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);
}
}
/*

View File

@ -5574,6 +5574,7 @@ getTableAttrs(TableInfo *tblinfo, int numTables)
int i_attislocal;
int i_attoptions;
int i_attcollation;
int i_attfdwoptions;
PGresult *res;
int ntups;
bool hasdefaults;
@ -5611,7 +5612,31 @@ getTableAttrs(TableInfo *tblinfo, int numTables)
resetPQExpBuffer(q);
if (g_fout->remoteVersion >= 90100)
if (g_fout->remoteVersion >= 90200)
{
/*
* attfdwoptions is new in 9.2.
*/
appendPQExpBuffer(q, "SELECT a.attnum, a.attname, a.atttypmod, "
"a.attstattarget, a.attstorage, t.typstorage, "
"a.attnotnull, a.atthasdef, a.attisdropped, "
"a.attlen, a.attalign, a.attislocal, "
"pg_catalog.format_type(t.oid,a.atttypmod) AS atttypname, "
"array_to_string(a.attoptions, ', ') AS attoptions, "
"CASE WHEN a.attcollation <> t.typcollation "
"THEN a.attcollation ELSE 0 END AS attcollation, "
"array_to_string(ARRAY("
" SELECT option_name || ' ' || quote_literal(option_value) "
" FROM pg_options_to_table(attfdwoptions)), ', ') "
" AS attfdwoptions "
"FROM pg_catalog.pg_attribute a LEFT JOIN pg_catalog.pg_type t "
"ON a.atttypid = t.oid "
"WHERE a.attrelid = '%u'::pg_catalog.oid "
"AND a.attnum > 0::pg_catalog.int2 "
"ORDER BY a.attrelid, a.attnum",
tbinfo->dobj.catId.oid);
}
else if (g_fout->remoteVersion >= 90100)
{
/*
* attcollation is new in 9.1. Since we only want to dump COLLATE
@ -5626,7 +5651,8 @@ getTableAttrs(TableInfo *tblinfo, int numTables)
"pg_catalog.format_type(t.oid,a.atttypmod) AS atttypname, "
"array_to_string(a.attoptions, ', ') AS attoptions, "
"CASE WHEN a.attcollation <> t.typcollation "
"THEN a.attcollation ELSE 0 END AS attcollation "
"THEN a.attcollation ELSE 0 END AS attcollation, "
"NULL AS attfdwoptions "
"FROM pg_catalog.pg_attribute a LEFT JOIN pg_catalog.pg_type t "
"ON a.atttypid = t.oid "
"WHERE a.attrelid = '%u'::pg_catalog.oid "
@ -5643,7 +5669,8 @@ getTableAttrs(TableInfo *tblinfo, int numTables)
"a.attlen, a.attalign, a.attislocal, "
"pg_catalog.format_type(t.oid,a.atttypmod) AS atttypname, "
"array_to_string(a.attoptions, ', ') AS attoptions, "
"0 AS attcollation "
"0 AS attcollation, "
"NULL AS attfdwoptions "
"FROM pg_catalog.pg_attribute a LEFT JOIN pg_catalog.pg_type t "
"ON a.atttypid = t.oid "
"WHERE a.attrelid = '%u'::pg_catalog.oid "
@ -5659,7 +5686,8 @@ getTableAttrs(TableInfo *tblinfo, int numTables)
"a.attnotnull, a.atthasdef, a.attisdropped, "
"a.attlen, a.attalign, a.attislocal, "
"pg_catalog.format_type(t.oid,a.atttypmod) AS atttypname, "
"'' AS attoptions, 0 AS attcollation "
"'' AS attoptions, 0 AS attcollation, "
"NULL AS attfdwoptions "
"FROM pg_catalog.pg_attribute a LEFT JOIN pg_catalog.pg_type t "
"ON a.atttypid = t.oid "
"WHERE a.attrelid = '%u'::pg_catalog.oid "
@ -5680,7 +5708,8 @@ getTableAttrs(TableInfo *tblinfo, int numTables)
"false AS attisdropped, a.attlen, "
"a.attalign, false AS attislocal, "
"format_type(t.oid,a.atttypmod) AS atttypname, "
"'' AS attoptions, 0 AS attcollation "
"'' AS attoptions, 0 AS attcollation, "
"NULL AS attfdwoptions "
"FROM pg_attribute a LEFT JOIN pg_type t "
"ON a.atttypid = t.oid "
"WHERE a.attrelid = '%u'::oid "
@ -5698,7 +5727,8 @@ getTableAttrs(TableInfo *tblinfo, int numTables)
"attlen, attalign, "
"false AS attislocal, "
"(SELECT typname FROM pg_type WHERE oid = atttypid) AS atttypname, "
"'' AS attoptions, 0 AS attcollation "
"'' AS attoptions, 0 AS attcollation, "
"NULL AS attfdwoptions "
"FROM pg_attribute a "
"WHERE attrelid = '%u'::oid "
"AND attnum > 0::int2 "
@ -5726,6 +5756,7 @@ getTableAttrs(TableInfo *tblinfo, int numTables)
i_attislocal = PQfnumber(res, "attislocal");
i_attoptions = PQfnumber(res, "attoptions");
i_attcollation = PQfnumber(res, "attcollation");
i_attfdwoptions = PQfnumber(res, "attfdwoptions");
tbinfo->numatts = ntups;
tbinfo->attnames = (char **) malloc(ntups * sizeof(char *));
@ -5742,6 +5773,7 @@ getTableAttrs(TableInfo *tblinfo, int numTables)
tbinfo->attrdefs = (AttrDefInfo **) malloc(ntups * sizeof(AttrDefInfo *));
tbinfo->attoptions = (char **) malloc(ntups * sizeof(char *));
tbinfo->attcollation = (Oid *) malloc(ntups * sizeof(Oid));
tbinfo->attfdwoptions = (char **) malloc(ntups * sizeof(char *));
tbinfo->inhAttrs = (bool *) malloc(ntups * sizeof(bool));
tbinfo->inhAttrDef = (bool *) malloc(ntups * sizeof(bool));
tbinfo->inhNotNull = (bool *) malloc(ntups * sizeof(bool));
@ -5768,6 +5800,7 @@ getTableAttrs(TableInfo *tblinfo, int numTables)
tbinfo->notnull[j] = (PQgetvalue(res, j, i_attnotnull)[0] == 't');
tbinfo->attoptions[j] = strdup(PQgetvalue(res, j, i_attoptions));
tbinfo->attcollation[j] = atooid(PQgetvalue(res, j, i_attcollation));
tbinfo->attfdwoptions[j] = strdup(PQgetvalue(res, j, i_attfdwoptions));
tbinfo->attrdefs[j] = NULL; /* fix below */
if (PQgetvalue(res, j, i_atthasdef)[0] == 't')
hasdefaults = true;
@ -12469,6 +12502,21 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo)
appendPQExpBuffer(q, "SET (%s);\n",
tbinfo->attoptions[j]);
}
/*
* Dump per-column fdw options.
*/
if (tbinfo->relkind == RELKIND_FOREIGN_TABLE &&
tbinfo->attfdwoptions[j] &&
tbinfo->attfdwoptions[j][0] != '\0')
{
appendPQExpBuffer(q, "ALTER FOREIGN TABLE %s ",
fmtId(tbinfo->dobj.name));
appendPQExpBuffer(q, "ALTER COLUMN %s ",
fmtId(tbinfo->attnames[j]));
appendPQExpBuffer(q, "OPTIONS (%s);\n",
tbinfo->attfdwoptions[j]);
}
}
}

View File

@ -275,6 +275,7 @@ typedef struct _tableInfo
bool *attislocal; /* true if attr has local definition */
char **attoptions; /* per-attribute options */
Oid *attcollation; /* per-attribute collation selection */
char **attfdwoptions; /* per-attribute fdw options */
/*
* Note: we need to store per-attribute notnull, default, and constraint

View File

@ -1281,7 +1281,12 @@ describeOneTableDetails(const char *schemaname,
res = NULL;
}
/* Get column info */
/*
* Get column info
*
* You need to modify value of "firstvcol" which willbe defined below if
* you are adding column(s) preceding to verbose-only columns.
*/
printfPQExpBuffer(&buf, "SELECT a.attname,");
appendPQExpBuffer(&buf, "\n pg_catalog.format_type(a.atttypid, a.atttypmod),"
"\n (SELECT substring(pg_catalog.pg_get_expr(d.adbin, d.adrelid) for 128)"
@ -1295,6 +1300,12 @@ describeOneTableDetails(const char *schemaname,
appendPQExpBuffer(&buf, "\n NULL AS attcollation");
if (tableinfo.relkind == 'i')
appendPQExpBuffer(&buf, ",\n pg_catalog.pg_get_indexdef(a.attrelid, a.attnum, TRUE) AS indexdef");
else
appendPQExpBuffer(&buf, ",\n NULL AS indexdef");
if (tableinfo.relkind == 'f' && pset.sversion >= 90200)
appendPQExpBuffer(&buf, ",\n a.attfdwoptions");
else
appendPQExpBuffer(&buf, ",\n NULL AS attfdwoptions");
if (verbose)
{
appendPQExpBuffer(&buf, ",\n a.attstorage");
@ -1386,6 +1397,9 @@ describeOneTableDetails(const char *schemaname,
if (tableinfo.relkind == 'i')
headers[cols++] = gettext_noop("Definition");
if (tableinfo.relkind == 'f' && pset.sversion >= 90200)
headers[cols++] = gettext_noop("Options");
if (verbose)
{
headers[cols++] = gettext_noop("Storage");
@ -1471,10 +1485,14 @@ describeOneTableDetails(const char *schemaname,
if (tableinfo.relkind == 'i')
printTableAddCell(&cont, PQgetvalue(res, i, 6), false, false);
/* FDW options for foreign table column, only for 9.2 or later */
if (tableinfo.relkind == 'f' && pset.sversion >= 90200)
printTableAddCell(&cont, PQgetvalue(res, i, 7), false, false);
/* Storage and Description */
if (verbose)
{
int firstvcol = (tableinfo.relkind == 'i' ? 7 : 6);
int firstvcol = 8;
char *storage = PQgetvalue(res, i, firstvcol);
/* these strings are literal in our syntax, so not translated. */

View File

@ -53,6 +53,6 @@
*/
/* yyyymmddN */
#define CATALOG_VERSION_NO 201107201
#define CATALOG_VERSION_NO 201108051
#endif

View File

@ -156,6 +156,9 @@ CATALOG(pg_attribute,1249) BKI_BOOTSTRAP BKI_WITHOUT_OIDS BKI_ROWTYPE_OID(75) BK
/* Column-level options */
text attoptions[1];
/* Column-level FDW options */
text attfdwoptions[1];
} FormData_pg_attribute;
/*
@ -179,7 +182,7 @@ typedef FormData_pg_attribute *Form_pg_attribute;
* ----------------
*/
#define Natts_pg_attribute 20
#define Natts_pg_attribute 21
#define Anum_pg_attribute_attrelid 1
#define Anum_pg_attribute_attname 2
#define Anum_pg_attribute_atttypid 3
@ -200,6 +203,7 @@ typedef FormData_pg_attribute *Form_pg_attribute;
#define Anum_pg_attribute_attcollation 18
#define Anum_pg_attribute_attacl 19
#define Anum_pg_attribute_attoptions 20
#define Anum_pg_attribute_attfdwoptions 21
/* ----------------

View File

@ -132,7 +132,7 @@ typedef FormData_pg_class *Form_pg_class;
/* 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_ ));
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 26 0 t f f f f 3 _null_ _null_ ));
DESCR("");

View File

@ -500,6 +500,7 @@ typedef struct ColumnDef
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;
/*
@ -1197,6 +1198,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 */

View File

@ -646,19 +646,19 @@ ERROR: syntax error at or near "WITH OIDS"
LINE 1: CREATE FOREIGN TABLE ft1 () SERVER sc WITH OIDS;
^
CREATE FOREIGN TABLE ft1 (
c1 integer NOT NULL,
c2 text,
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';
COMMENT ON COLUMN ft1.c1 IS 'ft1.c1';
\d+ ft1
Foreign table "public.ft1"
Column | Type | Modifiers | Storage | Description
--------+---------+-----------+----------+-------------
c1 | integer | not null | plain | ft1.c1
c2 | text | | extended |
c3 | date | | plain |
Foreign table "public.ft1"
Column | Type | Modifiers | Options | Storage | Description
--------+---------+-----------+---------------------------+----------+-------------
c1 | integer | not null | {param1=val1} | plain | ft1.c1
c2 | text | | {param2=val2,param3=val3} | extended |
c3 | date | | | plain |
Server: sc
Has OIDs: no
@ -687,7 +687,7 @@ ALTER FOREIGN TABLE ft1 ADD COLUMN c6 integer;
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 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
@ -698,6 +698,27 @@ ALTER FOREIGN TABLE ft1 ALTER COLUMN c8 TYPE char(10) USING '0'; -- ERROR
ERROR: "ft1" is not a table
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);
\d+ ft1
Foreign table "public.ft1"
Column | Type | Modifiers | Options | Storage | Description
--------+---------+-----------+---------------------------+----------+-------------
c1 | integer | not null | {param1=val1} | plain |
c2 | text | | {param2=val2,param3=val3} | extended |
c3 | date | | | plain |
c4 | integer | | | plain |
c6 | integer | not null | | plain |
c7 | integer | | {p1=v1,p2=v2} | plain |
c8 | text | | {p2=V2} | extended |
c9 | integer | | | plain |
c10 | integer | | {p1=v1} | plain |
Server: sc
Has OIDs: no
-- 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
@ -726,17 +747,17 @@ ERROR: relation "ft1" does not exist
ALTER FOREIGN TABLE foreign_schema.ft1 RENAME c1 TO foreign_column_1;
ALTER FOREIGN TABLE foreign_schema.ft1 RENAME TO foreign_table_1;
\d foreign_schema.foreign_table_1
Foreign table "foreign_schema.foreign_table_1"
Column | Type | Modifiers
------------------+---------+-----------
foreign_column_1 | integer | not null
c2 | text |
c3 | date |
c4 | integer |
c6 | integer | not null
c7 | integer |
c8 | text |
c10 | integer |
Foreign table "foreign_schema.foreign_table_1"
Column | Type | Modifiers | Options
------------------+---------+-----------+---------------------------
foreign_column_1 | integer | not null | {param1=val1}
c2 | text | | {param2=val2,param3=val3}
c3 | date | |
c4 | integer | |
c6 | integer | not null |
c7 | integer | | {p1=v1,p2=v2}
c8 | text | | {p2=V2}
c10 | integer | | {p1=v1}
Server: sc
-- Information schema

View File

@ -264,8 +264,8 @@ CREATE FOREIGN TABLE ft1 () SERVER no_server; -- ERROR
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,
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';
@ -288,7 +288,7 @@ ALTER FOREIGN TABLE ft1 ADD COLUMN c6 integer;
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 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
@ -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);
\d+ ft1
-- 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