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

File tree

23 files changed

+407
-47
lines changed

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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy