Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit c4096c7

Browse files
committed
Allow per-column foreign data wrapper options.
Shigeru Hanada, with fairly minor editing by me.
1 parent 68cbb9f commit c4096c7

23 files changed

+407
-47
lines changed

doc/src/sgml/catalogs.sgml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1157,6 +1157,15 @@
11571157
</entry>
11581158
</row>
11591159

1160+
<row>
1161+
<entry><structfield>attfdwoptions</structfield></entry>
1162+
<entry><type>text[]</type></entry>
1163+
<entry></entry>
1164+
<entry>
1165+
Attribute-level foreign data wrapper options, as <quote>keyword=value</> strings
1166+
</entry>
1167+
</row>
1168+
11601169
</tbody>
11611170
</tgroup>
11621171
</table>

doc/src/sgml/information_schema.sgml

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1018,6 +1018,69 @@
10181018
</table>
10191019
</sect1>
10201020

1021+
<sect1 id="infoschema-column-options">
1022+
<title><literal>column_options</literal></title>
1023+
1024+
<para>
1025+
The view <literal>column_options</literal> contains all the
1026+
options defined for foreign table columns in the current database. Only
1027+
those foreign table columns are shown that the current user has access to
1028+
(by way of being the owner or having some privilege).
1029+
</para>
1030+
1031+
<table>
1032+
<title><literal>column_options</literal> Columns</title>
1033+
1034+
<tgroup cols="3">
1035+
<thead>
1036+
<row>
1037+
<entry>Name</entry>
1038+
<entry>Data Type</entry>
1039+
<entry>Description</entry>
1040+
</row>
1041+
</thead>
1042+
1043+
<tbody>
1044+
<row>
1045+
<entry><literal>table_catalog</literal></entry>
1046+
<entry><type>sql_identifier</type></entry>
1047+
<entry>Name of the database that contains the foreign table (always the current database)</entry>
1048+
</row>
1049+
1050+
<row>
1051+
<entry><literal>table_schema</literal></entry>
1052+
<entry><type>sql_identifier</type></entry>
1053+
<entry>Name of the schema that contains the foreign table</entry>
1054+
</row>
1055+
1056+
<row>
1057+
<entry><literal>table_name</literal></entry>
1058+
<entry><type>sql_identifier</type></entry>
1059+
<entry>Name of the foreign table</entry>
1060+
</row>
1061+
1062+
<row>
1063+
<entry><literal>column_name</literal></entry>
1064+
<entry><type>sql_identifier</type></entry>
1065+
<entry>Name of the column</entry>
1066+
</row>
1067+
1068+
<row>
1069+
<entry><literal>option_name</literal></entry>
1070+
<entry><type>sql_identifier</type></entry>
1071+
<entry>Name of an option</entry>
1072+
</row>
1073+
1074+
<row>
1075+
<entry><literal>option_value</literal></entry>
1076+
<entry><type>character_data</type></entry>
1077+
<entry>Value of the option</entry>
1078+
</row>
1079+
</tbody>
1080+
</tgroup>
1081+
</table>
1082+
</sect1>
1083+
10211084
<sect1 id="infoschema-column-privileges">
10221085
<title><literal>column_privileges</literal></title>
10231086

doc/src/sgml/ref/alter_foreign_table.sgml

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ ALTER FOREIGN TABLE <replaceable class="PARAMETER">name</replaceable>
3636
DROP [ COLUMN ] [ IF EXISTS ] <replaceable class="PARAMETER">column</replaceable> [ RESTRICT | CASCADE ]
3737
ALTER [ COLUMN ] <replaceable class="PARAMETER">column</replaceable> [ SET DATA ] TYPE <replaceable class="PARAMETER">type</replaceable>
3838
ALTER [ COLUMN ] <replaceable class="PARAMETER">column</replaceable> { SET | DROP } NOT NULL
39+
ALTER [ COLUMN ] <replaceable class="PARAMETER">column</replaceable> OPTIONS ( [ ADD | SET | DROP ] <replaceable class="PARAMETER">option</replaceable> ['<replaceable class="PARAMETER">value</replaceable>'] [, ... ])
3940
OWNER TO <replaceable class="PARAMETER">new_owner</replaceable>
4041
OPTIONS ( [ ADD | SET | DROP ] <replaceable class="PARAMETER">option</replaceable> ['<replaceable class="PARAMETER">value</replaceable>'] [, ... ])
4142
</synopsis>
@@ -125,12 +126,13 @@ ALTER FOREIGN TABLE <replaceable class="PARAMETER">name</replaceable>
125126
<term><literal>OPTIONS ( [ ADD | SET | DROP ] <replaceable class="PARAMETER">option</replaceable> ['<replaceable class="PARAMETER">value</replaceable>'] [, ... ] )</literal></term>
126127
<listitem>
127128
<para>
128-
Change options for the foreign table.
129+
Change options for the foreign table or one of its columns.
129130
<literal>ADD</>, <literal>SET</>, and <literal>DROP</>
130131
specify the action to be performed. <literal>ADD</> is assumed
131-
if no operation is explicitly specified. Option names must be
132-
unique; names and values are also validated using the foreign
133-
data wrapper library.
132+
if no operation is explicitly specified. Duplicate option names are not
133+
allowed (although it's OK for a table option and a column option to have
134+
the same name). Option names and values are also validated using the
135+
foreign data wrapper library.
134136
</para>
135137
</listitem>
136138
</varlistentry>

doc/src/sgml/ref/create_foreign_table.sgml

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
<refsynopsisdiv>
2020
<synopsis>
2121
CREATE FOREIGN TABLE [ IF NOT EXISTS ] <replaceable class="PARAMETER">table_name</replaceable> ( [
22-
{ <replaceable class="PARAMETER">column_name</replaceable> <replaceable class="PARAMETER">data_type</replaceable> [ NULL | NOT NULL ] }
22+
{ <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 ] }
2323
[, ... ]
2424
] )
2525
SERVER <replaceable class="parameter">server_name</replaceable>
@@ -138,10 +138,12 @@ CREATE FOREIGN TABLE [ IF NOT EXISTS ] <replaceable class="PARAMETER">table_name
138138
<term><literal>OPTIONS ( <replaceable class="PARAMETER">option</replaceable> '<replaceable class="PARAMETER">value</replaceable>' [, ...] )</literal></term>
139139
<listitem>
140140
<para>
141-
Options to be associated with the new foreign table.
141+
Options to be associated with the new foreign table or one of its
142+
columns.
142143
The allowed option names and values are specific to each foreign
143144
data wrapper and are validated using the foreign-data wrapper's
144-
validator function. Option names must be unique.
145+
validator function. Duplicate option names are not allowed (although
146+
it's OK for a table option and a column option to have the same name).
145147
</para>
146148
</listitem>
147149
</varlistentry>

doc/src/sgml/ref/psql-ref.sgml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -891,6 +891,12 @@ testdb=&gt;
891891
below.)
892892
</para>
893893

894+
<para>
895+
For some types of relation, <literal>\d</> shows additional information
896+
for each column: column values for sequences, indexed expression for
897+
indexes and per-column foreign data wrapper options for foreign tables.
898+
</para>
899+
894900
<para>
895901
The command form <literal>\d+</literal> is identical, except that
896902
more information is displayed: any comments associated with the

src/backend/access/common/tupdesc.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -363,7 +363,7 @@ equalTupleDescs(TupleDesc tupdesc1, TupleDesc tupdesc2)
363363
return false;
364364
if (attr1->attcollation != attr2->attcollation)
365365
return false;
366-
/* attacl and attoptions are not even present... */
366+
/* attacl, attoptions and attfdwoptions are not even present... */
367367
}
368368

369369
if (tupdesc1->constr != NULL)
@@ -483,7 +483,7 @@ TupleDescInitEntry(TupleDesc desc,
483483
att->attisdropped = false;
484484
att->attislocal = true;
485485
att->attinhcount = 0;
486-
/* attacl and attoptions are not present in tupledescs */
486+
/* attacl, attoptions and attfdwoptions are not present in tupledescs */
487487

488488
tuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(oidtypeid));
489489
if (!HeapTupleIsValid(tuple))

src/backend/catalog/genbki.pl

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -369,7 +369,8 @@ sub emit_pgattr_row
369369
attislocal => 't',
370370
attinhcount => '0',
371371
attacl => '_null_',
372-
attoptions => '_null_'
372+
attoptions => '_null_',
373+
attfdwoptions => '_null_'
373374
);
374375
return {%PGATTR_DEFAULTS, %row};
375376
}
@@ -400,6 +401,7 @@ sub emit_schemapg_row
400401
# Only the fixed-size portions of the descriptors are ever used.
401402
delete $row->{attacl};
402403
delete $row->{attoptions};
404+
delete $row->{attfdwoptions};
403405

404406
# Expand booleans from 'f'/'t' to 'false'/'true'.
405407
# Some values might be other macros (eg FLOAT4PASSBYVAL), don't change.

src/backend/catalog/heap.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ static List *insert_ordered_unique_oid(List *list, Oid datum);
126126
*/
127127

128128
/*
129-
* The initializers below do not include the attoptions or attacl fields,
129+
* The initializers below do not include trailing variable length fields,
130130
* but that's OK - we're never going to reference anything beyond the
131131
* fixed-size portion of the structure anyway.
132132
*/
@@ -620,6 +620,7 @@ InsertPgAttributeTuple(Relation pg_attribute_rel,
620620
/* start out with empty permissions and empty options */
621621
nulls[Anum_pg_attribute_attacl - 1] = true;
622622
nulls[Anum_pg_attribute_attoptions - 1] = true;
623+
nulls[Anum_pg_attribute_attfdwoptions - 1] = true;
623624

624625
tup = heap_form_tuple(RelationGetDescr(pg_attribute_rel), values, nulls);
625626

src/backend/catalog/information_schema.sql

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2534,6 +2534,39 @@ GRANT SELECT ON element_types TO PUBLIC;
25342534

25352535
-- SQL/MED views; these use section numbers from part 9 of the standard.
25362536

2537+
/* Base view for foreign table columns */
2538+
CREATE VIEW _pg_foreign_table_columns AS
2539+
SELECT n.nspname,
2540+
c.relname,
2541+
a.attname,
2542+
a.attfdwoptions
2543+
FROM pg_foreign_table t, pg_authid u, pg_namespace n, pg_class c,
2544+
pg_attribute a
2545+
WHERE u.oid = c.relowner
2546+
AND (pg_has_role(c.relowner, 'USAGE')
2547+
OR has_column_privilege(c.oid, a.attnum, 'SELECT, INSERT, UPDATE, REFERENCES'))
2548+
AND n.oid = c.relnamespace
2549+
AND c.oid = t.ftrelid
2550+
AND c.relkind = 'f'
2551+
AND a.attrelid = c.oid
2552+
AND a.attnum > 0;
2553+
2554+
/*
2555+
* 24.2
2556+
* COLUMN_OPTIONS view
2557+
*/
2558+
CREATE VIEW column_options AS
2559+
SELECT CAST(current_database() AS sql_identifier) AS table_catalog,
2560+
c.nspname AS table_schema,
2561+
c.relname AS table_name,
2562+
c.attname AS column_name,
2563+
CAST((pg_options_to_table(c.attfdwoptions)).option_name AS sql_identifier) AS option_name,
2564+
CAST((pg_options_to_table(c.attfdwoptions)).option_value AS character_data) AS option_value
2565+
FROM _pg_foreign_table_columns c;
2566+
2567+
GRANT SELECT ON column_options TO PUBLIC;
2568+
2569+
25372570
/* Base view for foreign-data wrappers */
25382571
CREATE VIEW _pg_foreign_data_wrappers AS
25392572
SELECT w.oid,

src/backend/commands/tablecmds.c

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -346,6 +346,8 @@ static void ATPrepAlterColumnType(List **wqueue,
346346
static bool ATColumnChangeRequiresRewrite(Node *expr, AttrNumber varattno);
347347
static void ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
348348
AlterTableCmd *cmd, LOCKMODE lockmode);
349+
static void ATExecAlterColumnGenericOptions(Relation rel, const char *colName,
350+
List *options, LOCKMODE lockmode);
349351
static void ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab, LOCKMODE lockmode);
350352
static void ATPostAlterTypeParse(Oid oldId, char *cmd,
351353
List **wqueue, LOCKMODE lockmode, bool rewrite);
@@ -2648,6 +2650,7 @@ AlterTableGetLockLevel(List *cmds)
26482650
case AT_DropNotNull: /* may change some SQL plans */
26492651
case AT_SetNotNull:
26502652
case AT_GenericOptions:
2653+
case AT_AlterColumnGenericOptions:
26512654
cmd_lockmode = AccessExclusiveLock;
26522655
break;
26532656

@@ -2925,6 +2928,12 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
29252928
ATPrepAlterColumnType(wqueue, tab, rel, recurse, recursing, cmd, lockmode);
29262929
pass = AT_PASS_ALTER_TYPE;
29272930
break;
2931+
case AT_AlterColumnGenericOptions:
2932+
ATSimplePermissions(rel, ATT_FOREIGN_TABLE);
2933+
/* This command never recurses */
2934+
/* No command-specific prep needed */
2935+
pass = AT_PASS_MISC;
2936+
break;
29282937
case AT_ChangeOwner: /* ALTER OWNER */
29292938
/* This command never recurses */
29302939
/* No command-specific prep needed */
@@ -3169,6 +3178,9 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
31693178
case AT_AlterColumnType: /* ALTER COLUMN TYPE */
31703179
ATExecAlterColumnType(tab, rel, cmd, lockmode);
31713180
break;
3181+
case AT_AlterColumnGenericOptions: /* ALTER COLUMN OPTIONS */
3182+
ATExecAlterColumnGenericOptions(rel, cmd->name, (List *) cmd->def, lockmode);
3183+
break;
31723184
case AT_ChangeOwner: /* ALTER OWNER */
31733185
ATExecChangeOwner(RelationGetRelid(rel),
31743186
get_role_oid(cmd->name, false),
@@ -7397,6 +7409,100 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
73977409
heap_freetuple(heapTup);
73987410
}
73997411

7412+
static void
7413+
ATExecAlterColumnGenericOptions(Relation rel,
7414+
const char *colName,
7415+
List *options,
7416+
LOCKMODE lockmode)
7417+
{
7418+
Relation ftrel;
7419+
Relation attrel;
7420+
ForeignServer *server;
7421+
ForeignDataWrapper *fdw;
7422+
HeapTuple tuple;
7423+
HeapTuple newtuple;
7424+
bool isnull;
7425+
Datum repl_val[Natts_pg_attribute];
7426+
bool repl_null[Natts_pg_attribute];
7427+
bool repl_repl[Natts_pg_attribute];
7428+
Datum datum;
7429+
Form_pg_foreign_table fttableform;
7430+
Form_pg_attribute atttableform;
7431+
7432+
if (options == NIL)
7433+
return;
7434+
7435+
/* First, determine FDW validator associated to the foreign table. */
7436+
ftrel = heap_open(ForeignTableRelationId, AccessShareLock);
7437+
tuple = SearchSysCache1(FOREIGNTABLEREL, rel->rd_id);
7438+
if (!HeapTupleIsValid(tuple))
7439+
ereport(ERROR,
7440+
(errcode(ERRCODE_UNDEFINED_OBJECT),
7441+
errmsg("foreign table \"%s\" does not exist",
7442+
RelationGetRelationName(rel))));
7443+
fttableform = (Form_pg_foreign_table) GETSTRUCT(tuple);
7444+
server = GetForeignServer(fttableform->ftserver);
7445+
fdw = GetForeignDataWrapper(server->fdwid);
7446+
7447+
heap_close(ftrel, AccessShareLock);
7448+
ReleaseSysCache(tuple);
7449+
7450+
attrel = heap_open(AttributeRelationId, RowExclusiveLock);
7451+
tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
7452+
if (!HeapTupleIsValid(tuple))
7453+
ereport(ERROR,
7454+
(errcode(ERRCODE_UNDEFINED_COLUMN),
7455+
errmsg("column \"%s\" of relation \"%s\" does not exist",
7456+
colName, RelationGetRelationName(rel))));
7457+
7458+
/* Prevent them from altering a system attribute */
7459+
atttableform = (Form_pg_attribute) GETSTRUCT(tuple);
7460+
if (atttableform->attnum <= 0)
7461+
ereport(ERROR,
7462+
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7463+
errmsg("cannot alter system column \"%s\"", colName)));
7464+
7465+
7466+
/* Initialize buffers for new tuple values */
7467+
memset(repl_val, 0, sizeof(repl_val));
7468+
memset(repl_null, false, sizeof(repl_null));
7469+
memset(repl_repl, false, sizeof(repl_repl));
7470+
7471+
/* Extract the current options */
7472+
datum = SysCacheGetAttr(ATTNAME,
7473+
tuple,
7474+
Anum_pg_attribute_attfdwoptions,
7475+
&isnull);
7476+
if (isnull)
7477+
datum = PointerGetDatum(NULL);
7478+
7479+
/* Transform the options */
7480+
datum = transformGenericOptions(AttributeRelationId,
7481+
datum,
7482+
options,
7483+
fdw->fdwvalidator);
7484+
7485+
if (PointerIsValid(DatumGetPointer(datum)))
7486+
repl_val[Anum_pg_attribute_attfdwoptions - 1] = datum;
7487+
else
7488+
repl_null[Anum_pg_attribute_attfdwoptions - 1] = true;
7489+
7490+
repl_repl[Anum_pg_attribute_attfdwoptions - 1] = true;
7491+
7492+
/* Everything looks good - update the tuple */
7493+
7494+
newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrel),
7495+
repl_val, repl_null, repl_repl);
7496+
ReleaseSysCache(tuple);
7497+
7498+
simple_heap_update(attrel, &newtuple->t_self, newtuple);
7499+
CatalogUpdateIndexes(attrel, newtuple);
7500+
7501+
heap_close(attrel, RowExclusiveLock);
7502+
7503+
heap_freetuple(newtuple);
7504+
}
7505+
74007506
/*
74017507
* Cleanup after we've finished all the ALTER TYPE operations for a
74027508
* particular relation. We have to drop and recreate all the indexes

src/backend/nodes/copyfuncs.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2312,6 +2312,7 @@ _copyColumnDef(ColumnDef *from)
23122312
COPY_NODE_FIELD(collClause);
23132313
COPY_SCALAR_FIELD(collOid);
23142314
COPY_NODE_FIELD(constraints);
2315+
COPY_NODE_FIELD(fdwoptions);
23152316

23162317
return newnode;
23172318
}

src/backend/nodes/outfuncs.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2102,6 +2102,7 @@ _outColumnDef(StringInfo str, ColumnDef *node)
21022102
WRITE_NODE_FIELD(collClause);
21032103
WRITE_OID_FIELD(collOid);
21042104
WRITE_NODE_FIELD(constraints);
2105+
WRITE_NODE_FIELD(fdwoptions);
21052106
}
21062107

21072108
static void

0 commit comments

Comments
 (0)