diff options
Diffstat (limited to 'src/backend')
| -rw-r--r-- | src/backend/access/common/tupdesc.c | 4 | ||||
| -rw-r--r-- | src/backend/catalog/genbki.pl | 4 | ||||
| -rw-r--r-- | src/backend/catalog/heap.c | 3 | ||||
| -rw-r--r-- | src/backend/catalog/information_schema.sql | 33 | ||||
| -rw-r--r-- | src/backend/commands/tablecmds.c | 106 | ||||
| -rw-r--r-- | src/backend/nodes/copyfuncs.c | 1 | ||||
| -rw-r--r-- | src/backend/nodes/outfuncs.c | 1 | ||||
| -rw-r--r-- | src/backend/parser/gram.y | 14 | ||||
| -rw-r--r-- | src/backend/parser/parse_utilcmd.c | 25 |
9 files changed, 185 insertions, 6 deletions
diff --git a/src/backend/access/common/tupdesc.c b/src/backend/access/common/tupdesc.c index 4656dba642c..9e931df4b14 100644 --- a/src/backend/access/common/tupdesc.c +++ b/src/backend/access/common/tupdesc.c @@ -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)) diff --git a/src/backend/catalog/genbki.pl b/src/backend/catalog/genbki.pl index 0aeaf5bfd76..d91af529f43 100644 --- a/src/backend/catalog/genbki.pl +++ b/src/backend/catalog/genbki.pl @@ -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. diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c index 43994936257..7ec658146f0 100644 --- a/src/backend/catalog/heap.c +++ b/src/backend/catalog/heap.c @@ -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); diff --git a/src/backend/catalog/information_schema.sql b/src/backend/catalog/information_schema.sql index 80cd091ec87..47c48bfb275 100644 --- a/src/backend/catalog/information_schema.sql +++ b/src/backend/catalog/information_schema.sql @@ -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, diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 82bb756c75e..4509cdab900 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -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 diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index 7a5145621f3..d0704ed0718 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -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; } diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index b5be09af1a0..417aeb88221 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -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 diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index ac094aa5f3a..e9f3896badb 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -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; } diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index 1be2ac68a58..21b54f7f153 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -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); + } } /* |
