@@ -346,6 +346,8 @@ static void ATPrepAlterColumnType(List **wqueue,
346346static bool ATColumnChangeRequiresRewrite (Node * expr , AttrNumber varattno );
347347static 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 );
349351static void ATPostAlterTypeCleanup (List * * wqueue , AlteredTableInfo * tab , LOCKMODE lockmode );
350352static 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
0 commit comments