LCOV - code coverage report
Current view: top level - src/backend/commands - tablecmds.c (source / functions) Hit Total Coverage
Test: PostgreSQL 18devel Lines: 5969 6518 91.6 %
Date: 2024-07-27 03:11:23 Functions: 199 200 99.5 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * tablecmds.c
       4             :  *    Commands for creating and altering table structures and settings
       5             :  *
       6             :  * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
       7             :  * Portions Copyright (c) 1994, Regents of the University of California
       8             :  *
       9             :  *
      10             :  * IDENTIFICATION
      11             :  *    src/backend/commands/tablecmds.c
      12             :  *
      13             :  *-------------------------------------------------------------------------
      14             :  */
      15             : #include "postgres.h"
      16             : 
      17             : #include "access/attmap.h"
      18             : #include "access/genam.h"
      19             : #include "access/heapam.h"
      20             : #include "access/heapam_xlog.h"
      21             : #include "access/multixact.h"
      22             : #include "access/reloptions.h"
      23             : #include "access/relscan.h"
      24             : #include "access/sysattr.h"
      25             : #include "access/tableam.h"
      26             : #include "access/toast_compression.h"
      27             : #include "access/xact.h"
      28             : #include "access/xlog.h"
      29             : #include "access/xloginsert.h"
      30             : #include "catalog/catalog.h"
      31             : #include "catalog/heap.h"
      32             : #include "catalog/index.h"
      33             : #include "catalog/namespace.h"
      34             : #include "catalog/objectaccess.h"
      35             : #include "catalog/partition.h"
      36             : #include "catalog/pg_am.h"
      37             : #include "catalog/pg_attrdef.h"
      38             : #include "catalog/pg_collation.h"
      39             : #include "catalog/pg_constraint.h"
      40             : #include "catalog/pg_depend.h"
      41             : #include "catalog/pg_foreign_table.h"
      42             : #include "catalog/pg_inherits.h"
      43             : #include "catalog/pg_largeobject.h"
      44             : #include "catalog/pg_namespace.h"
      45             : #include "catalog/pg_opclass.h"
      46             : #include "catalog/pg_policy.h"
      47             : #include "catalog/pg_proc.h"
      48             : #include "catalog/pg_publication_rel.h"
      49             : #include "catalog/pg_rewrite.h"
      50             : #include "catalog/pg_statistic_ext.h"
      51             : #include "catalog/pg_tablespace.h"
      52             : #include "catalog/pg_trigger.h"
      53             : #include "catalog/pg_type.h"
      54             : #include "catalog/storage.h"
      55             : #include "catalog/storage_xlog.h"
      56             : #include "catalog/toasting.h"
      57             : #include "commands/cluster.h"
      58             : #include "commands/comment.h"
      59             : #include "commands/defrem.h"
      60             : #include "commands/event_trigger.h"
      61             : #include "commands/sequence.h"
      62             : #include "commands/tablecmds.h"
      63             : #include "commands/tablespace.h"
      64             : #include "commands/trigger.h"
      65             : #include "commands/typecmds.h"
      66             : #include "commands/user.h"
      67             : #include "commands/vacuum.h"
      68             : #include "executor/executor.h"
      69             : #include "foreign/fdwapi.h"
      70             : #include "foreign/foreign.h"
      71             : #include "miscadmin.h"
      72             : #include "nodes/makefuncs.h"
      73             : #include "nodes/nodeFuncs.h"
      74             : #include "nodes/parsenodes.h"
      75             : #include "optimizer/optimizer.h"
      76             : #include "parser/parse_coerce.h"
      77             : #include "parser/parse_collate.h"
      78             : #include "parser/parse_expr.h"
      79             : #include "parser/parse_relation.h"
      80             : #include "parser/parse_type.h"
      81             : #include "parser/parse_utilcmd.h"
      82             : #include "parser/parser.h"
      83             : #include "partitioning/partbounds.h"
      84             : #include "partitioning/partdesc.h"
      85             : #include "pgstat.h"
      86             : #include "rewrite/rewriteDefine.h"
      87             : #include "rewrite/rewriteHandler.h"
      88             : #include "rewrite/rewriteManip.h"
      89             : #include "storage/bufmgr.h"
      90             : #include "storage/lmgr.h"
      91             : #include "storage/lock.h"
      92             : #include "storage/predicate.h"
      93             : #include "storage/smgr.h"
      94             : #include "tcop/utility.h"
      95             : #include "utils/acl.h"
      96             : #include "utils/builtins.h"
      97             : #include "utils/fmgroids.h"
      98             : #include "utils/inval.h"
      99             : #include "utils/lsyscache.h"
     100             : #include "utils/memutils.h"
     101             : #include "utils/partcache.h"
     102             : #include "utils/relcache.h"
     103             : #include "utils/ruleutils.h"
     104             : #include "utils/snapmgr.h"
     105             : #include "utils/syscache.h"
     106             : #include "utils/timestamp.h"
     107             : #include "utils/typcache.h"
     108             : #include "utils/usercontext.h"
     109             : 
     110             : /*
     111             :  * ON COMMIT action list
     112             :  */
     113             : typedef struct OnCommitItem
     114             : {
     115             :     Oid         relid;          /* relid of relation */
     116             :     OnCommitAction oncommit;    /* what to do at end of xact */
     117             : 
     118             :     /*
     119             :      * If this entry was created during the current transaction,
     120             :      * creating_subid is the ID of the creating subxact; if created in a prior
     121             :      * transaction, creating_subid is zero.  If deleted during the current
     122             :      * transaction, deleting_subid is the ID of the deleting subxact; if no
     123             :      * deletion request is pending, deleting_subid is zero.
     124             :      */
     125             :     SubTransactionId creating_subid;
     126             :     SubTransactionId deleting_subid;
     127             : } OnCommitItem;
     128             : 
     129             : static List *on_commits = NIL;
     130             : 
     131             : 
     132             : /*
     133             :  * State information for ALTER TABLE
     134             :  *
     135             :  * The pending-work queue for an ALTER TABLE is a List of AlteredTableInfo
     136             :  * structs, one for each table modified by the operation (the named table
     137             :  * plus any child tables that are affected).  We save lists of subcommands
     138             :  * to apply to this table (possibly modified by parse transformation steps);
     139             :  * these lists will be executed in Phase 2.  If a Phase 3 step is needed,
     140             :  * necessary information is stored in the constraints and newvals lists.
     141             :  *
     142             :  * Phase 2 is divided into multiple passes; subcommands are executed in
     143             :  * a pass determined by subcommand type.
     144             :  */
     145             : 
     146             : typedef enum AlterTablePass
     147             : {
     148             :     AT_PASS_UNSET = -1,         /* UNSET will cause ERROR */
     149             :     AT_PASS_DROP,               /* DROP (all flavors) */
     150             :     AT_PASS_ALTER_TYPE,         /* ALTER COLUMN TYPE */
     151             :     AT_PASS_ADD_COL,            /* ADD COLUMN */
     152             :     AT_PASS_SET_EXPRESSION,     /* ALTER SET EXPRESSION */
     153             :     AT_PASS_OLD_INDEX,          /* re-add existing indexes */
     154             :     AT_PASS_OLD_CONSTR,         /* re-add existing constraints */
     155             :     /* We could support a RENAME COLUMN pass here, but not currently used */
     156             :     AT_PASS_ADD_CONSTR,         /* ADD constraints (initial examination) */
     157             :     AT_PASS_COL_ATTRS,          /* set column attributes, eg NOT NULL */
     158             :     AT_PASS_ADD_INDEXCONSTR,    /* ADD index-based constraints */
     159             :     AT_PASS_ADD_INDEX,          /* ADD indexes */
     160             :     AT_PASS_ADD_OTHERCONSTR,    /* ADD other constraints, defaults */
     161             :     AT_PASS_MISC,               /* other stuff */
     162             : } AlterTablePass;
     163             : 
     164             : #define AT_NUM_PASSES           (AT_PASS_MISC + 1)
     165             : 
     166             : typedef struct AlteredTableInfo
     167             : {
     168             :     /* Information saved before any work commences: */
     169             :     Oid         relid;          /* Relation to work on */
     170             :     char        relkind;        /* Its relkind */
     171             :     TupleDesc   oldDesc;        /* Pre-modification tuple descriptor */
     172             : 
     173             :     /*
     174             :      * Transiently set during Phase 2, normally set to NULL.
     175             :      *
     176             :      * ATRewriteCatalogs sets this when it starts, and closes when ATExecCmd
     177             :      * returns control.  This can be exploited by ATExecCmd subroutines to
     178             :      * close/reopen across transaction boundaries.
     179             :      */
     180             :     Relation    rel;
     181             : 
     182             :     /* Information saved by Phase 1 for Phase 2: */
     183             :     List       *subcmds[AT_NUM_PASSES]; /* Lists of AlterTableCmd */
     184             :     /* Information saved by Phases 1/2 for Phase 3: */
     185             :     List       *constraints;    /* List of NewConstraint */
     186             :     List       *newvals;        /* List of NewColumnValue */
     187             :     List       *afterStmts;     /* List of utility command parsetrees */
     188             :     bool        verify_new_notnull; /* T if we should recheck NOT NULL */
     189             :     int         rewrite;        /* Reason for forced rewrite, if any */
     190             :     bool        chgAccessMethod;    /* T if SET ACCESS METHOD is used */
     191             :     Oid         newAccessMethod;    /* new access method; 0 means no change,
     192             :                                      * if above is true */
     193             :     Oid         newTableSpace;  /* new tablespace; 0 means no change */
     194             :     bool        chgPersistence; /* T if SET LOGGED/UNLOGGED is used */
     195             :     char        newrelpersistence;  /* if above is true */
     196             :     Expr       *partition_constraint;   /* for attach partition validation */
     197             :     /* true, if validating default due to some other attach/detach */
     198             :     bool        validate_default;
     199             :     /* Objects to rebuild after completing ALTER TYPE operations */
     200             :     List       *changedConstraintOids;  /* OIDs of constraints to rebuild */
     201             :     List       *changedConstraintDefs;  /* string definitions of same */
     202             :     List       *changedIndexOids;   /* OIDs of indexes to rebuild */
     203             :     List       *changedIndexDefs;   /* string definitions of same */
     204             :     char       *replicaIdentityIndex;   /* index to reset as REPLICA IDENTITY */
     205             :     char       *clusterOnIndex; /* index to use for CLUSTER */
     206             :     List       *changedStatisticsOids;  /* OIDs of statistics to rebuild */
     207             :     List       *changedStatisticsDefs;  /* string definitions of same */
     208             : } AlteredTableInfo;
     209             : 
     210             : /* Struct describing one new constraint to check in Phase 3 scan */
     211             : /* Note: new not-null constraints are handled elsewhere */
     212             : typedef struct NewConstraint
     213             : {
     214             :     char       *name;           /* Constraint name, or NULL if none */
     215             :     ConstrType  contype;        /* CHECK or FOREIGN */
     216             :     Oid         refrelid;       /* PK rel, if FOREIGN */
     217             :     Oid         refindid;       /* OID of PK's index, if FOREIGN */
     218             :     Oid         conid;          /* OID of pg_constraint entry, if FOREIGN */
     219             :     Node       *qual;           /* Check expr or CONSTR_FOREIGN Constraint */
     220             :     ExprState  *qualstate;      /* Execution state for CHECK expr */
     221             : } NewConstraint;
     222             : 
     223             : /*
     224             :  * Struct describing one new column value that needs to be computed during
     225             :  * Phase 3 copy (this could be either a new column with a non-null default, or
     226             :  * a column that we're changing the type of).  Columns without such an entry
     227             :  * are just copied from the old table during ATRewriteTable.  Note that the
     228             :  * expr is an expression over *old* table values, except when is_generated
     229             :  * is true; then it is an expression over columns of the *new* tuple.
     230             :  */
     231             : typedef struct NewColumnValue
     232             : {
     233             :     AttrNumber  attnum;         /* which column */
     234             :     Expr       *expr;           /* expression to compute */
     235             :     ExprState  *exprstate;      /* execution state */
     236             :     bool        is_generated;   /* is it a GENERATED expression? */
     237             : } NewColumnValue;
     238             : 
     239             : /*
     240             :  * Error-reporting support for RemoveRelations
     241             :  */
     242             : struct dropmsgstrings
     243             : {
     244             :     char        kind;
     245             :     int         nonexistent_code;
     246             :     const char *nonexistent_msg;
     247             :     const char *skipping_msg;
     248             :     const char *nota_msg;
     249             :     const char *drophint_msg;
     250             : };
     251             : 
     252             : static const struct dropmsgstrings dropmsgstringarray[] = {
     253             :     {RELKIND_RELATION,
     254             :         ERRCODE_UNDEFINED_TABLE,
     255             :         gettext_noop("table \"%s\" does not exist"),
     256             :         gettext_noop("table \"%s\" does not exist, skipping"),
     257             :         gettext_noop("\"%s\" is not a table"),
     258             :     gettext_noop("Use DROP TABLE to remove a table.")},
     259             :     {RELKIND_SEQUENCE,
     260             :         ERRCODE_UNDEFINED_TABLE,
     261             :         gettext_noop("sequence \"%s\" does not exist"),
     262             :         gettext_noop("sequence \"%s\" does not exist, skipping"),
     263             :         gettext_noop("\"%s\" is not a sequence"),
     264             :     gettext_noop("Use DROP SEQUENCE to remove a sequence.")},
     265             :     {RELKIND_VIEW,
     266             :         ERRCODE_UNDEFINED_TABLE,
     267             :         gettext_noop("view \"%s\" does not exist"),
     268             :         gettext_noop("view \"%s\" does not exist, skipping"),
     269             :         gettext_noop("\"%s\" is not a view"),
     270             :     gettext_noop("Use DROP VIEW to remove a view.")},
     271             :     {RELKIND_MATVIEW,
     272             :         ERRCODE_UNDEFINED_TABLE,
     273             :         gettext_noop("materialized view \"%s\" does not exist"),
     274             :         gettext_noop("materialized view \"%s\" does not exist, skipping"),
     275             :         gettext_noop("\"%s\" is not a materialized view"),
     276             :     gettext_noop("Use DROP MATERIALIZED VIEW to remove a materialized view.")},
     277             :     {RELKIND_INDEX,
     278             :         ERRCODE_UNDEFINED_OBJECT,
     279             :         gettext_noop("index \"%s\" does not exist"),
     280             :         gettext_noop("index \"%s\" does not exist, skipping"),
     281             :         gettext_noop("\"%s\" is not an index"),
     282             :     gettext_noop("Use DROP INDEX to remove an index.")},
     283             :     {RELKIND_COMPOSITE_TYPE,
     284             :         ERRCODE_UNDEFINED_OBJECT,
     285             :         gettext_noop("type \"%s\" does not exist"),
     286             :         gettext_noop("type \"%s\" does not exist, skipping"),
     287             :         gettext_noop("\"%s\" is not a type"),
     288             :     gettext_noop("Use DROP TYPE to remove a type.")},
     289             :     {RELKIND_FOREIGN_TABLE,
     290             :         ERRCODE_UNDEFINED_OBJECT,
     291             :         gettext_noop("foreign table \"%s\" does not exist"),
     292             :         gettext_noop("foreign table \"%s\" does not exist, skipping"),
     293             :         gettext_noop("\"%s\" is not a foreign table"),
     294             :     gettext_noop("Use DROP FOREIGN TABLE to remove a foreign table.")},
     295             :     {RELKIND_PARTITIONED_TABLE,
     296             :         ERRCODE_UNDEFINED_TABLE,
     297             :         gettext_noop("table \"%s\" does not exist"),
     298             :         gettext_noop("table \"%s\" does not exist, skipping"),
     299             :         gettext_noop("\"%s\" is not a table"),
     300             :     gettext_noop("Use DROP TABLE to remove a table.")},
     301             :     {RELKIND_PARTITIONED_INDEX,
     302             :         ERRCODE_UNDEFINED_OBJECT,
     303             :         gettext_noop("index \"%s\" does not exist"),
     304             :         gettext_noop("index \"%s\" does not exist, skipping"),
     305             :         gettext_noop("\"%s\" is not an index"),
     306             :     gettext_noop("Use DROP INDEX to remove an index.")},
     307             :     {'\0', 0, NULL, NULL, NULL, NULL}
     308             : };
     309             : 
     310             : /* communication between RemoveRelations and RangeVarCallbackForDropRelation */
     311             : struct DropRelationCallbackState
     312             : {
     313             :     /* These fields are set by RemoveRelations: */
     314             :     char        expected_relkind;
     315             :     LOCKMODE    heap_lockmode;
     316             :     /* These fields are state to track which subsidiary locks are held: */
     317             :     Oid         heapOid;
     318             :     Oid         partParentOid;
     319             :     /* These fields are passed back by RangeVarCallbackForDropRelation: */
     320             :     char        actual_relkind;
     321             :     char        actual_relpersistence;
     322             : };
     323             : 
     324             : /* Alter table target-type flags for ATSimplePermissions */
     325             : #define     ATT_TABLE               0x0001
     326             : #define     ATT_VIEW                0x0002
     327             : #define     ATT_MATVIEW             0x0004
     328             : #define     ATT_INDEX               0x0008
     329             : #define     ATT_COMPOSITE_TYPE      0x0010
     330             : #define     ATT_FOREIGN_TABLE       0x0020
     331             : #define     ATT_PARTITIONED_INDEX   0x0040
     332             : #define     ATT_SEQUENCE            0x0080
     333             : 
     334             : /*
     335             :  * ForeignTruncateInfo
     336             :  *
     337             :  * Information related to truncation of foreign tables.  This is used for
     338             :  * the elements in a hash table. It uses the server OID as lookup key,
     339             :  * and includes a per-server list of all foreign tables involved in the
     340             :  * truncation.
     341             :  */
     342             : typedef struct ForeignTruncateInfo
     343             : {
     344             :     Oid         serverid;
     345             :     List       *rels;
     346             : } ForeignTruncateInfo;
     347             : 
     348             : /*
     349             :  * Partition tables are expected to be dropped when the parent partitioned
     350             :  * table gets dropped. Hence for partitioning we use AUTO dependency.
     351             :  * Otherwise, for regular inheritance use NORMAL dependency.
     352             :  */
     353             : #define child_dependency_type(child_is_partition)   \
     354             :     ((child_is_partition) ? DEPENDENCY_AUTO : DEPENDENCY_NORMAL)
     355             : 
     356             : static void truncate_check_rel(Oid relid, Form_pg_class reltuple);
     357             : static void truncate_check_perms(Oid relid, Form_pg_class reltuple);
     358             : static void truncate_check_activity(Relation rel);
     359             : static void RangeVarCallbackForTruncate(const RangeVar *relation,
     360             :                                         Oid relId, Oid oldRelId, void *arg);
     361             : static List *MergeAttributes(List *columns, const List *supers, char relpersistence,
     362             :                              bool is_partition, List **supconstr);
     363             : static List *MergeCheckConstraint(List *constraints, const char *name, Node *expr);
     364             : static void MergeChildAttribute(List *inh_columns, int exist_attno, int newcol_attno, const ColumnDef *newdef);
     365             : static ColumnDef *MergeInheritedAttribute(List *inh_columns, int exist_attno, const ColumnDef *newdef);
     366             : static void MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel, bool ispartition);
     367             : static void MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel);
     368             : static void StoreCatalogInheritance(Oid relationId, List *supers,
     369             :                                     bool child_is_partition);
     370             : static void StoreCatalogInheritance1(Oid relationId, Oid parentOid,
     371             :                                      int32 seqNumber, Relation inhRelation,
     372             :                                      bool child_is_partition);
     373             : static int  findAttrByName(const char *attributeName, const List *columns);
     374             : static void AlterIndexNamespaces(Relation classRel, Relation rel,
     375             :                                  Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved);
     376             : static void AlterSeqNamespaces(Relation classRel, Relation rel,
     377             :                                Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved,
     378             :                                LOCKMODE lockmode);
     379             : static ObjectAddress ATExecAlterConstraint(Relation rel, AlterTableCmd *cmd,
     380             :                                            bool recurse, bool recursing, LOCKMODE lockmode);
     381             : static bool ATExecAlterConstrRecurse(Constraint *cmdcon, Relation conrel, Relation tgrel,
     382             :                                      Relation rel, HeapTuple contuple, List **otherrelids,
     383             :                                      LOCKMODE lockmode);
     384             : static ObjectAddress ATExecValidateConstraint(List **wqueue,
     385             :                                               Relation rel, char *constrName,
     386             :                                               bool recurse, bool recursing, LOCKMODE lockmode);
     387             : static int  transformColumnNameList(Oid relId, List *colList,
     388             :                                     int16 *attnums, Oid *atttypids);
     389             : static int  transformFkeyGetPrimaryKey(Relation pkrel, Oid *indexOid,
     390             :                                        List **attnamelist,
     391             :                                        int16 *attnums, Oid *atttypids,
     392             :                                        Oid *opclasses);
     393             : static Oid  transformFkeyCheckAttrs(Relation pkrel,
     394             :                                     int numattrs, int16 *attnums,
     395             :                                     Oid *opclasses);
     396             : static void checkFkeyPermissions(Relation rel, int16 *attnums, int natts);
     397             : static CoercionPathType findFkeyCast(Oid targetTypeId, Oid sourceTypeId,
     398             :                                      Oid *funcid);
     399             : static void validateForeignKeyConstraint(char *conname,
     400             :                                          Relation rel, Relation pkrel,
     401             :                                          Oid pkindOid, Oid constraintOid);
     402             : static void CheckAlterTableIsSafe(Relation rel);
     403             : static void ATController(AlterTableStmt *parsetree,
     404             :                          Relation rel, List *cmds, bool recurse, LOCKMODE lockmode,
     405             :                          AlterTableUtilityContext *context);
     406             : static void ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
     407             :                       bool recurse, bool recursing, LOCKMODE lockmode,
     408             :                       AlterTableUtilityContext *context);
     409             : static void ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode,
     410             :                               AlterTableUtilityContext *context);
     411             : static void ATExecCmd(List **wqueue, AlteredTableInfo *tab,
     412             :                       AlterTableCmd *cmd, LOCKMODE lockmode, AlterTablePass cur_pass,
     413             :                       AlterTableUtilityContext *context);
     414             : static AlterTableCmd *ATParseTransformCmd(List **wqueue, AlteredTableInfo *tab,
     415             :                                           Relation rel, AlterTableCmd *cmd,
     416             :                                           bool recurse, LOCKMODE lockmode,
     417             :                                           AlterTablePass cur_pass,
     418             :                                           AlterTableUtilityContext *context);
     419             : static void ATRewriteTables(AlterTableStmt *parsetree,
     420             :                             List **wqueue, LOCKMODE lockmode,
     421             :                             AlterTableUtilityContext *context);
     422             : static void ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode);
     423             : static AlteredTableInfo *ATGetQueueEntry(List **wqueue, Relation rel);
     424             : static void ATSimplePermissions(AlterTableType cmdtype, Relation rel, int allowed_targets);
     425             : static void ATSimpleRecursion(List **wqueue, Relation rel,
     426             :                               AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode,
     427             :                               AlterTableUtilityContext *context);
     428             : static void ATCheckPartitionsNotInUse(Relation rel, LOCKMODE lockmode);
     429             : static void ATTypedTableRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd,
     430             :                                   LOCKMODE lockmode,
     431             :                                   AlterTableUtilityContext *context);
     432             : static List *find_typed_table_dependencies(Oid typeOid, const char *typeName,
     433             :                                            DropBehavior behavior);
     434             : static void ATPrepAddColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
     435             :                             bool is_view, AlterTableCmd *cmd, LOCKMODE lockmode,
     436             :                             AlterTableUtilityContext *context);
     437             : static ObjectAddress ATExecAddColumn(List **wqueue, AlteredTableInfo *tab,
     438             :                                      Relation rel, AlterTableCmd **cmd,
     439             :                                      bool recurse, bool recursing,
     440             :                                      LOCKMODE lockmode, AlterTablePass cur_pass,
     441             :                                      AlterTableUtilityContext *context);
     442             : static bool check_for_column_name_collision(Relation rel, const char *colname,
     443             :                                             bool if_not_exists);
     444             : static void add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid);
     445             : static void add_column_collation_dependency(Oid relid, int32 attnum, Oid collid);
     446             : static void ATPrepDropNotNull(Relation rel, bool recurse, bool recursing);
     447             : static ObjectAddress ATExecDropNotNull(Relation rel, const char *colName, LOCKMODE lockmode);
     448             : static void ATPrepSetNotNull(List **wqueue, Relation rel,
     449             :                              AlterTableCmd *cmd, bool recurse, bool recursing,
     450             :                              LOCKMODE lockmode,
     451             :                              AlterTableUtilityContext *context);
     452             : static ObjectAddress ATExecSetNotNull(AlteredTableInfo *tab, Relation rel,
     453             :                                       const char *colName, LOCKMODE lockmode);
     454             : static void ATExecCheckNotNull(AlteredTableInfo *tab, Relation rel,
     455             :                                const char *colName, LOCKMODE lockmode);
     456             : static bool NotNullImpliedByRelConstraints(Relation rel, Form_pg_attribute attr);
     457             : static bool ConstraintImpliedByRelConstraint(Relation scanrel,
     458             :                                              List *testConstraint, List *provenConstraint);
     459             : static ObjectAddress ATExecColumnDefault(Relation rel, const char *colName,
     460             :                                          Node *newDefault, LOCKMODE lockmode);
     461             : static ObjectAddress ATExecCookedColumnDefault(Relation rel, AttrNumber attnum,
     462             :                                                Node *newDefault);
     463             : static ObjectAddress ATExecAddIdentity(Relation rel, const char *colName,
     464             :                                        Node *def, LOCKMODE lockmode, bool recurse, bool recursing);
     465             : static ObjectAddress ATExecSetIdentity(Relation rel, const char *colName,
     466             :                                        Node *def, LOCKMODE lockmode, bool recurse, bool recursing);
     467             : static ObjectAddress ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode,
     468             :                                         bool recurse, bool recursing);
     469             : static ObjectAddress ATExecSetExpression(AlteredTableInfo *tab, Relation rel, const char *colName,
     470             :                                          Node *newExpr, LOCKMODE lockmode);
     471             : static void ATPrepDropExpression(Relation rel, AlterTableCmd *cmd, bool recurse, bool recursing, LOCKMODE lockmode);
     472             : static ObjectAddress ATExecDropExpression(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode);
     473             : static ObjectAddress ATExecSetStatistics(Relation rel, const char *colName, int16 colNum,
     474             :                                          Node *newValue, LOCKMODE lockmode);
     475             : static ObjectAddress ATExecSetOptions(Relation rel, const char *colName,
     476             :                                       Node *options, bool isReset, LOCKMODE lockmode);
     477             : static ObjectAddress ATExecSetStorage(Relation rel, const char *colName,
     478             :                                       Node *newValue, LOCKMODE lockmode);
     479             : static void ATPrepDropColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
     480             :                              AlterTableCmd *cmd, LOCKMODE lockmode,
     481             :                              AlterTableUtilityContext *context);
     482             : static ObjectAddress ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
     483             :                                       DropBehavior behavior,
     484             :                                       bool recurse, bool recursing,
     485             :                                       bool missing_ok, LOCKMODE lockmode,
     486             :                                       ObjectAddresses *addrs);
     487             : static ObjectAddress ATExecAddIndex(AlteredTableInfo *tab, Relation rel,
     488             :                                     IndexStmt *stmt, bool is_rebuild, LOCKMODE lockmode);
     489             : static ObjectAddress ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
     490             :                                          CreateStatsStmt *stmt, bool is_rebuild, LOCKMODE lockmode);
     491             : static ObjectAddress ATExecAddConstraint(List **wqueue,
     492             :                                          AlteredTableInfo *tab, Relation rel,
     493             :                                          Constraint *newConstraint, bool recurse, bool is_readd,
     494             :                                          LOCKMODE lockmode);
     495             : static char *ChooseForeignKeyConstraintNameAddition(List *colnames);
     496             : static ObjectAddress ATExecAddIndexConstraint(AlteredTableInfo *tab, Relation rel,
     497             :                                               IndexStmt *stmt, LOCKMODE lockmode);
     498             : static ObjectAddress ATAddCheckConstraint(List **wqueue,
     499             :                                           AlteredTableInfo *tab, Relation rel,
     500             :                                           Constraint *constr,
     501             :                                           bool recurse, bool recursing, bool is_readd,
     502             :                                           LOCKMODE lockmode);
     503             : static ObjectAddress ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab,
     504             :                                                Relation rel, Constraint *fkconstraint,
     505             :                                                bool recurse, bool recursing,
     506             :                                                LOCKMODE lockmode);
     507             : static ObjectAddress addFkRecurseReferenced(List **wqueue, Constraint *fkconstraint,
     508             :                                             Relation rel, Relation pkrel, Oid indexOid, Oid parentConstr,
     509             :                                             int numfks, int16 *pkattnum, int16 *fkattnum,
     510             :                                             Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators,
     511             :                                             int numfkdelsetcols, int16 *fkdelsetcols,
     512             :                                             bool old_check_ok,
     513             :                                             Oid parentDelTrigger, Oid parentUpdTrigger);
     514             : static void validateFkOnDeleteSetColumns(int numfks, const int16 *fkattnums,
     515             :                                          int numfksetcols, const int16 *fksetcolsattnums,
     516             :                                          List *fksetcols);
     517             : static void addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint,
     518             :                                     Relation rel, Relation pkrel, Oid indexOid, Oid parentConstr,
     519             :                                     int numfks, int16 *pkattnum, int16 *fkattnum,
     520             :                                     Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators,
     521             :                                     int numfkdelsetcols, int16 *fkdelsetcols,
     522             :                                     bool old_check_ok, LOCKMODE lockmode,
     523             :                                     Oid parentInsTrigger, Oid parentUpdTrigger);
     524             : static void CloneForeignKeyConstraints(List **wqueue, Relation parentRel,
     525             :                                        Relation partitionRel);
     526             : static void CloneFkReferenced(Relation parentRel, Relation partitionRel);
     527             : static void CloneFkReferencing(List **wqueue, Relation parentRel,
     528             :                                Relation partRel);
     529             : static void createForeignKeyCheckTriggers(Oid myRelOid, Oid refRelOid,
     530             :                                           Constraint *fkconstraint, Oid constraintOid,
     531             :                                           Oid indexOid,
     532             :                                           Oid parentInsTrigger, Oid parentUpdTrigger,
     533             :                                           Oid *insertTrigOid, Oid *updateTrigOid);
     534             : static void createForeignKeyActionTriggers(Relation rel, Oid refRelOid,
     535             :                                            Constraint *fkconstraint, Oid constraintOid,
     536             :                                            Oid indexOid,
     537             :                                            Oid parentDelTrigger, Oid parentUpdTrigger,
     538             :                                            Oid *deleteTrigOid, Oid *updateTrigOid);
     539             : static bool tryAttachPartitionForeignKey(ForeignKeyCacheInfo *fk,
     540             :                                          Oid partRelid,
     541             :                                          Oid parentConstrOid, int numfks,
     542             :                                          AttrNumber *mapped_conkey, AttrNumber *confkey,
     543             :                                          Oid *conpfeqop,
     544             :                                          Oid parentInsTrigger,
     545             :                                          Oid parentUpdTrigger,
     546             :                                          Relation trigrel);
     547             : static void GetForeignKeyActionTriggers(Relation trigrel,
     548             :                                         Oid conoid, Oid confrelid, Oid conrelid,
     549             :                                         Oid *deleteTriggerOid,
     550             :                                         Oid *updateTriggerOid);
     551             : static void GetForeignKeyCheckTriggers(Relation trigrel,
     552             :                                        Oid conoid, Oid confrelid, Oid conrelid,
     553             :                                        Oid *insertTriggerOid,
     554             :                                        Oid *updateTriggerOid);
     555             : static void ATExecDropConstraint(Relation rel, const char *constrName,
     556             :                                  DropBehavior behavior,
     557             :                                  bool recurse, bool recursing,
     558             :                                  bool missing_ok, LOCKMODE lockmode);
     559             : static void ATPrepAlterColumnType(List **wqueue,
     560             :                                   AlteredTableInfo *tab, Relation rel,
     561             :                                   bool recurse, bool recursing,
     562             :                                   AlterTableCmd *cmd, LOCKMODE lockmode,
     563             :                                   AlterTableUtilityContext *context);
     564             : static bool ATColumnChangeRequiresRewrite(Node *expr, AttrNumber varattno);
     565             : static ObjectAddress ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
     566             :                                            AlterTableCmd *cmd, LOCKMODE lockmode);
     567             : static void RememberAllDependentForRebuilding(AlteredTableInfo *tab, AlterTableType subtype,
     568             :                                               Relation rel, AttrNumber attnum, const char *colName);
     569             : static void RememberConstraintForRebuilding(Oid conoid, AlteredTableInfo *tab);
     570             : static void RememberIndexForRebuilding(Oid indoid, AlteredTableInfo *tab);
     571             : static void RememberStatisticsForRebuilding(Oid stxoid, AlteredTableInfo *tab);
     572             : static void ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab,
     573             :                                    LOCKMODE lockmode);
     574             : static void ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId,
     575             :                                  char *cmd, List **wqueue, LOCKMODE lockmode,
     576             :                                  bool rewrite);
     577             : static void RebuildConstraintComment(AlteredTableInfo *tab, AlterTablePass pass,
     578             :                                      Oid objid, Relation rel, List *domname,
     579             :                                      const char *conname);
     580             : static void TryReuseIndex(Oid oldId, IndexStmt *stmt);
     581             : static void TryReuseForeignKey(Oid oldId, Constraint *con);
     582             : static ObjectAddress ATExecAlterColumnGenericOptions(Relation rel, const char *colName,
     583             :                                                      List *options, LOCKMODE lockmode);
     584             : static void change_owner_fix_column_acls(Oid relationOid,
     585             :                                          Oid oldOwnerId, Oid newOwnerId);
     586             : static void change_owner_recurse_to_sequences(Oid relationOid,
     587             :                                               Oid newOwnerId, LOCKMODE lockmode);
     588             : static ObjectAddress ATExecClusterOn(Relation rel, const char *indexName,
     589             :                                      LOCKMODE lockmode);
     590             : static void ATExecDropCluster(Relation rel, LOCKMODE lockmode);
     591             : static void ATPrepSetAccessMethod(AlteredTableInfo *tab, Relation rel, const char *amname);
     592             : static void ATExecSetAccessMethodNoStorage(Relation rel, Oid newAccessMethodId);
     593             : static bool ATPrepChangePersistence(Relation rel, bool toLogged);
     594             : static void ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel,
     595             :                                 const char *tablespacename, LOCKMODE lockmode);
     596             : static void ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode);
     597             : static void ATExecSetTableSpaceNoStorage(Relation rel, Oid newTableSpace);
     598             : static void ATExecSetRelOptions(Relation rel, List *defList,
     599             :                                 AlterTableType operation,
     600             :                                 LOCKMODE lockmode);
     601             : static void ATExecEnableDisableTrigger(Relation rel, const char *trigname,
     602             :                                        char fires_when, bool skip_system, bool recurse,
     603             :                                        LOCKMODE lockmode);
     604             : static void ATExecEnableDisableRule(Relation rel, const char *rulename,
     605             :                                     char fires_when, LOCKMODE lockmode);
     606             : static void ATPrepAddInherit(Relation child_rel);
     607             : static ObjectAddress ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode);
     608             : static ObjectAddress ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode);
     609             : static void drop_parent_dependency(Oid relid, Oid refclassid, Oid refobjid,
     610             :                                    DependencyType deptype);
     611             : static ObjectAddress ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode);
     612             : static void ATExecDropOf(Relation rel, LOCKMODE lockmode);
     613             : static void ATExecReplicaIdentity(Relation rel, ReplicaIdentityStmt *stmt, LOCKMODE lockmode);
     614             : static void ATExecGenericOptions(Relation rel, List *options);
     615             : static void ATExecSetRowSecurity(Relation rel, bool rls);
     616             : static void ATExecForceNoForceRowSecurity(Relation rel, bool force_rls);
     617             : static ObjectAddress ATExecSetCompression(Relation rel,
     618             :                                           const char *column, Node *newValue, LOCKMODE lockmode);
     619             : 
     620             : static void index_copy_data(Relation rel, RelFileLocator newrlocator);
     621             : static const char *storage_name(char c);
     622             : 
     623             : static void RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid,
     624             :                                             Oid oldRelOid, void *arg);
     625             : static void RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid,
     626             :                                              Oid oldrelid, void *arg);
     627             : static PartitionSpec *transformPartitionSpec(Relation rel, PartitionSpec *partspec);
     628             : static void ComputePartitionAttrs(ParseState *pstate, Relation rel, List *partParams, AttrNumber *partattrs,
     629             :                                   List **partexprs, Oid *partopclass, Oid *partcollation,
     630             :                                   PartitionStrategy strategy);
     631             : static void CreateInheritance(Relation child_rel, Relation parent_rel, bool ispartition);
     632             : static void RemoveInheritance(Relation child_rel, Relation parent_rel,
     633             :                               bool expect_detached);
     634             : static ObjectAddress ATExecAttachPartition(List **wqueue, Relation rel,
     635             :                                            PartitionCmd *cmd,
     636             :                                            AlterTableUtilityContext *context);
     637             : static void AttachPartitionEnsureIndexes(List **wqueue, Relation rel, Relation attachrel);
     638             : static void QueuePartitionConstraintValidation(List **wqueue, Relation scanrel,
     639             :                                                List *partConstraint,
     640             :                                                bool validate_default);
     641             : static void CloneRowTriggersToPartition(Relation parent, Relation partition);
     642             : static void DetachAddConstraintIfNeeded(List **wqueue, Relation partRel);
     643             : static void DropClonedTriggersFromPartition(Oid partitionId);
     644             : static ObjectAddress ATExecDetachPartition(List **wqueue, AlteredTableInfo *tab,
     645             :                                            Relation rel, RangeVar *name,
     646             :                                            bool concurrent);
     647             : static void DetachPartitionFinalize(Relation rel, Relation partRel,
     648             :                                     bool concurrent, Oid defaultPartOid);
     649             : static ObjectAddress ATExecDetachPartitionFinalize(Relation rel, RangeVar *name);
     650             : static ObjectAddress ATExecAttachPartitionIdx(List **wqueue, Relation parentIdx,
     651             :                                               RangeVar *name);
     652             : static void validatePartitionedIndex(Relation partedIdx, Relation partedTbl);
     653             : static void refuseDupeIndexAttach(Relation parentIdx, Relation partIdx,
     654             :                                   Relation partitionTbl);
     655             : static List *GetParentedForeignKeyRefs(Relation partition);
     656             : static void ATDetachCheckNoForeignKeyRefs(Relation partition);
     657             : static char GetAttributeCompression(Oid atttypid, const char *compression);
     658             : static char GetAttributeStorage(Oid atttypid, const char *storagemode);
     659             : 
     660             : static void ATExecSplitPartition(List **wqueue, AlteredTableInfo *tab,
     661             :                                  Relation rel, PartitionCmd *cmd,
     662             :                                  AlterTableUtilityContext *context);
     663             : static void ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel,
     664             :                                   PartitionCmd *cmd, AlterTableUtilityContext *context);
     665             : 
     666             : /* ----------------------------------------------------------------
     667             :  *      DefineRelation
     668             :  *              Creates a new relation.
     669             :  *
     670             :  * stmt carries parsetree information from an ordinary CREATE TABLE statement.
     671             :  * The other arguments are used to extend the behavior for other cases:
     672             :  * relkind: relkind to assign to the new relation
     673             :  * ownerId: if not InvalidOid, use this as the new relation's owner.
     674             :  * typaddress: if not null, it's set to the pg_type entry's address.
     675             :  * queryString: for error reporting
     676             :  *
     677             :  * Note that permissions checks are done against current user regardless of
     678             :  * ownerId.  A nonzero ownerId is used when someone is creating a relation
     679             :  * "on behalf of" someone else, so we still want to see that the current user
     680             :  * has permissions to do it.
     681             :  *
     682             :  * If successful, returns the address of the new relation.
     683             :  * ----------------------------------------------------------------
     684             :  */
     685             : ObjectAddress
     686       52762 : DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
     687             :                ObjectAddress *typaddress, const char *queryString)
     688             : {
     689             :     char        relname[NAMEDATALEN];
     690             :     Oid         namespaceId;
     691             :     Oid         relationId;
     692             :     Oid         tablespaceId;
     693             :     Relation    rel;
     694             :     TupleDesc   descriptor;
     695             :     List       *inheritOids;
     696             :     List       *old_constraints;
     697             :     List       *rawDefaults;
     698             :     List       *cookedDefaults;
     699             :     Datum       reloptions;
     700             :     ListCell   *listptr;
     701             :     AttrNumber  attnum;
     702             :     bool        partitioned;
     703             :     static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
     704             :     Oid         ofTypeId;
     705             :     ObjectAddress address;
     706             :     LOCKMODE    parentLockmode;
     707       52762 :     Oid         accessMethodId = InvalidOid;
     708             : 
     709             :     /*
     710             :      * Truncate relname to appropriate length (probably a waste of time, as
     711             :      * parser should have done this already).
     712             :      */
     713       52762 :     strlcpy(relname, stmt->relation->relname, NAMEDATALEN);
     714             : 
     715             :     /*
     716             :      * Check consistency of arguments
     717             :      */
     718       52762 :     if (stmt->oncommit != ONCOMMIT_NOOP
     719         178 :         && stmt->relation->relpersistence != RELPERSISTENCE_TEMP)
     720          12 :         ereport(ERROR,
     721             :                 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
     722             :                  errmsg("ON COMMIT can only be used on temporary tables")));
     723             : 
     724       52750 :     if (stmt->partspec != NULL)
     725             :     {
     726        4948 :         if (relkind != RELKIND_RELATION)
     727           0 :             elog(ERROR, "unexpected relkind: %d", (int) relkind);
     728             : 
     729        4948 :         relkind = RELKIND_PARTITIONED_TABLE;
     730        4948 :         partitioned = true;
     731             :     }
     732             :     else
     733       47802 :         partitioned = false;
     734             : 
     735             :     /*
     736             :      * Look up the namespace in which we are supposed to create the relation,
     737             :      * check we have permission to create there, lock it against concurrent
     738             :      * drop, and mark stmt->relation as RELPERSISTENCE_TEMP if a temporary
     739             :      * namespace is selected.
     740             :      */
     741             :     namespaceId =
     742       52750 :         RangeVarGetAndCheckCreationNamespace(stmt->relation, NoLock, NULL);
     743             : 
     744             :     /*
     745             :      * Security check: disallow creating temp tables from security-restricted
     746             :      * code.  This is needed because calling code might not expect untrusted
     747             :      * tables to appear in pg_temp at the front of its search path.
     748             :      */
     749       52750 :     if (stmt->relation->relpersistence == RELPERSISTENCE_TEMP
     750        3118 :         && InSecurityRestrictedOperation())
     751           0 :         ereport(ERROR,
     752             :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
     753             :                  errmsg("cannot create temporary table within security-restricted operation")));
     754             : 
     755             :     /*
     756             :      * Determine the lockmode to use when scanning parents.  A self-exclusive
     757             :      * lock is needed here.
     758             :      *
     759             :      * For regular inheritance, if two backends attempt to add children to the
     760             :      * same parent simultaneously, and that parent has no pre-existing
     761             :      * children, then both will attempt to update the parent's relhassubclass
     762             :      * field, leading to a "tuple concurrently updated" error.  Also, this
     763             :      * interlocks against a concurrent ANALYZE on the parent table, which
     764             :      * might otherwise be attempting to clear the parent's relhassubclass
     765             :      * field, if its previous children were recently dropped.
     766             :      *
     767             :      * If the child table is a partition, then we instead grab an exclusive
     768             :      * lock on the parent because its partition descriptor will be changed by
     769             :      * addition of the new partition.
     770             :      */
     771       52750 :     parentLockmode = (stmt->partbound != NULL ? AccessExclusiveLock :
     772             :                       ShareUpdateExclusiveLock);
     773             : 
     774             :     /* Determine the list of OIDs of the parents. */
     775       52750 :     inheritOids = NIL;
     776       62938 :     foreach(listptr, stmt->inhRelations)
     777             :     {
     778       10188 :         RangeVar   *rv = (RangeVar *) lfirst(listptr);
     779             :         Oid         parentOid;
     780             : 
     781       10188 :         parentOid = RangeVarGetRelid(rv, parentLockmode, false);
     782             : 
     783             :         /*
     784             :          * Reject duplications in the list of parents.
     785             :          */
     786       10188 :         if (list_member_oid(inheritOids, parentOid))
     787           0 :             ereport(ERROR,
     788             :                     (errcode(ERRCODE_DUPLICATE_TABLE),
     789             :                      errmsg("relation \"%s\" would be inherited from more than once",
     790             :                             get_rel_name(parentOid))));
     791             : 
     792       10188 :         inheritOids = lappend_oid(inheritOids, parentOid);
     793             :     }
     794             : 
     795             :     /*
     796             :      * Select tablespace to use: an explicitly indicated one, or (in the case
     797             :      * of a partitioned table) the parent's, if it has one.
     798             :      */
     799       52750 :     if (stmt->tablespacename)
     800             :     {
     801         146 :         tablespaceId = get_tablespace_oid(stmt->tablespacename, false);
     802             : 
     803         140 :         if (partitioned && tablespaceId == MyDatabaseTableSpace)
     804           6 :             ereport(ERROR,
     805             :                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     806             :                      errmsg("cannot specify default tablespace for partitioned relations")));
     807             :     }
     808       52604 :     else if (stmt->partbound)
     809             :     {
     810             :         Assert(list_length(inheritOids) == 1);
     811        8356 :         tablespaceId = get_rel_tablespace(linitial_oid(inheritOids));
     812             :     }
     813             :     else
     814       44248 :         tablespaceId = InvalidOid;
     815             : 
     816             :     /* still nothing? use the default */
     817       52738 :     if (!OidIsValid(tablespaceId))
     818       52564 :         tablespaceId = GetDefaultTablespace(stmt->relation->relpersistence,
     819             :                                             partitioned);
     820             : 
     821             :     /* Check permissions except when using database's default */
     822       52732 :     if (OidIsValid(tablespaceId) && tablespaceId != MyDatabaseTableSpace)
     823             :     {
     824             :         AclResult   aclresult;
     825             : 
     826         192 :         aclresult = object_aclcheck(TableSpaceRelationId, tablespaceId, GetUserId(),
     827             :                                     ACL_CREATE);
     828         192 :         if (aclresult != ACLCHECK_OK)
     829           6 :             aclcheck_error(aclresult, OBJECT_TABLESPACE,
     830           6 :                            get_tablespace_name(tablespaceId));
     831             :     }
     832             : 
     833             :     /* In all cases disallow placing user relations in pg_global */
     834       52726 :     if (tablespaceId == GLOBALTABLESPACE_OID)
     835          18 :         ereport(ERROR,
     836             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     837             :                  errmsg("only shared relations can be placed in pg_global tablespace")));
     838             : 
     839             :     /* Identify user ID that will own the table */
     840       52708 :     if (!OidIsValid(ownerId))
     841       52472 :         ownerId = GetUserId();
     842             : 
     843             :     /*
     844             :      * Parse and validate reloptions, if any.
     845             :      */
     846       52708 :     reloptions = transformRelOptions((Datum) 0, stmt->options, NULL, validnsps,
     847             :                                      true, false);
     848             : 
     849       52690 :     switch (relkind)
     850             :     {
     851       13384 :         case RELKIND_VIEW:
     852       13384 :             (void) view_reloptions(reloptions, true);
     853       13366 :             break;
     854        4930 :         case RELKIND_PARTITIONED_TABLE:
     855        4930 :             (void) partitioned_table_reloptions(reloptions, true);
     856        4924 :             break;
     857       34376 :         default:
     858       34376 :             (void) heap_reloptions(relkind, reloptions, true);
     859             :     }
     860             : 
     861       52570 :     if (stmt->ofTypename)
     862             :     {
     863             :         AclResult   aclresult;
     864             : 
     865          86 :         ofTypeId = typenameTypeId(NULL, stmt->ofTypename);
     866             : 
     867          86 :         aclresult = object_aclcheck(TypeRelationId, ofTypeId, GetUserId(), ACL_USAGE);
     868          86 :         if (aclresult != ACLCHECK_OK)
     869           6 :             aclcheck_error_type(aclresult, ofTypeId);
     870             :     }
     871             :     else
     872       52484 :         ofTypeId = InvalidOid;
     873             : 
     874             :     /*
     875             :      * Look up inheritance ancestors and generate relation schema, including
     876             :      * inherited attributes.  (Note that stmt->tableElts is destructively
     877             :      * modified by MergeAttributes.)
     878             :      */
     879       52396 :     stmt->tableElts =
     880       52564 :         MergeAttributes(stmt->tableElts, inheritOids,
     881       52564 :                         stmt->relation->relpersistence,
     882       52564 :                         stmt->partbound != NULL,
     883             :                         &old_constraints);
     884             : 
     885             :     /*
     886             :      * Create a tuple descriptor from the relation schema.  Note that this
     887             :      * deals with column names, types, and not-null constraints, but not
     888             :      * default values or CHECK constraints; we handle those below.
     889             :      */
     890       52396 :     descriptor = BuildDescForRelation(stmt->tableElts);
     891             : 
     892             :     /*
     893             :      * Find columns with default values and prepare for insertion of the
     894             :      * defaults.  Pre-cooked (that is, inherited) defaults go into a list of
     895             :      * CookedConstraint structs that we'll pass to heap_create_with_catalog,
     896             :      * while raw defaults go into a list of RawColumnDefault structs that will
     897             :      * be processed by AddRelationNewConstraints.  (We can't deal with raw
     898             :      * expressions until we can do transformExpr.)
     899             :      *
     900             :      * We can set the atthasdef flags now in the tuple descriptor; this just
     901             :      * saves StoreAttrDefault from having to do an immediate update of the
     902             :      * pg_attribute rows.
     903             :      */
     904       52348 :     rawDefaults = NIL;
     905       52348 :     cookedDefaults = NIL;
     906       52348 :     attnum = 0;
     907             : 
     908      266010 :     foreach(listptr, stmt->tableElts)
     909             :     {
     910      213662 :         ColumnDef  *colDef = lfirst(listptr);
     911             :         Form_pg_attribute attr;
     912             : 
     913      213662 :         attnum++;
     914      213662 :         attr = TupleDescAttr(descriptor, attnum - 1);
     915             : 
     916      213662 :         if (colDef->raw_default != NULL)
     917             :         {
     918             :             RawColumnDefault *rawEnt;
     919             : 
     920             :             Assert(colDef->cooked_default == NULL);
     921             : 
     922        2388 :             rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
     923        2388 :             rawEnt->attnum = attnum;
     924        2388 :             rawEnt->raw_default = colDef->raw_default;
     925        2388 :             rawEnt->missingMode = false;
     926        2388 :             rawEnt->generated = colDef->generated;
     927        2388 :             rawDefaults = lappend(rawDefaults, rawEnt);
     928        2388 :             attr->atthasdef = true;
     929             :         }
     930      211274 :         else if (colDef->cooked_default != NULL)
     931             :         {
     932             :             CookedConstraint *cooked;
     933             : 
     934         446 :             cooked = (CookedConstraint *) palloc(sizeof(CookedConstraint));
     935         446 :             cooked->contype = CONSTR_DEFAULT;
     936         446 :             cooked->conoid = InvalidOid; /* until created */
     937         446 :             cooked->name = NULL;
     938         446 :             cooked->attnum = attnum;
     939         446 :             cooked->expr = colDef->cooked_default;
     940         446 :             cooked->skip_validation = false;
     941         446 :             cooked->is_local = true; /* not used for defaults */
     942         446 :             cooked->inhcount = 0;    /* ditto */
     943         446 :             cooked->is_no_inherit = false;
     944         446 :             cookedDefaults = lappend(cookedDefaults, cooked);
     945         446 :             attr->atthasdef = true;
     946             :         }
     947             :     }
     948             : 
     949             :     /*
     950             :      * For relations with table AM and partitioned tables, select access
     951             :      * method to use: an explicitly indicated one, or (in the case of a
     952             :      * partitioned table) the parent's, if it has one.
     953             :      */
     954       52348 :     if (stmt->accessMethod != NULL)
     955             :     {
     956             :         Assert(RELKIND_HAS_TABLE_AM(relkind) || relkind == RELKIND_PARTITIONED_TABLE);
     957         146 :         accessMethodId = get_table_am_oid(stmt->accessMethod, false);
     958             :     }
     959       52202 :     else if (RELKIND_HAS_TABLE_AM(relkind) || relkind == RELKIND_PARTITIONED_TABLE)
     960             :     {
     961       36062 :         if (stmt->partbound)
     962             :         {
     963             :             Assert(list_length(inheritOids) == 1);
     964        8198 :             accessMethodId = get_rel_relam(linitial_oid(inheritOids));
     965             :         }
     966             : 
     967       36062 :         if (RELKIND_HAS_TABLE_AM(relkind) && !OidIsValid(accessMethodId))
     968       31112 :             accessMethodId = get_table_am_oid(default_table_access_method, false);
     969             :     }
     970             : 
     971             :     /*
     972             :      * Create the relation.  Inherited defaults and constraints are passed in
     973             :      * for immediate handling --- since they don't need parsing, they can be
     974             :      * stored immediately.
     975             :      */
     976       52330 :     relationId = heap_create_with_catalog(relname,
     977             :                                           namespaceId,
     978             :                                           tablespaceId,
     979             :                                           InvalidOid,
     980             :                                           InvalidOid,
     981             :                                           ofTypeId,
     982             :                                           ownerId,
     983             :                                           accessMethodId,
     984             :                                           descriptor,
     985             :                                           list_concat(cookedDefaults,
     986             :                                                       old_constraints),
     987             :                                           relkind,
     988       52330 :                                           stmt->relation->relpersistence,
     989             :                                           false,
     990             :                                           false,
     991             :                                           stmt->oncommit,
     992             :                                           reloptions,
     993             :                                           true,
     994             :                                           allowSystemTableMods,
     995             :                                           false,
     996             :                                           InvalidOid,
     997             :                                           typaddress);
     998             : 
     999             :     /*
    1000             :      * We must bump the command counter to make the newly-created relation
    1001             :      * tuple visible for opening.
    1002             :      */
    1003       52306 :     CommandCounterIncrement();
    1004             : 
    1005             :     /*
    1006             :      * Open the new relation and acquire exclusive lock on it.  This isn't
    1007             :      * really necessary for locking out other backends (since they can't see
    1008             :      * the new rel anyway until we commit), but it keeps the lock manager from
    1009             :      * complaining about deadlock risks.
    1010             :      */
    1011       52306 :     rel = relation_open(relationId, AccessExclusiveLock);
    1012             : 
    1013             :     /*
    1014             :      * Now add any newly specified column default and generation expressions
    1015             :      * to the new relation.  These are passed to us in the form of raw
    1016             :      * parsetrees; we need to transform them to executable expression trees
    1017             :      * before they can be added. The most convenient way to do that is to
    1018             :      * apply the parser's transformExpr routine, but transformExpr doesn't
    1019             :      * work unless we have a pre-existing relation. So, the transformation has
    1020             :      * to be postponed to this final step of CREATE TABLE.
    1021             :      *
    1022             :      * This needs to be before processing the partitioning clauses because
    1023             :      * those could refer to generated columns.
    1024             :      */
    1025       52306 :     if (rawDefaults)
    1026        2024 :         AddRelationNewConstraints(rel, rawDefaults, NIL,
    1027             :                                   true, true, false, queryString);
    1028             : 
    1029             :     /*
    1030             :      * Make column generation expressions visible for use by partitioning.
    1031             :      */
    1032       52204 :     CommandCounterIncrement();
    1033             : 
    1034             :     /* Process and store partition bound, if any. */
    1035       52204 :     if (stmt->partbound)
    1036             :     {
    1037             :         PartitionBoundSpec *bound;
    1038             :         ParseState *pstate;
    1039        8302 :         Oid         parentId = linitial_oid(inheritOids),
    1040             :                     defaultPartOid;
    1041             :         Relation    parent,
    1042        8302 :                     defaultRel = NULL;
    1043             :         ParseNamespaceItem *nsitem;
    1044             : 
    1045             :         /* Already have strong enough lock on the parent */
    1046        8302 :         parent = table_open(parentId, NoLock);
    1047             : 
    1048             :         /*
    1049             :          * We are going to try to validate the partition bound specification
    1050             :          * against the partition key of parentRel, so it better have one.
    1051             :          */
    1052        8302 :         if (parent->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
    1053          18 :             ereport(ERROR,
    1054             :                     (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
    1055             :                      errmsg("\"%s\" is not partitioned",
    1056             :                             RelationGetRelationName(parent))));
    1057             : 
    1058             :         /*
    1059             :          * The partition constraint of the default partition depends on the
    1060             :          * partition bounds of every other partition. It is possible that
    1061             :          * another backend might be about to execute a query on the default
    1062             :          * partition table, and that the query relies on previously cached
    1063             :          * default partition constraints. We must therefore take a table lock
    1064             :          * strong enough to prevent all queries on the default partition from
    1065             :          * proceeding until we commit and send out a shared-cache-inval notice
    1066             :          * that will make them update their index lists.
    1067             :          *
    1068             :          * Order of locking: The relation being added won't be visible to
    1069             :          * other backends until it is committed, hence here in
    1070             :          * DefineRelation() the order of locking the default partition and the
    1071             :          * relation being added does not matter. But at all other places we
    1072             :          * need to lock the default relation before we lock the relation being
    1073             :          * added or removed i.e. we should take the lock in same order at all
    1074             :          * the places such that lock parent, lock default partition and then
    1075             :          * lock the partition so as to avoid a deadlock.
    1076             :          */
    1077             :         defaultPartOid =
    1078        8284 :             get_default_oid_from_partdesc(RelationGetPartitionDesc(parent,
    1079             :                                                                    true));
    1080        8284 :         if (OidIsValid(defaultPartOid))
    1081         378 :             defaultRel = table_open(defaultPartOid, AccessExclusiveLock);
    1082             : 
    1083             :         /* Transform the bound values */
    1084        8284 :         pstate = make_parsestate(NULL);
    1085        8284 :         pstate->p_sourcetext = queryString;
    1086             : 
    1087             :         /*
    1088             :          * Add an nsitem containing this relation, so that transformExpr
    1089             :          * called on partition bound expressions is able to report errors
    1090             :          * using a proper context.
    1091             :          */
    1092        8284 :         nsitem = addRangeTableEntryForRelation(pstate, rel, AccessShareLock,
    1093             :                                                NULL, false, false);
    1094        8284 :         addNSItemToQuery(pstate, nsitem, false, true, true);
    1095             : 
    1096        8284 :         bound = transformPartitionBound(pstate, parent, stmt->partbound);
    1097             : 
    1098             :         /*
    1099             :          * Check first that the new partition's bound is valid and does not
    1100             :          * overlap with any of existing partitions of the parent.
    1101             :          */
    1102        8080 :         check_new_partition_bound(relname, parent, bound, pstate);
    1103             : 
    1104             :         /*
    1105             :          * If the default partition exists, its partition constraints will
    1106             :          * change after the addition of this new partition such that it won't
    1107             :          * allow any row that qualifies for this new partition. So, check that
    1108             :          * the existing data in the default partition satisfies the constraint
    1109             :          * as it will exist after adding this partition.
    1110             :          */
    1111        7966 :         if (OidIsValid(defaultPartOid))
    1112             :         {
    1113         348 :             check_default_partition_contents(parent, defaultRel, bound);
    1114             :             /* Keep the lock until commit. */
    1115         330 :             table_close(defaultRel, NoLock);
    1116             :         }
    1117             : 
    1118             :         /* Update the pg_class entry. */
    1119        7948 :         StorePartitionBound(rel, parent, bound);
    1120             : 
    1121        7948 :         table_close(parent, NoLock);
    1122             :     }
    1123             : 
    1124             :     /* Store inheritance information for new rel. */
    1125       51850 :     StoreCatalogInheritance(relationId, inheritOids, stmt->partbound != NULL);
    1126             : 
    1127             :     /*
    1128             :      * Process the partitioning specification (if any) and store the partition
    1129             :      * key information into the catalog.
    1130             :      */
    1131       51850 :     if (partitioned)
    1132             :     {
    1133             :         ParseState *pstate;
    1134             :         int         partnatts;
    1135             :         AttrNumber  partattrs[PARTITION_MAX_KEYS];
    1136             :         Oid         partopclass[PARTITION_MAX_KEYS];
    1137             :         Oid         partcollation[PARTITION_MAX_KEYS];
    1138        4924 :         List       *partexprs = NIL;
    1139             : 
    1140        4924 :         pstate = make_parsestate(NULL);
    1141        4924 :         pstate->p_sourcetext = queryString;
    1142             : 
    1143        4924 :         partnatts = list_length(stmt->partspec->partParams);
    1144             : 
    1145             :         /* Protect fixed-size arrays here and in executor */
    1146        4924 :         if (partnatts > PARTITION_MAX_KEYS)
    1147           0 :             ereport(ERROR,
    1148             :                     (errcode(ERRCODE_TOO_MANY_COLUMNS),
    1149             :                      errmsg("cannot partition using more than %d columns",
    1150             :                             PARTITION_MAX_KEYS)));
    1151             : 
    1152             :         /*
    1153             :          * We need to transform the raw parsetrees corresponding to partition
    1154             :          * expressions into executable expression trees.  Like column defaults
    1155             :          * and CHECK constraints, we could not have done the transformation
    1156             :          * earlier.
    1157             :          */
    1158        4924 :         stmt->partspec = transformPartitionSpec(rel, stmt->partspec);
    1159             : 
    1160        4894 :         ComputePartitionAttrs(pstate, rel, stmt->partspec->partParams,
    1161             :                               partattrs, &partexprs, partopclass,
    1162        4894 :                               partcollation, stmt->partspec->strategy);
    1163             : 
    1164        4810 :         StorePartitionKey(rel, stmt->partspec->strategy, partnatts, partattrs,
    1165             :                           partexprs,
    1166             :                           partopclass, partcollation);
    1167             : 
    1168             :         /* make it all visible */
    1169        4810 :         CommandCounterIncrement();
    1170             :     }
    1171             : 
    1172             :     /*
    1173             :      * If we're creating a partition, create now all the indexes, triggers,
    1174             :      * FKs defined in the parent.
    1175             :      *
    1176             :      * We can't do it earlier, because DefineIndex wants to know the partition
    1177             :      * key which we just stored.
    1178             :      */
    1179       51736 :     if (stmt->partbound)
    1180             :     {
    1181        7942 :         Oid         parentId = linitial_oid(inheritOids);
    1182             :         Relation    parent;
    1183             :         List       *idxlist;
    1184             :         ListCell   *cell;
    1185             : 
    1186             :         /* Already have strong enough lock on the parent */
    1187        7942 :         parent = table_open(parentId, NoLock);
    1188        7942 :         idxlist = RelationGetIndexList(parent);
    1189             : 
    1190             :         /*
    1191             :          * For each index in the parent table, create one in the partition
    1192             :          */
    1193        9346 :         foreach(cell, idxlist)
    1194             :         {
    1195        1422 :             Relation    idxRel = index_open(lfirst_oid(cell), AccessShareLock);
    1196             :             AttrMap    *attmap;
    1197             :             IndexStmt  *idxstmt;
    1198             :             Oid         constraintOid;
    1199             : 
    1200        1422 :             if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
    1201             :             {
    1202          36 :                 if (idxRel->rd_index->indisunique)
    1203          12 :                     ereport(ERROR,
    1204             :                             (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    1205             :                              errmsg("cannot create foreign partition of partitioned table \"%s\"",
    1206             :                                     RelationGetRelationName(parent)),
    1207             :                              errdetail("Table \"%s\" contains indexes that are unique.",
    1208             :                                        RelationGetRelationName(parent))));
    1209             :                 else
    1210             :                 {
    1211          24 :                     index_close(idxRel, AccessShareLock);
    1212          24 :                     continue;
    1213             :                 }
    1214             :             }
    1215             : 
    1216        1386 :             attmap = build_attrmap_by_name(RelationGetDescr(rel),
    1217             :                                            RelationGetDescr(parent),
    1218             :                                            false);
    1219             :             idxstmt =
    1220        1386 :                 generateClonedIndexStmt(NULL, idxRel,
    1221             :                                         attmap, &constraintOid);
    1222        1386 :             DefineIndex(RelationGetRelid(rel),
    1223             :                         idxstmt,
    1224             :                         InvalidOid,
    1225             :                         RelationGetRelid(idxRel),
    1226             :                         constraintOid,
    1227             :                         -1,
    1228             :                         false, false, false, false, false);
    1229             : 
    1230        1380 :             index_close(idxRel, AccessShareLock);
    1231             :         }
    1232             : 
    1233        7924 :         list_free(idxlist);
    1234             : 
    1235             :         /*
    1236             :          * If there are any row-level triggers, clone them to the new
    1237             :          * partition.
    1238             :          */
    1239        7924 :         if (parent->trigdesc != NULL)
    1240         414 :             CloneRowTriggersToPartition(parent, rel);
    1241             : 
    1242             :         /*
    1243             :          * And foreign keys too.  Note that because we're freshly creating the
    1244             :          * table, there is no need to verify these new constraints.
    1245             :          */
    1246        7924 :         CloneForeignKeyConstraints(NULL, parent, rel);
    1247             : 
    1248        7924 :         table_close(parent, NoLock);
    1249             :     }
    1250             : 
    1251             :     /*
    1252             :      * Now add any newly specified CHECK constraints to the new relation. Same
    1253             :      * as for defaults above, but these need to come after partitioning is set
    1254             :      * up.
    1255             :      */
    1256       51718 :     if (stmt->constraints)
    1257         634 :         AddRelationNewConstraints(rel, NIL, stmt->constraints,
    1258             :                                   true, true, false, queryString);
    1259             : 
    1260       51700 :     ObjectAddressSet(address, RelationRelationId, relationId);
    1261             : 
    1262             :     /*
    1263             :      * Clean up.  We keep lock on new relation (although it shouldn't be
    1264             :      * visible to anyone else anyway, until commit).
    1265             :      */
    1266       51700 :     relation_close(rel, NoLock);
    1267             : 
    1268       51700 :     return address;
    1269             : }
    1270             : 
    1271             : /*
    1272             :  * BuildDescForRelation
    1273             :  *
    1274             :  * Given a list of ColumnDef nodes, build a TupleDesc.
    1275             :  *
    1276             :  * Note: This is only for the limited purpose of table and view creation.  Not
    1277             :  * everything is filled in.  A real tuple descriptor should be obtained from
    1278             :  * the relcache.
    1279             :  */
    1280             : TupleDesc
    1281       55038 : BuildDescForRelation(const List *columns)
    1282             : {
    1283             :     int         natts;
    1284             :     AttrNumber  attnum;
    1285             :     ListCell   *l;
    1286             :     TupleDesc   desc;
    1287             :     char       *attname;
    1288             :     Oid         atttypid;
    1289             :     int32       atttypmod;
    1290             :     Oid         attcollation;
    1291             :     int         attdim;
    1292             : 
    1293             :     /*
    1294             :      * allocate a new tuple descriptor
    1295             :      */
    1296       55038 :     natts = list_length(columns);
    1297       55038 :     desc = CreateTemplateTupleDesc(natts);
    1298             : 
    1299       55038 :     attnum = 0;
    1300             : 
    1301      271606 :     foreach(l, columns)
    1302             :     {
    1303      216628 :         ColumnDef  *entry = lfirst(l);
    1304             :         AclResult   aclresult;
    1305             :         Form_pg_attribute att;
    1306             : 
    1307             :         /*
    1308             :          * for each entry in the list, get the name and type information from
    1309             :          * the list and have TupleDescInitEntry fill in the attribute
    1310             :          * information we need.
    1311             :          */
    1312      216628 :         attnum++;
    1313             : 
    1314      216628 :         attname = entry->colname;
    1315      216628 :         typenameTypeIdAndMod(NULL, entry->typeName, &atttypid, &atttypmod);
    1316             : 
    1317      216628 :         aclresult = object_aclcheck(TypeRelationId, atttypid, GetUserId(), ACL_USAGE);
    1318      216628 :         if (aclresult != ACLCHECK_OK)
    1319          42 :             aclcheck_error_type(aclresult, atttypid);
    1320             : 
    1321      216586 :         attcollation = GetColumnDefCollation(NULL, entry, atttypid);
    1322      216586 :         attdim = list_length(entry->typeName->arrayBounds);
    1323      216586 :         if (attdim > PG_INT16_MAX)
    1324           0 :             ereport(ERROR,
    1325             :                     errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
    1326             :                     errmsg("too many array dimensions"));
    1327             : 
    1328      216586 :         if (entry->typeName->setof)
    1329           0 :             ereport(ERROR,
    1330             :                     (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    1331             :                      errmsg("column \"%s\" cannot be declared SETOF",
    1332             :                             attname)));
    1333             : 
    1334      216586 :         TupleDescInitEntry(desc, attnum, attname,
    1335             :                            atttypid, atttypmod, attdim);
    1336      216586 :         att = TupleDescAttr(desc, attnum - 1);
    1337             : 
    1338             :         /* Override TupleDescInitEntry's settings as requested */
    1339      216586 :         TupleDescInitEntryCollation(desc, attnum, attcollation);
    1340             : 
    1341             :         /* Fill in additional stuff not handled by TupleDescInitEntry */
    1342      216586 :         att->attnotnull = entry->is_not_null;
    1343      216586 :         att->attislocal = entry->is_local;
    1344      216586 :         att->attinhcount = entry->inhcount;
    1345      216586 :         att->attidentity = entry->identity;
    1346      216586 :         att->attgenerated = entry->generated;
    1347      216586 :         att->attcompression = GetAttributeCompression(att->atttypid, entry->compression);
    1348      216574 :         if (entry->storage)
    1349       22588 :             att->attstorage = entry->storage;
    1350      193986 :         else if (entry->storage_name)
    1351          20 :             att->attstorage = GetAttributeStorage(att->atttypid, entry->storage_name);
    1352             :     }
    1353             : 
    1354       54978 :     return desc;
    1355             : }
    1356             : 
    1357             : /*
    1358             :  * Emit the right error or warning message for a "DROP" command issued on a
    1359             :  * non-existent relation
    1360             :  */
    1361             : static void
    1362        1084 : DropErrorMsgNonExistent(RangeVar *rel, char rightkind, bool missing_ok)
    1363             : {
    1364             :     const struct dropmsgstrings *rentry;
    1365             : 
    1366        1204 :     if (rel->schemaname != NULL &&
    1367         120 :         !OidIsValid(LookupNamespaceNoError(rel->schemaname)))
    1368             :     {
    1369          42 :         if (!missing_ok)
    1370             :         {
    1371           0 :             ereport(ERROR,
    1372             :                     (errcode(ERRCODE_UNDEFINED_SCHEMA),
    1373             :                      errmsg("schema \"%s\" does not exist", rel->schemaname)));
    1374             :         }
    1375             :         else
    1376             :         {
    1377          42 :             ereport(NOTICE,
    1378             :                     (errmsg("schema \"%s\" does not exist, skipping",
    1379             :                             rel->schemaname)));
    1380             :         }
    1381          42 :         return;
    1382             :     }
    1383             : 
    1384        1362 :     for (rentry = dropmsgstringarray; rentry->kind != '\0'; rentry++)
    1385             :     {
    1386        1362 :         if (rentry->kind == rightkind)
    1387             :         {
    1388        1042 :             if (!missing_ok)
    1389             :             {
    1390         132 :                 ereport(ERROR,
    1391             :                         (errcode(rentry->nonexistent_code),
    1392             :                          errmsg(rentry->nonexistent_msg, rel->relname)));
    1393             :             }
    1394             :             else
    1395             :             {
    1396         910 :                 ereport(NOTICE, (errmsg(rentry->skipping_msg, rel->relname)));
    1397         910 :                 break;
    1398             :             }
    1399             :         }
    1400             :     }
    1401             : 
    1402             :     Assert(rentry->kind != '\0');    /* Should be impossible */
    1403             : }
    1404             : 
    1405             : /*
    1406             :  * Emit the right error message for a "DROP" command issued on a
    1407             :  * relation of the wrong type
    1408             :  */
    1409             : static void
    1410           0 : DropErrorMsgWrongType(const char *relname, char wrongkind, char rightkind)
    1411             : {
    1412             :     const struct dropmsgstrings *rentry;
    1413             :     const struct dropmsgstrings *wentry;
    1414             : 
    1415           0 :     for (rentry = dropmsgstringarray; rentry->kind != '\0'; rentry++)
    1416           0 :         if (rentry->kind == rightkind)
    1417           0 :             break;
    1418             :     Assert(rentry->kind != '\0');
    1419             : 
    1420           0 :     for (wentry = dropmsgstringarray; wentry->kind != '\0'; wentry++)
    1421           0 :         if (wentry->kind == wrongkind)
    1422           0 :             break;
    1423             :     /* wrongkind could be something we don't have in our table... */
    1424             : 
    1425           0 :     ereport(ERROR,
    1426             :             (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    1427             :              errmsg(rentry->nota_msg, relname),
    1428             :              (wentry->kind != '\0') ? errhint("%s", _(wentry->drophint_msg)) : 0));
    1429             : }
    1430             : 
    1431             : /*
    1432             :  * RemoveRelations
    1433             :  *      Implements DROP TABLE, DROP INDEX, DROP SEQUENCE, DROP VIEW,
    1434             :  *      DROP MATERIALIZED VIEW, DROP FOREIGN TABLE
    1435             :  */
    1436             : void
    1437       16074 : RemoveRelations(DropStmt *drop)
    1438             : {
    1439             :     ObjectAddresses *objects;
    1440             :     char        relkind;
    1441             :     ListCell   *cell;
    1442       16074 :     int         flags = 0;
    1443       16074 :     LOCKMODE    lockmode = AccessExclusiveLock;
    1444             : 
    1445             :     /* DROP CONCURRENTLY uses a weaker lock, and has some restrictions */
    1446       16074 :     if (drop->concurrent)
    1447             :     {
    1448             :         /*
    1449             :          * Note that for temporary relations this lock may get upgraded later
    1450             :          * on, but as no other session can access a temporary relation, this
    1451             :          * is actually fine.
    1452             :          */
    1453         130 :         lockmode = ShareUpdateExclusiveLock;
    1454             :         Assert(drop->removeType == OBJECT_INDEX);
    1455         130 :         if (list_length(drop->objects) != 1)
    1456           6 :             ereport(ERROR,
    1457             :                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    1458             :                      errmsg("DROP INDEX CONCURRENTLY does not support dropping multiple objects")));
    1459         124 :         if (drop->behavior == DROP_CASCADE)
    1460           0 :             ereport(ERROR,
    1461             :                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    1462             :                      errmsg("DROP INDEX CONCURRENTLY does not support CASCADE")));
    1463             :     }
    1464             : 
    1465             :     /*
    1466             :      * First we identify all the relations, then we delete them in a single
    1467             :      * performMultipleDeletions() call.  This is to avoid unwanted DROP
    1468             :      * RESTRICT errors if one of the relations depends on another.
    1469             :      */
    1470             : 
    1471             :     /* Determine required relkind */
    1472       16068 :     switch (drop->removeType)
    1473             :     {
    1474       13970 :         case OBJECT_TABLE:
    1475       13970 :             relkind = RELKIND_RELATION;
    1476       13970 :             break;
    1477             : 
    1478         770 :         case OBJECT_INDEX:
    1479         770 :             relkind = RELKIND_INDEX;
    1480         770 :             break;
    1481             : 
    1482         172 :         case OBJECT_SEQUENCE:
    1483         172 :             relkind = RELKIND_SEQUENCE;
    1484         172 :             break;
    1485             : 
    1486         886 :         case OBJECT_VIEW:
    1487         886 :             relkind = RELKIND_VIEW;
    1488         886 :             break;
    1489             : 
    1490         120 :         case OBJECT_MATVIEW:
    1491         120 :             relkind = RELKIND_MATVIEW;
    1492         120 :             break;
    1493             : 
    1494         150 :         case OBJECT_FOREIGN_TABLE:
    1495         150 :             relkind = RELKIND_FOREIGN_TABLE;
    1496         150 :             break;
    1497             : 
    1498           0 :         default:
    1499           0 :             elog(ERROR, "unrecognized drop object type: %d",
    1500             :                  (int) drop->removeType);
    1501             :             relkind = 0;        /* keep compiler quiet */
    1502             :             break;
    1503             :     }
    1504             : 
    1505             :     /* Lock and validate each relation; build a list of object addresses */
    1506       16068 :     objects = new_object_addresses();
    1507             : 
    1508       35524 :     foreach(cell, drop->objects)
    1509             :     {
    1510       19614 :         RangeVar   *rel = makeRangeVarFromNameList((List *) lfirst(cell));
    1511             :         Oid         relOid;
    1512             :         ObjectAddress obj;
    1513             :         struct DropRelationCallbackState state;
    1514             : 
    1515             :         /*
    1516             :          * These next few steps are a great deal like relation_openrv, but we
    1517             :          * don't bother building a relcache entry since we don't need it.
    1518             :          *
    1519             :          * Check for shared-cache-inval messages before trying to access the
    1520             :          * relation.  This is needed to cover the case where the name
    1521             :          * identifies a rel that has been dropped and recreated since the
    1522             :          * start of our transaction: if we don't flush the old syscache entry,
    1523             :          * then we'll latch onto that entry and suffer an error later.
    1524             :          */
    1525       19614 :         AcceptInvalidationMessages();
    1526             : 
    1527             :         /* Look up the appropriate relation using namespace search. */
    1528       19614 :         state.expected_relkind = relkind;
    1529       39228 :         state.heap_lockmode = drop->concurrent ?
    1530       19614 :             ShareUpdateExclusiveLock : AccessExclusiveLock;
    1531             :         /* We must initialize these fields to show that no locks are held: */
    1532       19614 :         state.heapOid = InvalidOid;
    1533       19614 :         state.partParentOid = InvalidOid;
    1534             : 
    1535       19614 :         relOid = RangeVarGetRelidExtended(rel, lockmode, RVR_MISSING_OK,
    1536             :                                           RangeVarCallbackForDropRelation,
    1537             :                                           (void *) &state);
    1538             : 
    1539             :         /* Not there? */
    1540       19594 :         if (!OidIsValid(relOid))
    1541             :         {
    1542        1084 :             DropErrorMsgNonExistent(rel, relkind, drop->missing_ok);
    1543         952 :             continue;
    1544             :         }
    1545             : 
    1546             :         /*
    1547             :          * Decide if concurrent mode needs to be used here or not.  The
    1548             :          * callback retrieved the rel's persistence for us.
    1549             :          */
    1550       18510 :         if (drop->concurrent &&
    1551         118 :             state.actual_relpersistence != RELPERSISTENCE_TEMP)
    1552             :         {
    1553             :             Assert(list_length(drop->objects) == 1 &&
    1554             :                    drop->removeType == OBJECT_INDEX);
    1555         100 :             flags |= PERFORM_DELETION_CONCURRENTLY;
    1556             :         }
    1557             : 
    1558             :         /*
    1559             :          * Concurrent index drop cannot be used with partitioned indexes,
    1560             :          * either.
    1561             :          */
    1562       18510 :         if ((flags & PERFORM_DELETION_CONCURRENTLY) != 0 &&
    1563         100 :             state.actual_relkind == RELKIND_PARTITIONED_INDEX)
    1564           6 :             ereport(ERROR,
    1565             :                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    1566             :                      errmsg("cannot drop partitioned index \"%s\" concurrently",
    1567             :                             rel->relname)));
    1568             : 
    1569             :         /*
    1570             :          * If we're told to drop a partitioned index, we must acquire lock on
    1571             :          * all the children of its parent partitioned table before proceeding.
    1572             :          * Otherwise we'd try to lock the child index partitions before their
    1573             :          * tables, leading to potential deadlock against other sessions that
    1574             :          * will lock those objects in the other order.
    1575             :          */
    1576       18504 :         if (state.actual_relkind == RELKIND_PARTITIONED_INDEX)
    1577          70 :             (void) find_all_inheritors(state.heapOid,
    1578             :                                        state.heap_lockmode,
    1579             :                                        NULL);
    1580             : 
    1581             :         /* OK, we're ready to delete this one */
    1582       18504 :         obj.classId = RelationRelationId;
    1583       18504 :         obj.objectId = relOid;
    1584       18504 :         obj.objectSubId = 0;
    1585             : 
    1586       18504 :         add_exact_object_address(&obj, objects);
    1587             :     }
    1588             : 
    1589       15910 :     performMultipleDeletions(objects, drop->behavior, flags);
    1590             : 
    1591       15774 :     free_object_addresses(objects);
    1592       15774 : }
    1593             : 
    1594             : /*
    1595             :  * Before acquiring a table lock, check whether we have sufficient rights.
    1596             :  * In the case of DROP INDEX, also try to lock the table before the index.
    1597             :  * Also, if the table to be dropped is a partition, we try to lock the parent
    1598             :  * first.
    1599             :  */
    1600             : static void
    1601       19774 : RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid, Oid oldRelOid,
    1602             :                                 void *arg)
    1603             : {
    1604             :     HeapTuple   tuple;
    1605             :     struct DropRelationCallbackState *state;
    1606             :     char        expected_relkind;
    1607             :     bool        is_partition;
    1608             :     Form_pg_class classform;
    1609             :     LOCKMODE    heap_lockmode;
    1610       19774 :     bool        invalid_system_index = false;
    1611             : 
    1612       19774 :     state = (struct DropRelationCallbackState *) arg;
    1613       19774 :     heap_lockmode = state->heap_lockmode;
    1614             : 
    1615             :     /*
    1616             :      * If we previously locked some other index's heap, and the name we're
    1617             :      * looking up no longer refers to that relation, release the now-useless
    1618             :      * lock.
    1619             :      */
    1620       19774 :     if (relOid != oldRelOid && OidIsValid(state->heapOid))
    1621             :     {
    1622           0 :         UnlockRelationOid(state->heapOid, heap_lockmode);
    1623           0 :         state->heapOid = InvalidOid;
    1624             :     }
    1625             : 
    1626             :     /*
    1627             :      * Similarly, if we previously locked some other partition's heap, and the
    1628             :      * name we're looking up no longer refers to that relation, release the
    1629             :      * now-useless lock.
    1630             :      */
    1631       19774 :     if (relOid != oldRelOid && OidIsValid(state->partParentOid))
    1632             :     {
    1633           0 :         UnlockRelationOid(state->partParentOid, AccessExclusiveLock);
    1634           0 :         state->partParentOid = InvalidOid;
    1635             :     }
    1636             : 
    1637             :     /* Didn't find a relation, so no need for locking or permission checks. */
    1638       19774 :     if (!OidIsValid(relOid))
    1639        1088 :         return;
    1640             : 
    1641       18686 :     tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid));
    1642       18686 :     if (!HeapTupleIsValid(tuple))
    1643           0 :         return;                 /* concurrently dropped, so nothing to do */
    1644       18686 :     classform = (Form_pg_class) GETSTRUCT(tuple);
    1645       18686 :     is_partition = classform->relispartition;
    1646             : 
    1647             :     /* Pass back some data to save lookups in RemoveRelations */
    1648       18686 :     state->actual_relkind = classform->relkind;
    1649       18686 :     state->actual_relpersistence = classform->relpersistence;
    1650             : 
    1651             :     /*
    1652             :      * Both RELKIND_RELATION and RELKIND_PARTITIONED_TABLE are OBJECT_TABLE,
    1653             :      * but RemoveRelations() can only pass one relkind for a given relation.
    1654             :      * It chooses RELKIND_RELATION for both regular and partitioned tables.
    1655             :      * That means we must be careful before giving the wrong type error when
    1656             :      * the relation is RELKIND_PARTITIONED_TABLE.  An equivalent problem
    1657             :      * exists with indexes.
    1658             :      */
    1659       18686 :     if (classform->relkind == RELKIND_PARTITIONED_TABLE)
    1660        3006 :         expected_relkind = RELKIND_RELATION;
    1661       15680 :     else if (classform->relkind == RELKIND_PARTITIONED_INDEX)
    1662          76 :         expected_relkind = RELKIND_INDEX;
    1663             :     else
    1664       15604 :         expected_relkind = classform->relkind;
    1665             : 
    1666       18686 :     if (state->expected_relkind != expected_relkind)
    1667           0 :         DropErrorMsgWrongType(rel->relname, classform->relkind,
    1668           0 :                               state->expected_relkind);
    1669             : 
    1670             :     /* Allow DROP to either table owner or schema owner */
    1671       18686 :     if (!object_ownercheck(RelationRelationId, relOid, GetUserId()) &&
    1672          18 :         !object_ownercheck(NamespaceRelationId, classform->relnamespace, GetUserId()))
    1673          18 :         aclcheck_error(ACLCHECK_NOT_OWNER,
    1674          18 :                        get_relkind_objtype(classform->relkind),
    1675          18 :                        rel->relname);
    1676             : 
    1677             :     /*
    1678             :      * Check the case of a system index that might have been invalidated by a
    1679             :      * failed concurrent process and allow its drop. For the time being, this
    1680             :      * only concerns indexes of toast relations that became invalid during a
    1681             :      * REINDEX CONCURRENTLY process.
    1682             :      */
    1683       18668 :     if (IsSystemClass(relOid, classform) && classform->relkind == RELKIND_INDEX)
    1684             :     {
    1685             :         HeapTuple   locTuple;
    1686             :         Form_pg_index indexform;
    1687             :         bool        indisvalid;
    1688             : 
    1689           0 :         locTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(relOid));
    1690           0 :         if (!HeapTupleIsValid(locTuple))
    1691             :         {
    1692           0 :             ReleaseSysCache(tuple);
    1693           0 :             return;
    1694             :         }
    1695             : 
    1696           0 :         indexform = (Form_pg_index) GETSTRUCT(locTuple);
    1697           0 :         indisvalid = indexform->indisvalid;
    1698           0 :         ReleaseSysCache(locTuple);
    1699             : 
    1700             :         /* Mark object as being an invalid index of system catalogs */
    1701           0 :         if (!indisvalid)
    1702           0 :             invalid_system_index = true;
    1703             :     }
    1704             : 
    1705             :     /* In the case of an invalid index, it is fine to bypass this check */
    1706       18668 :     if (!invalid_system_index && !allowSystemTableMods && IsSystemClass(relOid, classform))
    1707           2 :         ereport(ERROR,
    1708             :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
    1709             :                  errmsg("permission denied: \"%s\" is a system catalog",
    1710             :                         rel->relname)));
    1711             : 
    1712       18666 :     ReleaseSysCache(tuple);
    1713             : 
    1714             :     /*
    1715             :      * In DROP INDEX, attempt to acquire lock on the parent table before
    1716             :      * locking the index.  index_drop() will need this anyway, and since
    1717             :      * regular queries lock tables before their indexes, we risk deadlock if
    1718             :      * we do it the other way around.  No error if we don't find a pg_index
    1719             :      * entry, though --- the relation may have been dropped.  Note that this
    1720             :      * code will execute for either plain or partitioned indexes.
    1721             :      */
    1722       18666 :     if (expected_relkind == RELKIND_INDEX &&
    1723             :         relOid != oldRelOid)
    1724             :     {
    1725         746 :         state->heapOid = IndexGetRelation(relOid, true);
    1726         746 :         if (OidIsValid(state->heapOid))
    1727         746 :             LockRelationOid(state->heapOid, heap_lockmode);
    1728             :     }
    1729             : 
    1730             :     /*
    1731             :      * Similarly, if the relation is a partition, we must acquire lock on its
    1732             :      * parent before locking the partition.  That's because queries lock the
    1733             :      * parent before its partitions, so we risk deadlock if we do it the other
    1734             :      * way around.
    1735             :      */
    1736       18666 :     if (is_partition && relOid != oldRelOid)
    1737             :     {
    1738         600 :         state->partParentOid = get_partition_parent(relOid, true);
    1739         600 :         if (OidIsValid(state->partParentOid))
    1740         600 :             LockRelationOid(state->partParentOid, AccessExclusiveLock);
    1741             :     }
    1742             : }
    1743             : 
    1744             : /*
    1745             :  * ExecuteTruncate
    1746             :  *      Executes a TRUNCATE command.
    1747             :  *
    1748             :  * This is a multi-relation truncate.  We first open and grab exclusive
    1749             :  * lock on all relations involved, checking permissions and otherwise
    1750             :  * verifying that the relation is OK for truncation.  Note that if relations
    1751             :  * are foreign tables, at this stage, we have not yet checked that their
    1752             :  * foreign data in external data sources are OK for truncation.  These are
    1753             :  * checked when foreign data are actually truncated later.  In CASCADE mode,
    1754             :  * relations having FK references to the targeted relations are automatically
    1755             :  * added to the group; in RESTRICT mode, we check that all FK references are
    1756             :  * internal to the group that's being truncated.  Finally all the relations
    1757             :  * are truncated and reindexed.
    1758             :  */
    1759             : void
    1760        1368 : ExecuteTruncate(TruncateStmt *stmt)
    1761             : {
    1762        1368 :     List       *rels = NIL;
    1763        1368 :     List       *relids = NIL;
    1764        1368 :     List       *relids_logged = NIL;
    1765             :     ListCell   *cell;
    1766             : 
    1767             :     /*
    1768             :      * Open, exclusive-lock, and check all the explicitly-specified relations
    1769             :      */
    1770        2890 :     foreach(cell, stmt->relations)
    1771             :     {
    1772        1570 :         RangeVar   *rv = lfirst(cell);
    1773             :         Relation    rel;
    1774        1570 :         bool        recurse = rv->inh;
    1775             :         Oid         myrelid;
    1776        1570 :         LOCKMODE    lockmode = AccessExclusiveLock;
    1777             : 
    1778        1570 :         myrelid = RangeVarGetRelidExtended(rv, lockmode,
    1779             :                                            0, RangeVarCallbackForTruncate,
    1780             :                                            NULL);
    1781             : 
    1782             :         /* don't throw error for "TRUNCATE foo, foo" */
    1783        1534 :         if (list_member_oid(relids, myrelid))
    1784           2 :             continue;
    1785             : 
    1786             :         /* open the relation, we already hold a lock on it */
    1787        1532 :         rel = table_open(myrelid, NoLock);
    1788             : 
    1789             :         /*
    1790             :          * RangeVarGetRelidExtended() has done most checks with its callback,
    1791             :          * but other checks with the now-opened Relation remain.
    1792             :          */
    1793        1532 :         truncate_check_activity(rel);
    1794             : 
    1795        1532 :         rels = lappend(rels, rel);
    1796        1532 :         relids = lappend_oid(relids, myrelid);
    1797             : 
    1798             :         /* Log this relation only if needed for logical decoding */
    1799        1532 :         if (RelationIsLogicallyLogged(rel))
    1800          66 :             relids_logged = lappend_oid(relids_logged, myrelid);
    1801             : 
    1802        1532 :         if (recurse)
    1803             :         {
    1804             :             ListCell   *child;
    1805             :             List       *children;
    1806             : 
    1807        1470 :             children = find_all_inheritors(myrelid, lockmode, NULL);
    1808             : 
    1809        4576 :             foreach(child, children)
    1810             :             {
    1811        3106 :                 Oid         childrelid = lfirst_oid(child);
    1812             : 
    1813        3106 :                 if (list_member_oid(relids, childrelid))
    1814        1470 :                     continue;
    1815             : 
    1816             :                 /* find_all_inheritors already got lock */
    1817        1636 :                 rel = table_open(childrelid, NoLock);
    1818             : 
    1819             :                 /*
    1820             :                  * It is possible that the parent table has children that are
    1821             :                  * temp tables of other backends.  We cannot safely access
    1822             :                  * such tables (because of buffering issues), and the best
    1823             :                  * thing to do is to silently ignore them.  Note that this
    1824             :                  * check is the same as one of the checks done in
    1825             :                  * truncate_check_activity() called below, still it is kept
    1826             :                  * here for simplicity.
    1827             :                  */
    1828        1636 :                 if (RELATION_IS_OTHER_TEMP(rel))
    1829             :                 {
    1830           8 :                     table_close(rel, lockmode);
    1831           8 :                     continue;
    1832             :                 }
    1833             : 
    1834             :                 /*
    1835             :                  * Inherited TRUNCATE commands perform access permission
    1836             :                  * checks on the parent table only. So we skip checking the
    1837             :                  * children's permissions and don't call
    1838             :                  * truncate_check_perms() here.
    1839             :                  */
    1840        1628 :                 truncate_check_rel(RelationGetRelid(rel), rel->rd_rel);
    1841        1628 :                 truncate_check_activity(rel);
    1842             : 
    1843        1628 :                 rels = lappend(rels, rel);
    1844        1628 :                 relids = lappend_oid(relids, childrelid);
    1845             : 
    1846             :                 /* Log this relation only if needed for logical decoding */
    1847        1628 :                 if (RelationIsLogicallyLogged(rel))
    1848          22 :                     relids_logged = lappend_oid(relids_logged, childrelid);
    1849             :             }
    1850             :         }
    1851          62 :         else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
    1852          12 :             ereport(ERROR,
    1853             :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    1854             :                      errmsg("cannot truncate only a partitioned table"),
    1855             :                      errhint("Do not specify the ONLY keyword, or use TRUNCATE ONLY on the partitions directly.")));
    1856             :     }
    1857             : 
    1858        1320 :     ExecuteTruncateGuts(rels, relids, relids_logged,
    1859        1320 :                         stmt->behavior, stmt->restart_seqs, false);
    1860             : 
    1861             :     /* And close the rels */
    1862        4232 :     foreach(cell, rels)
    1863             :     {
    1864        2994 :         Relation    rel = (Relation) lfirst(cell);
    1865             : 
    1866        2994 :         table_close(rel, NoLock);
    1867             :     }
    1868        1238 : }
    1869             : 
    1870             : /*
    1871             :  * ExecuteTruncateGuts
    1872             :  *
    1873             :  * Internal implementation of TRUNCATE.  This is called by the actual TRUNCATE
    1874             :  * command (see above) as well as replication subscribers that execute a
    1875             :  * replicated TRUNCATE action.
    1876             :  *
    1877             :  * explicit_rels is the list of Relations to truncate that the command
    1878             :  * specified.  relids is the list of Oids corresponding to explicit_rels.
    1879             :  * relids_logged is the list of Oids (a subset of relids) that require
    1880             :  * WAL-logging.  This is all a bit redundant, but the existing callers have
    1881             :  * this information handy in this form.
    1882             :  */
    1883             : void
    1884        1358 : ExecuteTruncateGuts(List *explicit_rels,
    1885             :                     List *relids,
    1886             :                     List *relids_logged,
    1887             :                     DropBehavior behavior, bool restart_seqs,
    1888             :                     bool run_as_table_owner)
    1889             : {
    1890             :     List       *rels;
    1891        1358 :     List       *seq_relids = NIL;
    1892        1358 :     HTAB       *ft_htab = NULL;
    1893             :     EState     *estate;
    1894             :     ResultRelInfo *resultRelInfos;
    1895             :     ResultRelInfo *resultRelInfo;
    1896             :     SubTransactionId mySubid;
    1897             :     ListCell   *cell;
    1898             :     Oid        *logrelids;
    1899             : 
    1900             :     /*
    1901             :      * Check the explicitly-specified relations.
    1902             :      *
    1903             :      * In CASCADE mode, suck in all referencing relations as well.  This
    1904             :      * requires multiple iterations to find indirectly-dependent relations. At
    1905             :      * each phase, we need to exclusive-lock new rels before looking for their
    1906             :      * dependencies, else we might miss something.  Also, we check each rel as
    1907             :      * soon as we open it, to avoid a faux pas such as holding lock for a long
    1908             :      * time on a rel we have no permissions for.
    1909             :      */
    1910        1358 :     rels = list_copy(explicit_rels);
    1911        1358 :     if (behavior == DROP_CASCADE)
    1912             :     {
    1913             :         for (;;)
    1914          40 :         {
    1915             :             List       *newrelids;
    1916             : 
    1917          80 :             newrelids = heap_truncate_find_FKs(relids);
    1918          80 :             if (newrelids == NIL)
    1919          40 :                 break;          /* nothing else to add */
    1920             : 
    1921         134 :             foreach(cell, newrelids)
    1922             :             {
    1923          94 :                 Oid         relid = lfirst_oid(cell);
    1924             :                 Relation    rel;
    1925             : 
    1926          94 :                 rel = table_open(relid, AccessExclusiveLock);
    1927          94 :                 ereport(NOTICE,
    1928             :                         (errmsg("truncate cascades to table \"%s\"",
    1929             :                                 RelationGetRelationName(rel))));
    1930          94 :                 truncate_check_rel(relid, rel->rd_rel);
    1931          94 :                 truncate_check_perms(relid, rel->rd_rel);
    1932          94 :                 truncate_check_activity(rel);
    1933          94 :                 rels = lappend(rels, rel);
    1934          94 :                 relids = lappend_oid(relids, relid);
    1935             : 
    1936             :                 /* Log this relation only if needed for logical decoding */
    1937          94 :                 if (RelationIsLogicallyLogged(rel))
    1938           0 :                     relids_logged = lappend_oid(relids_logged, relid);
    1939             :             }
    1940             :         }
    1941             :     }
    1942             : 
    1943             :     /*
    1944             :      * Check foreign key references.  In CASCADE mode, this should be
    1945             :      * unnecessary since we just pulled in all the references; but as a
    1946             :      * cross-check, do it anyway if in an Assert-enabled build.
    1947             :      */
    1948             : #ifdef USE_ASSERT_CHECKING
    1949             :     heap_truncate_check_FKs(rels, false);
    1950             : #else
    1951        1358 :     if (behavior == DROP_RESTRICT)
    1952        1318 :         heap_truncate_check_FKs(rels, false);
    1953             : #endif
    1954             : 
    1955             :     /*
    1956             :      * If we are asked to restart sequences, find all the sequences, lock them
    1957             :      * (we need AccessExclusiveLock for ResetSequence), and check permissions.
    1958             :      * We want to do this early since it's pointless to do all the truncation
    1959             :      * work only to fail on sequence permissions.
    1960             :      */
    1961        1284 :     if (restart_seqs)
    1962             :     {
    1963          52 :         foreach(cell, rels)
    1964             :         {
    1965          26 :             Relation    rel = (Relation) lfirst(cell);
    1966          26 :             List       *seqlist = getOwnedSequences(RelationGetRelid(rel));
    1967             :             ListCell   *seqcell;
    1968             : 
    1969          62 :             foreach(seqcell, seqlist)
    1970             :             {
    1971          36 :                 Oid         seq_relid = lfirst_oid(seqcell);
    1972             :                 Relation    seq_rel;
    1973             : 
    1974          36 :                 seq_rel = relation_open(seq_relid, AccessExclusiveLock);
    1975             : 
    1976             :                 /* This check must match AlterSequence! */
    1977          36 :                 if (!object_ownercheck(RelationRelationId, seq_relid, GetUserId()))
    1978           0 :                     aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_SEQUENCE,
    1979           0 :                                    RelationGetRelationName(seq_rel));
    1980             : 
    1981          36 :                 seq_relids = lappend_oid(seq_relids, seq_relid);
    1982             : 
    1983          36 :                 relation_close(seq_rel, NoLock);
    1984             :             }
    1985             :         }
    1986             :     }
    1987             : 
    1988             :     /* Prepare to catch AFTER triggers. */
    1989        1284 :     AfterTriggerBeginQuery();
    1990             : 
    1991             :     /*
    1992             :      * To fire triggers, we'll need an EState as well as a ResultRelInfo for
    1993             :      * each relation.  We don't need to call ExecOpenIndices, though.
    1994             :      *
    1995             :      * We put the ResultRelInfos in the es_opened_result_relations list, even
    1996             :      * though we don't have a range table and don't populate the
    1997             :      * es_result_relations array.  That's a bit bogus, but it's enough to make
    1998             :      * ExecGetTriggerResultRel() find them.
    1999             :      */
    2000        1284 :     estate = CreateExecutorState();
    2001             :     resultRelInfos = (ResultRelInfo *)
    2002        1284 :         palloc(list_length(rels) * sizeof(ResultRelInfo));
    2003        1284 :     resultRelInfo = resultRelInfos;
    2004        4450 :     foreach(cell, rels)
    2005             :     {
    2006        3166 :         Relation    rel = (Relation) lfirst(cell);
    2007             : 
    2008        3166 :         InitResultRelInfo(resultRelInfo,
    2009             :                           rel,
    2010             :                           0,    /* dummy rangetable index */
    2011             :                           NULL,
    2012             :                           0);
    2013        3166 :         estate->es_opened_result_relations =
    2014        3166 :             lappend(estate->es_opened_result_relations, resultRelInfo);
    2015        3166 :         resultRelInfo++;
    2016             :     }
    2017             : 
    2018             :     /*
    2019             :      * Process all BEFORE STATEMENT TRUNCATE triggers before we begin
    2020             :      * truncating (this is because one of them might throw an error). Also, if
    2021             :      * we were to allow them to prevent statement execution, that would need
    2022             :      * to be handled here.
    2023             :      */
    2024        1284 :     resultRelInfo = resultRelInfos;
    2025        4450 :     foreach(cell, rels)
    2026             :     {
    2027             :         UserContext ucxt;
    2028             : 
    2029        3166 :         if (run_as_table_owner)
    2030          70 :             SwitchToUntrustedUser(resultRelInfo->ri_RelationDesc->rd_rel->relowner,
    2031             :                                   &ucxt);
    2032        3166 :         ExecBSTruncateTriggers(estate, resultRelInfo);
    2033        3166 :         if (run_as_table_owner)
    2034          70 :             RestoreUserContext(&ucxt);
    2035        3166 :         resultRelInfo++;
    2036             :     }
    2037             : 
    2038             :     /*
    2039             :      * OK, truncate each table.
    2040             :      */
    2041        1284 :     mySubid = GetCurrentSubTransactionId();
    2042             : 
    2043        4450 :     foreach(cell, rels)
    2044             :     {
    2045        3166 :         Relation    rel = (Relation) lfirst(cell);
    2046             : 
    2047             :         /* Skip partitioned tables as there is nothing to do */
    2048        3166 :         if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
    2049         644 :             continue;
    2050             : 
    2051             :         /*
    2052             :          * Build the lists of foreign tables belonging to each foreign server
    2053             :          * and pass each list to the foreign data wrapper's callback function,
    2054             :          * so that each server can truncate its all foreign tables in bulk.
    2055             :          * Each list is saved as a single entry in a hash table that uses the
    2056             :          * server OID as lookup key.
    2057             :          */
    2058        2522 :         if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
    2059             :         {
    2060          34 :             Oid         serverid = GetForeignServerIdByRelId(RelationGetRelid(rel));
    2061             :             bool        found;
    2062             :             ForeignTruncateInfo *ft_info;
    2063             : 
    2064             :             /* First time through, initialize hashtable for foreign tables */
    2065          34 :             if (!ft_htab)
    2066             :             {
    2067             :                 HASHCTL     hctl;
    2068             : 
    2069          30 :                 memset(&hctl, 0, sizeof(HASHCTL));
    2070          30 :                 hctl.keysize = sizeof(Oid);
    2071          30 :                 hctl.entrysize = sizeof(ForeignTruncateInfo);
    2072          30 :                 hctl.hcxt = CurrentMemoryContext;
    2073             : 
    2074          30 :                 ft_htab = hash_create("TRUNCATE for Foreign Tables",
    2075             :                                       32,   /* start small and extend */
    2076             :                                       &hctl,
    2077             :                                       HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);
    2078             :             }
    2079             : 
    2080             :             /* Find or create cached entry for the foreign table */
    2081          34 :             ft_info = hash_search(ft_htab, &serverid, HASH_ENTER, &found);
    2082          34 :             if (!found)
    2083          30 :                 ft_info->rels = NIL;
    2084             : 
    2085             :             /*
    2086             :              * Save the foreign table in the entry of the server that the
    2087             :              * foreign table belongs to.
    2088             :              */
    2089          34 :             ft_info->rels = lappend(ft_info->rels, rel);
    2090          34 :             continue;
    2091             :         }
    2092             : 
    2093             :         /*
    2094             :          * Normally, we need a transaction-safe truncation here.  However, if
    2095             :          * the table was either created in the current (sub)transaction or has
    2096             :          * a new relfilenumber in the current (sub)transaction, then we can
    2097             :          * just truncate it in-place, because a rollback would cause the whole
    2098             :          * table or the current physical file to be thrown away anyway.
    2099             :          */
    2100        2488 :         if (rel->rd_createSubid == mySubid ||
    2101        2462 :             rel->rd_newRelfilelocatorSubid == mySubid)
    2102             :         {
    2103             :             /* Immediate, non-rollbackable truncation is OK */
    2104          90 :             heap_truncate_one_rel(rel);
    2105             :         }
    2106             :         else
    2107             :         {
    2108             :             Oid         heap_relid;
    2109             :             Oid         toast_relid;
    2110        2398 :             ReindexParams reindex_params = {0};
    2111             : 
    2112             :             /*
    2113             :              * This effectively deletes all rows in the table, and may be done
    2114             :              * in a serializable transaction.  In that case we must record a
    2115             :              * rw-conflict in to this transaction from each transaction
    2116             :              * holding a predicate lock on the table.
    2117             :              */
    2118        2398 :             CheckTableForSerializableConflictIn(rel);
    2119             : 
    2120             :             /*
    2121             :              * Need the full transaction-safe pushups.
    2122             :              *
    2123             :              * Create a new empty storage file for the relation, and assign it
    2124             :              * as the relfilenumber value. The old storage file is scheduled
    2125             :              * for deletion at commit.
    2126             :              */
    2127        2398 :             RelationSetNewRelfilenumber(rel, rel->rd_rel->relpersistence);
    2128             : 
    2129        2398 :             heap_relid = RelationGetRelid(rel);
    2130             : 
    2131             :             /*
    2132             :              * The same for the toast table, if any.
    2133             :              */
    2134        2398 :             toast_relid = rel->rd_rel->reltoastrelid;
    2135        2398 :             if (OidIsValid(toast_relid))
    2136             :             {
    2137        1404 :                 Relation    toastrel = relation_open(toast_relid,
    2138             :                                                      AccessExclusiveLock);
    2139             : 
    2140        1404 :                 RelationSetNewRelfilenumber(toastrel,
    2141        1404 :                                             toastrel->rd_rel->relpersistence);
    2142        1404 :                 table_close(toastrel, NoLock);
    2143             :             }
    2144             : 
    2145             :             /*
    2146             :              * Reconstruct the indexes to match, and we're done.
    2147             :              */
    2148        2398 :             reindex_relation(NULL, heap_relid, REINDEX_REL_PROCESS_TOAST,
    2149             :                              &reindex_params);
    2150             :         }
    2151             : 
    2152        2488 :         pgstat_count_truncate(rel);
    2153             :     }
    2154             : 
    2155             :     /* Now go through the hash table, and truncate foreign tables */
    2156        1284 :     if (ft_htab)
    2157             :     {
    2158             :         ForeignTruncateInfo *ft_info;
    2159             :         HASH_SEQ_STATUS seq;
    2160             : 
    2161          30 :         hash_seq_init(&seq, ft_htab);
    2162             : 
    2163          30 :         PG_TRY();
    2164             :         {
    2165          52 :             while ((ft_info = hash_seq_search(&seq)) != NULL)
    2166             :             {
    2167          30 :                 FdwRoutine *routine = GetFdwRoutineByServerId(ft_info->serverid);
    2168             : 
    2169             :                 /* truncate_check_rel() has checked that already */
    2170             :                 Assert(routine->ExecForeignTruncate != NULL);
    2171             : 
    2172          30 :                 routine->ExecForeignTruncate(ft_info->rels,
    2173             :                                              behavior,
    2174             :                                              restart_seqs);
    2175             :             }
    2176             :         }
    2177           8 :         PG_FINALLY();
    2178             :         {
    2179          30 :             hash_destroy(ft_htab);
    2180             :         }
    2181          30 :         PG_END_TRY();
    2182             :     }
    2183             : 
    2184             :     /*
    2185             :      * Restart owned sequences if we were asked to.
    2186             :      */
    2187        1312 :     foreach(cell, seq_relids)
    2188             :     {
    2189          36 :         Oid         seq_relid = lfirst_oid(cell);
    2190             : 
    2191          36 :         ResetSequence(seq_relid);
    2192             :     }
    2193             : 
    2194             :     /*
    2195             :      * Write a WAL record to allow this set of actions to be logically
    2196             :      * decoded.
    2197             :      *
    2198             :      * Assemble an array of relids so we can write a single WAL record for the
    2199             :      * whole action.
    2200             :      */
    2201        1276 :     if (relids_logged != NIL)
    2202             :     {
    2203             :         xl_heap_truncate xlrec;
    2204          52 :         int         i = 0;
    2205             : 
    2206             :         /* should only get here if wal_level >= logical */
    2207             :         Assert(XLogLogicalInfoActive());
    2208             : 
    2209          52 :         logrelids = palloc(list_length(relids_logged) * sizeof(Oid));
    2210         140 :         foreach(cell, relids_logged)
    2211          88 :             logrelids[i++] = lfirst_oid(cell);
    2212             : 
    2213          52 :         xlrec.dbId = MyDatabaseId;
    2214          52 :         xlrec.nrelids = list_length(relids_logged);
    2215          52 :         xlrec.flags = 0;
    2216          52 :         if (behavior == DROP_CASCADE)
    2217           2 :             xlrec.flags |= XLH_TRUNCATE_CASCADE;
    2218          52 :         if (restart_seqs)
    2219           4 :             xlrec.flags |= XLH_TRUNCATE_RESTART_SEQS;
    2220             : 
    2221          52 :         XLogBeginInsert();
    2222          52 :         XLogRegisterData((char *) &xlrec, SizeOfHeapTruncate);
    2223          52 :         XLogRegisterData((char *) logrelids, list_length(relids_logged) * sizeof(Oid));
    2224             : 
    2225          52 :         XLogSetRecordFlags(XLOG_INCLUDE_ORIGIN);
    2226             : 
    2227          52 :         (void) XLogInsert(RM_HEAP_ID, XLOG_HEAP_TRUNCATE);
    2228             :     }
    2229             : 
    2230             :     /*
    2231             :      * Process all AFTER STATEMENT TRUNCATE triggers.
    2232             :      */
    2233        1276 :     resultRelInfo = resultRelInfos;
    2234        4434 :     foreach(cell, rels)
    2235             :     {
    2236             :         UserContext ucxt;
    2237             : 
    2238        3158 :         if (run_as_table_owner)
    2239          70 :             SwitchToUntrustedUser(resultRelInfo->ri_RelationDesc->rd_rel->relowner,
    2240             :                                   &ucxt);
    2241        3158 :         ExecASTruncateTriggers(estate, resultRelInfo);
    2242        3158 :         if (run_as_table_owner)
    2243          70 :             RestoreUserContext(&ucxt);
    2244        3158 :         resultRelInfo++;
    2245             :     }
    2246             : 
    2247             :     /* Handle queued AFTER triggers */
    2248        1276 :     AfterTriggerEndQuery(estate);
    2249             : 
    2250             :     /* We can clean up the EState now */
    2251        1276 :     FreeExecutorState(estate);
    2252             : 
    2253             :     /*
    2254             :      * Close any rels opened by CASCADE (can't do this while EState still
    2255             :      * holds refs)
    2256             :      */
    2257        1276 :     rels = list_difference_ptr(rels, explicit_rels);
    2258        1370 :     foreach(cell, rels)
    2259             :     {
    2260          94 :         Relation    rel = (Relation) lfirst(cell);
    2261             : 
    2262          94 :         table_close(rel, NoLock);
    2263             :     }
    2264        1276 : }
    2265             : 
    2266             : /*
    2267             :  * Check that a given relation is safe to truncate.  Subroutine for
    2268             :  * ExecuteTruncate() and RangeVarCallbackForTruncate().
    2269             :  */
    2270             : static void
    2271        3360 : truncate_check_rel(Oid relid, Form_pg_class reltuple)
    2272             : {
    2273        3360 :     char       *relname = NameStr(reltuple->relname);
    2274             : 
    2275             :     /*
    2276             :      * Only allow truncate on regular tables, foreign tables using foreign
    2277             :      * data wrappers supporting TRUNCATE and partitioned tables (although, the
    2278             :      * latter are only being included here for the following checks; no
    2279             :      * physical truncation will occur in their case.).
    2280             :      */
    2281        3360 :     if (reltuple->relkind == RELKIND_FOREIGN_TABLE)
    2282             :     {
    2283          36 :         Oid         serverid = GetForeignServerIdByRelId(relid);
    2284          36 :         FdwRoutine *fdwroutine = GetFdwRoutineByServerId(serverid);
    2285             : 
    2286          36 :         if (!fdwroutine->ExecForeignTruncate)
    2287           2 :             ereport(ERROR,
    2288             :                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    2289             :                      errmsg("cannot truncate foreign table \"%s\"",
    2290             :                             relname)));
    2291             :     }
    2292        3324 :     else if (reltuple->relkind != RELKIND_RELATION &&
    2293         652 :              reltuple->relkind != RELKIND_PARTITIONED_TABLE)
    2294           0 :         ereport(ERROR,
    2295             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    2296             :                  errmsg("\"%s\" is not a table", relname)));
    2297             : 
    2298             :     /*
    2299             :      * Most system catalogs can't be truncated at all, or at least not unless
    2300             :      * allow_system_table_mods=on. As an exception, however, we allow
    2301             :      * pg_largeobject to be truncated as part of pg_upgrade, because we need
    2302             :      * to change its relfilenode to match the old cluster, and allowing a
    2303             :      * TRUNCATE command to be executed is the easiest way of doing that.
    2304             :      */
    2305        3358 :     if (!allowSystemTableMods && IsSystemClass(relid, reltuple)
    2306          22 :         && (!IsBinaryUpgrade || relid != LargeObjectRelationId))
    2307           2 :         ereport(ERROR,
    2308             :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
    2309             :                  errmsg("permission denied: \"%s\" is a system catalog",
    2310             :                         relname)));
    2311             : 
    2312        3356 :     InvokeObjectTruncateHook(relid);
    2313        3356 : }
    2314             : 
    2315             : /*
    2316             :  * Check that current user has the permission to truncate given relation.
    2317             :  */
    2318             : static void
    2319        1728 : truncate_check_perms(Oid relid, Form_pg_class reltuple)
    2320             : {
    2321        1728 :     char       *relname = NameStr(reltuple->relname);
    2322             :     AclResult   aclresult;
    2323             : 
    2324             :     /* Permissions checks */
    2325        1728 :     aclresult = pg_class_aclcheck(relid, GetUserId(), ACL_TRUNCATE);
    2326        1728 :     if (aclresult != ACLCHECK_OK)
    2327          32 :         aclcheck_error(aclresult, get_relkind_objtype(reltuple->relkind),
    2328             :                        relname);
    2329        1696 : }
    2330             : 
    2331             : /*
    2332             :  * Set of extra sanity checks to check if a given relation is safe to
    2333             :  * truncate.  This is split with truncate_check_rel() as
    2334             :  * RangeVarCallbackForTruncate() cannot open a Relation yet.
    2335             :  */
    2336             : static void
    2337        3254 : truncate_check_activity(Relation rel)
    2338             : {
    2339             :     /*
    2340             :      * Don't allow truncate on temp tables of other backends ... their local
    2341             :      * buffer manager is not going to cope.
    2342             :      */
    2343        3254 :     if (RELATION_IS_OTHER_TEMP(rel))
    2344           0 :         ereport(ERROR,
    2345             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    2346             :                  errmsg("cannot truncate temporary tables of other sessions")));
    2347             : 
    2348             :     /*
    2349             :      * Also check for active uses of the relation in the current transaction,
    2350             :      * including open scans and pending AFTER trigger events.
    2351             :      */
    2352        3254 :     CheckTableNotInUse(rel, "TRUNCATE");
    2353        3254 : }
    2354             : 
    2355             : /*
    2356             :  * storage_name
    2357             :  *    returns the name corresponding to a typstorage/attstorage enum value
    2358             :  */
    2359             : static const char *
    2360          24 : storage_name(char c)
    2361             : {
    2362          24 :     switch (c)
    2363             :     {
    2364           0 :         case TYPSTORAGE_PLAIN:
    2365           0 :             return "PLAIN";
    2366           0 :         case TYPSTORAGE_EXTERNAL:
    2367           0 :             return "EXTERNAL";
    2368          12 :         case TYPSTORAGE_EXTENDED:
    2369          12 :             return "EXTENDED";
    2370          12 :         case TYPSTORAGE_MAIN:
    2371          12 :             return "MAIN";
    2372           0 :         default:
    2373           0 :             return "???";
    2374             :     }
    2375             : }
    2376             : 
    2377             : /*----------
    2378             :  * MergeAttributes
    2379             :  *      Returns new schema given initial schema and superclasses.
    2380             :  *
    2381             :  * Input arguments:
    2382             :  * 'columns' is the column/attribute definition for the table. (It's a list
    2383             :  *      of ColumnDef's.) It is destructively changed.
    2384             :  * 'supers' is a list of OIDs of parent relations, already locked by caller.
    2385             :  * 'relpersistence' is the persistence type of the table.
    2386             :  * 'is_partition' tells if the table is a partition.
    2387             :  *
    2388             :  * Output arguments:
    2389             :  * 'supconstr' receives a list of constraints belonging to the parents,
    2390             :  *      updated as necessary to be valid for the child.
    2391             :  *
    2392             :  * Return value:
    2393             :  * Completed schema list.
    2394             :  *
    2395             :  * Notes:
    2396             :  *    The order in which the attributes are inherited is very important.
    2397             :  *    Intuitively, the inherited attributes should come first. If a table
    2398             :  *    inherits from multiple parents, the order of those attributes are
    2399             :  *    according to the order of the parents specified in CREATE TABLE.
    2400             :  *
    2401             :  *    Here's an example:
    2402             :  *
    2403             :  *      create table person (name text, age int4, location point);
    2404             :  *      create table emp (salary int4, manager text) inherits(person);
    2405             :  *      create table student (gpa float8) inherits (person);
    2406             :  *      create table stud_emp (percent int4) inherits (emp, student);
    2407             :  *
    2408             :  *    The order of the attributes of stud_emp is:
    2409             :  *
    2410             :  *                          person {1:name, 2:age, 3:location}
    2411             :  *                          /    \
    2412             :  *             {6:gpa}  student   emp {4:salary, 5:manager}
    2413             :  *                          \    /
    2414             :  *                         stud_emp {7:percent}
    2415             :  *
    2416             :  *     If the same attribute name appears multiple times, then it appears
    2417             :  *     in the result table in the proper location for its first appearance.
    2418             :  *
    2419             :  *     Constraints (including not-null constraints) for the child table
    2420             :  *     are the union of all relevant constraints, from both the child schema
    2421             :  *     and parent tables.
    2422             :  *
    2423             :  *     The default value for a child column is defined as:
    2424             :  *      (1) If the child schema specifies a default, that value is used.
    2425             :  *      (2) If neither the child nor any parent specifies a default, then
    2426             :  *          the column will not have a default.
    2427             :  *      (3) If conflicting defaults are inherited from different parents
    2428             :  *          (and not overridden by the child), an error is raised.
    2429             :  *      (4) Otherwise the inherited default is used.
    2430             :  *
    2431             :  *      Note that the default-value infrastructure is used for generated
    2432             :  *      columns' expressions too, so most of the preceding paragraph applies
    2433             :  *      to generation expressions too.  We insist that a child column be
    2434             :  *      generated if and only if its parent(s) are, but it need not have
    2435             :  *      the same generation expression.
    2436             :  *----------
    2437             :  */
    2438             : static List *
    2439       52564 : MergeAttributes(List *columns, const List *supers, char relpersistence,
    2440             :                 bool is_partition, List **supconstr)
    2441             : {
    2442       52564 :     List       *inh_columns = NIL;
    2443       52564 :     List       *constraints = NIL;
    2444       52564 :     bool        have_bogus_defaults = false;
    2445             :     int         child_attno;
    2446             :     static Node bogus_marker = {0}; /* marks conflicting defaults */
    2447       52564 :     List       *saved_columns = NIL;
    2448             :     ListCell   *lc;
    2449             : 
    2450             :     /*
    2451             :      * Check for and reject tables with too many columns. We perform this
    2452             :      * check relatively early for two reasons: (a) we don't run the risk of
    2453             :      * overflowing an AttrNumber in subsequent code (b) an O(n^2) algorithm is
    2454             :      * okay if we're processing <= 1600 columns, but could take minutes to
    2455             :      * execute if the user attempts to create a table with hundreds of
    2456             :      * thousands of columns.
    2457             :      *
    2458             :      * Note that we also need to check that we do not exceed this figure after
    2459             :      * including columns from inherited relations.
    2460             :      */
    2461       52564 :     if (list_length(columns) > MaxHeapAttributeNumber)
    2462           0 :         ereport(ERROR,
    2463             :                 (errcode(ERRCODE_TOO_MANY_COLUMNS),
    2464             :                  errmsg("tables can have at most %d columns",
    2465             :                         MaxHeapAttributeNumber)));
    2466             : 
    2467             :     /*
    2468             :      * Check for duplicate names in the explicit list of attributes.
    2469             :      *
    2470             :      * Although we might consider merging such entries in the same way that we
    2471             :      * handle name conflicts for inherited attributes, it seems to make more
    2472             :      * sense to assume such conflicts are errors.
    2473             :      *
    2474             :      * We don't use foreach() here because we have two nested loops over the
    2475             :      * columns list, with possible element deletions in the inner one.  If we
    2476             :      * used foreach_delete_current() it could only fix up the state of one of
    2477             :      * the loops, so it seems cleaner to use looping over list indexes for
    2478             :      * both loops.  Note that any deletion will happen beyond where the outer
    2479             :      * loop is, so its index never needs adjustment.
    2480             :      */
    2481      245954 :     for (int coldefpos = 0; coldefpos < list_length(columns); coldefpos++)
    2482             :     {
    2483      193414 :         ColumnDef  *coldef = list_nth_node(ColumnDef, columns, coldefpos);
    2484             : 
    2485      193414 :         if (!is_partition && coldef->typeName == NULL)
    2486             :         {
    2487             :             /*
    2488             :              * Typed table column option that does not belong to a column from
    2489             :              * the type.  This works because the columns from the type come
    2490             :              * first in the list.  (We omit this check for partition column
    2491             :              * lists; those are processed separately below.)
    2492             :              */
    2493           6 :             ereport(ERROR,
    2494             :                     (errcode(ERRCODE_UNDEFINED_COLUMN),
    2495             :                      errmsg("column \"%s\" does not exist",
    2496             :                             coldef->colname)));
    2497             :         }
    2498             : 
    2499             :         /* restpos scans all entries beyond coldef; incr is in loop body */
    2500     6165864 :         for (int restpos = coldefpos + 1; restpos < list_length(columns);)
    2501             :         {
    2502     5972474 :             ColumnDef  *restdef = list_nth_node(ColumnDef, columns, restpos);
    2503             : 
    2504     5972474 :             if (strcmp(coldef->colname, restdef->colname) == 0)
    2505             :             {
    2506          50 :                 if (coldef->is_from_type)
    2507             :                 {
    2508             :                     /*
    2509             :                      * merge the column options into the column from the type
    2510             :                      */
    2511          32 :                     coldef->is_not_null = restdef->is_not_null;
    2512          32 :                     coldef->raw_default = restdef->raw_default;
    2513          32 :                     coldef->cooked_default = restdef->cooked_default;
    2514          32 :                     coldef->constraints = restdef->constraints;
    2515          32 :                     coldef->is_from_type = false;
    2516          32 :                     columns = list_delete_nth_cell(columns, restpos);
    2517             :                 }
    2518             :                 else
    2519          18 :                     ereport(ERROR,
    2520             :                             (errcode(ERRCODE_DUPLICATE_COLUMN),
    2521             :                              errmsg("column \"%s\" specified more than once",
    2522             :                                     coldef->colname)));
    2523             :             }
    2524             :             else
    2525     5972424 :                 restpos++;
    2526             :         }
    2527             :     }
    2528             : 
    2529             :     /*
    2530             :      * In case of a partition, there are no new column definitions, only dummy
    2531             :      * ColumnDefs created for column constraints.  Set them aside for now and
    2532             :      * process them at the end.
    2533             :      */
    2534       52540 :     if (is_partition)
    2535             :     {
    2536        8344 :         saved_columns = columns;
    2537        8344 :         columns = NIL;
    2538             :     }
    2539             : 
    2540             :     /*
    2541             :      * Scan the parents left-to-right, and merge their attributes to form a
    2542             :      * list of inherited columns (inh_columns).
    2543             :      */
    2544       52540 :     child_attno = 0;
    2545       62632 :     foreach(lc, supers)
    2546             :     {
    2547       10164 :         Oid         parent = lfirst_oid(lc);
    2548             :         Relation    relation;
    2549             :         TupleDesc   tupleDesc;
    2550             :         TupleConstr *constr;
    2551             :         AttrMap    *newattmap;
    2552             :         List       *inherited_defaults;
    2553             :         List       *cols_with_defaults;
    2554             :         ListCell   *lc1;
    2555             :         ListCell   *lc2;
    2556             : 
    2557             :         /* caller already got lock */
    2558       10164 :         relation = table_open(parent, NoLock);
    2559             : 
    2560             :         /*
    2561             :          * Check for active uses of the parent partitioned table in the
    2562             :          * current transaction, such as being used in some manner by an
    2563             :          * enclosing command.
    2564             :          */
    2565       10164 :         if (is_partition)
    2566        8344 :             CheckTableNotInUse(relation, "CREATE TABLE .. PARTITION OF");
    2567             : 
    2568             :         /*
    2569             :          * We do not allow partitioned tables and partitions to participate in
    2570             :          * regular inheritance.
    2571             :          */
    2572       10158 :         if (relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && !is_partition)
    2573           6 :             ereport(ERROR,
    2574             :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    2575             :                      errmsg("cannot inherit from partitioned table \"%s\"",
    2576             :                             RelationGetRelationName(relation))));
    2577       10152 :         if (relation->rd_rel->relispartition && !is_partition)
    2578           6 :             ereport(ERROR,
    2579             :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    2580             :                      errmsg("cannot inherit from partition \"%s\"",
    2581             :                             RelationGetRelationName(relation))));
    2582             : 
    2583       10146 :         if (relation->rd_rel->relkind != RELKIND_RELATION &&
    2584        8340 :             relation->rd_rel->relkind != RELKIND_FOREIGN_TABLE &&
    2585        8320 :             relation->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
    2586           0 :             ereport(ERROR,
    2587             :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    2588             :                      errmsg("inherited relation \"%s\" is not a table or foreign table",
    2589             :                             RelationGetRelationName(relation))));
    2590             : 
    2591             :         /*
    2592             :          * If the parent is permanent, so must be all of its partitions.  Note
    2593             :          * that inheritance allows that case.
    2594             :          */
    2595       10146 :         if (is_partition &&
    2596        8338 :             relation->rd_rel->relpersistence != RELPERSISTENCE_TEMP &&
    2597             :             relpersistence == RELPERSISTENCE_TEMP)
    2598           6 :             ereport(ERROR,
    2599             :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    2600             :                      errmsg("cannot create a temporary relation as partition of permanent relation \"%s\"",
    2601             :                             RelationGetRelationName(relation))));
    2602             : 
    2603             :         /* Permanent rels cannot inherit from temporary ones */
    2604       10140 :         if (relpersistence != RELPERSISTENCE_TEMP &&
    2605        9762 :             relation->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
    2606          24 :             ereport(ERROR,
    2607             :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    2608             :                      errmsg(!is_partition
    2609             :                             ? "cannot inherit from temporary relation \"%s\""
    2610             :                             : "cannot create a permanent relation as partition of temporary relation \"%s\"",
    2611             :                             RelationGetRelationName(relation))));
    2612             : 
    2613             :         /* If existing rel is temp, it must belong to this session */
    2614       10116 :         if (relation->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
    2615         330 :             !relation->rd_islocaltemp)
    2616           0 :             ereport(ERROR,
    2617             :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    2618             :                      errmsg(!is_partition
    2619             :                             ? "cannot inherit from temporary relation of another session"
    2620             :                             : "cannot create as partition of temporary relation of another session")));
    2621             : 
    2622             :         /*
    2623             :          * We should have an UNDER permission flag for this, but for now,
    2624             :          * demand that creator of a child table own the parent.
    2625             :          */
    2626       10116 :         if (!object_ownercheck(RelationRelationId, RelationGetRelid(relation), GetUserId()))
    2627           0 :             aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(relation->rd_rel->relkind),
    2628           0 :                            RelationGetRelationName(relation));
    2629             : 
    2630       10116 :         tupleDesc = RelationGetDescr(relation);
    2631       10116 :         constr = tupleDesc->constr;
    2632             : 
    2633             :         /*
    2634             :          * newattmap->attnums[] will contain the child-table attribute numbers
    2635             :          * for the attributes of this parent table.  (They are not the same
    2636             :          * for parents after the first one, nor if we have dropped columns.)
    2637             :          */
    2638       10116 :         newattmap = make_attrmap(tupleDesc->natts);
    2639             : 
    2640             :         /* We can't process inherited defaults until newattmap is complete. */
    2641       10116 :         inherited_defaults = cols_with_defaults = NIL;
    2642             : 
    2643       31572 :         for (AttrNumber parent_attno = 1; parent_attno <= tupleDesc->natts;
    2644       21456 :              parent_attno++)
    2645             :         {
    2646       21480 :             Form_pg_attribute attribute = TupleDescAttr(tupleDesc,
    2647             :                                                         parent_attno - 1);
    2648       21480 :             char       *attributeName = NameStr(attribute->attname);
    2649             :             int         exist_attno;
    2650             :             ColumnDef  *newdef;
    2651             :             ColumnDef  *mergeddef;
    2652             : 
    2653             :             /*
    2654             :              * Ignore dropped columns in the parent.
    2655             :              */
    2656       21480 :             if (attribute->attisdropped)
    2657         192 :                 continue;       /* leave newattmap->attnums entry as zero */
    2658             : 
    2659             :             /*
    2660             :              * Create new column definition
    2661             :              */
    2662       21288 :             newdef = makeColumnDef(attributeName, attribute->atttypid,
    2663             :                                    attribute->atttypmod, attribute->attcollation);
    2664       21288 :             newdef->is_not_null = attribute->attnotnull;
    2665       21288 :             newdef->storage = attribute->attstorage;
    2666       21288 :             newdef->generated = attribute->attgenerated;
    2667       21288 :             if (CompressionMethodIsValid(attribute->attcompression))
    2668          30 :                 newdef->compression =
    2669          30 :                     pstrdup(GetCompressionMethodName(attribute->attcompression));
    2670             : 
    2671             :             /*
    2672             :              * Regular inheritance children are independent enough not to
    2673             :              * inherit identity columns.  But partitions are integral part of
    2674             :              * a partitioned table and inherit identity column.
    2675             :              */
    2676       21288 :             if (is_partition)
    2677       17770 :                 newdef->identity = attribute->attidentity;
    2678             : 
    2679             :             /*
    2680             :              * Does it match some previously considered column from another
    2681             :              * parent?
    2682             :              */
    2683       21288 :             exist_attno = findAttrByName(attributeName, inh_columns);
    2684       21288 :             if (exist_attno > 0)
    2685             :             {
    2686             :                 /*
    2687             :                  * Yes, try to merge the two column definitions.
    2688             :                  */
    2689         230 :                 mergeddef = MergeInheritedAttribute(inh_columns, exist_attno, newdef);
    2690             : 
    2691         206 :                 newattmap->attnums[parent_attno - 1] = exist_attno;
    2692             : 
    2693             :                 /*
    2694             :                  * Partitions have only one parent, so conflict should never
    2695             :                  * occur.
    2696             :                  */
    2697             :                 Assert(!is_partition);
    2698             :             }
    2699             :             else
    2700             :             {
    2701             :                 /*
    2702             :                  * No, create a new inherited column
    2703             :                  */
    2704       21058 :                 newdef->inhcount = 1;
    2705       21058 :                 newdef->is_local = false;
    2706       21058 :                 inh_columns = lappend(inh_columns, newdef);
    2707             : 
    2708       21058 :                 newattmap->attnums[parent_attno - 1] = ++child_attno;
    2709       21058 :                 mergeddef = newdef;
    2710             :             }
    2711             : 
    2712             :             /*
    2713             :              * Locate default/generation expression if any
    2714             :              */
    2715       21264 :             if (attribute->atthasdef)
    2716             :             {
    2717             :                 Node       *this_default;
    2718             : 
    2719         658 :                 this_default = TupleDescGetDefault(tupleDesc, parent_attno);
    2720         658 :                 if (this_default == NULL)
    2721           0 :                     elog(ERROR, "default expression not found for attribute %d of relation \"%s\"",
    2722             :                          parent_attno, RelationGetRelationName(relation));
    2723             : 
    2724             :                 /*
    2725             :                  * If it's a GENERATED default, it might contain Vars that
    2726             :                  * need to be mapped to the inherited column(s)' new numbers.
    2727             :                  * We can't do that till newattmap is ready, so just remember
    2728             :                  * all the inherited default expressions for the moment.
    2729             :                  */
    2730         658 :                 inherited_defaults = lappend(inherited_defaults, this_default);
    2731         658 :                 cols_with_defaults = lappend(cols_with_defaults, mergeddef);
    2732             :             }
    2733             :         }
    2734             : 
    2735             :         /*
    2736             :          * Now process any inherited default expressions, adjusting attnos
    2737             :          * using the completed newattmap map.
    2738             :          */
    2739       10750 :         forboth(lc1, inherited_defaults, lc2, cols_with_defaults)
    2740             :         {
    2741         658 :             Node       *this_default = (Node *) lfirst(lc1);
    2742         658 :             ColumnDef  *def = (ColumnDef *) lfirst(lc2);
    2743             :             bool        found_whole_row;
    2744             : 
    2745             :             /* Adjust Vars to match new table's column numbering */
    2746         658 :             this_default = map_variable_attnos(this_default,
    2747             :                                                1, 0,
    2748             :                                                newattmap,
    2749             :                                                InvalidOid, &found_whole_row);
    2750             : 
    2751             :             /*
    2752             :              * For the moment we have to reject whole-row variables.  We could
    2753             :              * convert them, if we knew the new table's rowtype OID, but that
    2754             :              * hasn't been assigned yet.  (A variable could only appear in a
    2755             :              * generation expression, so the error message is correct.)
    2756             :              */
    2757         658 :             if (found_whole_row)
    2758           0 :                 ereport(ERROR,
    2759             :                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    2760             :                          errmsg("cannot convert whole-row table reference"),
    2761             :                          errdetail("Generation expression for column \"%s\" contains a whole-row reference to table \"%s\".",
    2762             :                                    def->colname,
    2763             :                                    RelationGetRelationName(relation))));
    2764             : 
    2765             :             /*
    2766             :              * If we already had a default from some prior parent, check to
    2767             :              * see if they are the same.  If so, no problem; if not, mark the
    2768             :              * column as having a bogus default.  Below, we will complain if
    2769             :              * the bogus default isn't overridden by the child columns.
    2770             :              */
    2771             :             Assert(def->raw_default == NULL);
    2772         658 :             if (def->cooked_default == NULL)
    2773         628 :                 def->cooked_default = this_default;
    2774          30 :             else if (!equal(def->cooked_default, this_default))
    2775             :             {
    2776          24 :                 def->cooked_default = &bogus_marker;
    2777          24 :                 have_bogus_defaults = true;
    2778             :             }
    2779             :         }
    2780             : 
    2781             :         /*
    2782             :          * Now copy the CHECK constraints of this parent, adjusting attnos
    2783             :          * using the completed newattmap map.  Identically named constraints
    2784             :          * are merged if possible, else we throw error.
    2785             :          */
    2786       10092 :         if (constr && constr->num_check > 0)
    2787             :         {
    2788         304 :             ConstrCheck *check = constr->check;
    2789             : 
    2790         638 :             for (int i = 0; i < constr->num_check; i++)
    2791             :             {
    2792         334 :                 char       *name = check[i].ccname;
    2793             :                 Node       *expr;
    2794             :                 bool        found_whole_row;
    2795             : 
    2796             :                 /* ignore if the constraint is non-inheritable */
    2797         334 :                 if (check[i].ccnoinherit)
    2798          48 :                     continue;
    2799             : 
    2800             :                 /* Adjust Vars to match new table's column numbering */
    2801         286 :                 expr = map_variable_attnos(stringToNode(check[i].ccbin),
    2802             :                                            1, 0,
    2803             :                                            newattmap,
    2804             :                                            InvalidOid, &found_whole_row);
    2805             : 
    2806             :                 /*
    2807             :                  * For the moment we have to reject whole-row variables. We
    2808             :                  * could convert them, if we knew the new table's rowtype OID,
    2809             :                  * but that hasn't been assigned yet.
    2810             :                  */
    2811         286 :                 if (found_whole_row)
    2812           0 :                     ereport(ERROR,
    2813             :                             (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    2814             :                              errmsg("cannot convert whole-row table reference"),
    2815             :                              errdetail("Constraint \"%s\" contains a whole-row reference to table \"%s\".",
    2816             :                                        name,
    2817             :                                        RelationGetRelationName(relation))));
    2818             : 
    2819         286 :                 constraints = MergeCheckConstraint(constraints, name, expr);
    2820             :             }
    2821             :         }
    2822             : 
    2823       10092 :         free_attrmap(newattmap);
    2824             : 
    2825             :         /*
    2826             :          * Close the parent rel, but keep our lock on it until xact commit.
    2827             :          * That will prevent someone else from deleting or ALTERing the parent
    2828             :          * before the child is committed.
    2829             :          */
    2830       10092 :         table_close(relation, NoLock);
    2831             :     }
    2832             : 
    2833             :     /*
    2834             :      * If we had no inherited attributes, the result columns are just the
    2835             :      * explicitly declared columns.  Otherwise, we need to merge the declared
    2836             :      * columns into the inherited column list.  Although, we never have any
    2837             :      * explicitly declared columns if the table is a partition.
    2838             :      */
    2839       52468 :     if (inh_columns != NIL)
    2840             :     {
    2841        9826 :         int         newcol_attno = 0;
    2842             : 
    2843       10556 :         foreach(lc, columns)
    2844             :         {
    2845         778 :             ColumnDef  *newdef = lfirst_node(ColumnDef, lc);
    2846         778 :             char       *attributeName = newdef->colname;
    2847             :             int         exist_attno;
    2848             : 
    2849             :             /*
    2850             :              * Partitions have only one parent and have no column definitions
    2851             :              * of their own, so conflict should never occur.
    2852             :              */
    2853             :             Assert(!is_partition);
    2854             : 
    2855         778 :             newcol_attno++;
    2856             : 
    2857             :             /*
    2858             :              * Does it match some inherited column?
    2859             :              */
    2860         778 :             exist_attno = findAttrByName(attributeName, inh_columns);
    2861         778 :             if (exist_attno > 0)
    2862             :             {
    2863             :                 /*
    2864             :                  * Yes, try to merge the two column definitions.
    2865             :                  */
    2866         244 :                 MergeChildAttribute(inh_columns, exist_attno, newcol_attno, newdef);
    2867             :             }
    2868             :             else
    2869             :             {
    2870             :                 /*
    2871             :                  * No, attach new column unchanged to result columns.
    2872             :                  */
    2873         534 :                 inh_columns = lappend(inh_columns, newdef);
    2874             :             }
    2875             :         }
    2876             : 
    2877        9778 :         columns = inh_columns;
    2878             : 
    2879             :         /*
    2880             :          * Check that we haven't exceeded the legal # of columns after merging
    2881             :          * in inherited columns.
    2882             :          */
    2883        9778 :         if (list_length(columns) > MaxHeapAttributeNumber)
    2884           0 :             ereport(ERROR,
    2885             :                     (errcode(ERRCODE_TOO_MANY_COLUMNS),
    2886             :                      errmsg("tables can have at most %d columns",
    2887             :                             MaxHeapAttributeNumber)));
    2888             :     }
    2889             : 
    2890             :     /*
    2891             :      * Now that we have the column definition list for a partition, we can
    2892             :      * check whether the columns referenced in the column constraint specs
    2893             :      * actually exist.  Also, we merge parent's not-null constraints and
    2894             :      * defaults into each corresponding column definition.
    2895             :      */
    2896       52420 :     if (is_partition)
    2897             :     {
    2898        8514 :         foreach(lc, saved_columns)
    2899             :         {
    2900         212 :             ColumnDef  *restdef = lfirst(lc);
    2901         212 :             bool        found = false;
    2902             :             ListCell   *l;
    2903             : 
    2904         804 :             foreach(l, columns)
    2905             :             {
    2906         604 :                 ColumnDef  *coldef = lfirst(l);
    2907             : 
    2908         604 :                 if (strcmp(coldef->colname, restdef->colname) == 0)
    2909             :                 {
    2910         212 :                     found = true;
    2911         212 :                     coldef->is_not_null |= restdef->is_not_null;
    2912             : 
    2913             :                     /*
    2914             :                      * Check for conflicts related to generated columns.
    2915             :                      *
    2916             :                      * Same rules as above: generated-ness has to match the
    2917             :                      * parent, but the contents of the generation expression
    2918             :                      * can be different.
    2919             :                      */
    2920         212 :                     if (coldef->generated)
    2921             :                     {
    2922         110 :                         if (restdef->raw_default && !restdef->generated)
    2923           6 :                             ereport(ERROR,
    2924             :                                     (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
    2925             :                                      errmsg("column \"%s\" inherits from generated column but specifies default",
    2926             :                                             restdef->colname)));
    2927         104 :                         if (restdef->identity)
    2928           0 :                             ereport(ERROR,
    2929             :                                     (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
    2930             :                                      errmsg("column \"%s\" inherits from generated column but specifies identity",
    2931             :                                             restdef->colname)));
    2932             :                     }
    2933             :                     else
    2934             :                     {
    2935         102 :                         if (restdef->generated)
    2936           6 :                             ereport(ERROR,
    2937             :                                     (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
    2938             :                                      errmsg("child column \"%s\" specifies generation expression",
    2939             :                                             restdef->colname),
    2940             :                                      errhint("A child table column cannot be generated unless its parent column is.")));
    2941             :                     }
    2942             : 
    2943             :                     /*
    2944             :                      * Override the parent's default value for this column
    2945             :                      * (coldef->cooked_default) with the partition's local
    2946             :                      * definition (restdef->raw_default), if there's one. It
    2947             :                      * should be physically impossible to get a cooked default
    2948             :                      * in the local definition or a raw default in the
    2949             :                      * inherited definition, but make sure they're nulls, for
    2950             :                      * future-proofing.
    2951             :                      */
    2952             :                     Assert(restdef->cooked_default == NULL);
    2953             :                     Assert(coldef->raw_default == NULL);
    2954         200 :                     if (restdef->raw_default)
    2955             :                     {
    2956         128 :                         coldef->raw_default = restdef->raw_default;
    2957         128 :                         coldef->cooked_default = NULL;
    2958             :                     }
    2959             :                 }
    2960             :             }
    2961             : 
    2962             :             /* complain for constraints on columns not in parent */
    2963         200 :             if (!found)
    2964           0 :                 ereport(ERROR,
    2965             :                         (errcode(ERRCODE_UNDEFINED_COLUMN),
    2966             :                          errmsg("column \"%s\" does not exist",
    2967             :                                 restdef->colname)));
    2968             :         }
    2969             :     }
    2970             : 
    2971             :     /*
    2972             :      * If we found any conflicting parent default values, check to make sure
    2973             :      * they were overridden by the child.
    2974             :      */
    2975       52408 :     if (have_bogus_defaults)
    2976             :     {
    2977          54 :         foreach(lc, columns)
    2978             :         {
    2979          42 :             ColumnDef  *def = lfirst(lc);
    2980             : 
    2981          42 :             if (def->cooked_default == &bogus_marker)
    2982             :             {
    2983          12 :                 if (def->generated)
    2984           6 :                     ereport(ERROR,
    2985             :                             (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
    2986             :                              errmsg("column \"%s\" inherits conflicting generation expressions",
    2987             :                                     def->colname),
    2988             :                              errhint("To resolve the conflict, specify a generation expression explicitly.")));
    2989             :                 else
    2990           6 :                     ereport(ERROR,
    2991             :                             (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
    2992             :                              errmsg("column \"%s\" inherits conflicting default values",
    2993             :                                     def->colname),
    2994             :                              errhint("To resolve the conflict, specify a default explicitly.")));
    2995             :             }
    2996             :         }
    2997             :     }
    2998             : 
    2999       52396 :     *supconstr = constraints;
    3000             : 
    3001       52396 :     return columns;
    3002             : }
    3003             : 
    3004             : 
    3005             : /*
    3006             :  * MergeCheckConstraint
    3007             :  *      Try to merge an inherited CHECK constraint with previous ones
    3008             :  *
    3009             :  * If we inherit identically-named constraints from multiple parents, we must
    3010             :  * merge them, or throw an error if they don't have identical definitions.
    3011             :  *
    3012             :  * constraints is a list of CookedConstraint structs for previous constraints.
    3013             :  *
    3014             :  * If the new constraint matches an existing one, then the existing
    3015             :  * constraint's inheritance count is updated.  If there is a conflict (same
    3016             :  * name but different expression), throw an error.  If the constraint neither
    3017             :  * matches nor conflicts with an existing one, a new constraint is appended to
    3018             :  * the list.
    3019             :  */
    3020             : static List *
    3021         286 : MergeCheckConstraint(List *constraints, const char *name, Node *expr)
    3022             : {
    3023             :     ListCell   *lc;
    3024             :     CookedConstraint *newcon;
    3025             : 
    3026         316 :     foreach(lc, constraints)
    3027             :     {
    3028          72 :         CookedConstraint *ccon = (CookedConstraint *) lfirst(lc);
    3029             : 
    3030             :         Assert(ccon->contype == CONSTR_CHECK);
    3031             : 
    3032             :         /* Non-matching names never conflict */
    3033          72 :         if (strcmp(ccon->name, name) != 0)
    3034          30 :             continue;
    3035             : 
    3036          42 :         if (equal(expr, ccon->expr))
    3037             :         {
    3038             :             /* OK to merge constraint with existing */
    3039          42 :             ccon->inhcount++;
    3040          42 :             if (ccon->inhcount < 0)
    3041           0 :                 ereport(ERROR,
    3042             :                         errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
    3043             :                         errmsg("too many inheritance parents"));
    3044          42 :             return constraints;
    3045             :         }
    3046             : 
    3047           0 :         ereport(ERROR,
    3048             :                 (errcode(ERRCODE_DUPLICATE_OBJECT),
    3049             :                  errmsg("check constraint name \"%s\" appears multiple times but with different expressions",
    3050             :                         name)));
    3051             :     }
    3052             : 
    3053             :     /*
    3054             :      * Constraint couldn't be merged with an existing one and also didn't
    3055             :      * conflict with an existing one, so add it as a new one to the list.
    3056             :      */
    3057         244 :     newcon = palloc0_object(CookedConstraint);
    3058         244 :     newcon->contype = CONSTR_CHECK;
    3059         244 :     newcon->name = pstrdup(name);
    3060         244 :     newcon->expr = expr;
    3061         244 :     newcon->inhcount = 1;
    3062         244 :     return lappend(constraints, newcon);
    3063             : }
    3064             : 
    3065             : /*
    3066             :  * MergeChildAttribute
    3067             :  *      Merge given child attribute definition into given inherited attribute.
    3068             :  *
    3069             :  * Input arguments:
    3070             :  * 'inh_columns' is the list of inherited ColumnDefs.
    3071             :  * 'exist_attno' is the number of the inherited attribute in inh_columns
    3072             :  * 'newcol_attno' is the attribute number in child table's schema definition
    3073             :  * 'newdef' is the column/attribute definition from the child table.
    3074             :  *
    3075             :  * The ColumnDef in 'inh_columns' list is modified.  The child attribute's
    3076             :  * ColumnDef remains unchanged.
    3077             :  *
    3078             :  * Notes:
    3079             :  * - The attribute is merged according to the rules laid out in the prologue
    3080             :  *   of MergeAttributes().
    3081             :  * - If matching inherited attribute exists but the child attribute can not be
    3082             :  *   merged into it, the function throws respective errors.
    3083             :  * - A partition can not have its own column definitions. Hence this function
    3084             :  *   is applicable only to a regular inheritance child.
    3085             :  */
    3086             : static void
    3087         244 : MergeChildAttribute(List *inh_columns, int exist_attno, int newcol_attno, const ColumnDef *newdef)
    3088             : {
    3089         244 :     char       *attributeName = newdef->colname;
    3090             :     ColumnDef  *inhdef;
    3091             :     Oid         inhtypeid,
    3092             :                 newtypeid;
    3093             :     int32       inhtypmod,
    3094             :                 newtypmod;
    3095             :     Oid         inhcollid,
    3096             :                 newcollid;
    3097             : 
    3098         244 :     if (exist_attno == newcol_attno)
    3099         222 :         ereport(NOTICE,
    3100             :                 (errmsg("merging column \"%s\" with inherited definition",
    3101             :                         attributeName)));
    3102             :     else
    3103          22 :         ereport(NOTICE,
    3104             :                 (errmsg("moving and merging column \"%s\" with inherited definition", attributeName),
    3105             :                  errdetail("User-specified column moved to the position of the inherited column.")));
    3106             : 
    3107         244 :     inhdef = list_nth_node(ColumnDef, inh_columns, exist_attno - 1);
    3108             : 
    3109             :     /*
    3110             :      * Must have the same type and typmod
    3111             :      */
    3112         244 :     typenameTypeIdAndMod(NULL, inhdef->typeName, &inhtypeid, &inhtypmod);
    3113         244 :     typenameTypeIdAndMod(NULL, newdef->typeName, &newtypeid, &newtypmod);
    3114         244 :     if (inhtypeid != newtypeid || inhtypmod != newtypmod)
    3115          12 :         ereport(ERROR,
    3116             :                 (errcode(ERRCODE_DATATYPE_MISMATCH),
    3117             :                  errmsg("column \"%s\" has a type conflict",
    3118             :                         attributeName),
    3119             :                  errdetail("%s versus %s",
    3120             :                            format_type_with_typemod(inhtypeid, inhtypmod),
    3121             :                            format_type_with_typemod(newtypeid, newtypmod))));
    3122             : 
    3123             :     /*
    3124             :      * Must have the same collation
    3125             :      */
    3126         232 :     inhcollid = GetColumnDefCollation(NULL, inhdef, inhtypeid);
    3127         232 :     newcollid = GetColumnDefCollation(NULL, newdef, newtypeid);
    3128         232 :     if (inhcollid != newcollid)
    3129           6 :         ereport(ERROR,
    3130             :                 (errcode(ERRCODE_COLLATION_MISMATCH),
    3131             :                  errmsg("column \"%s\" has a collation conflict",
    3132             :                         attributeName),
    3133             :                  errdetail("\"%s\" versus \"%s\"",
    3134             :                            get_collation_name(inhcollid),
    3135             :                            get_collation_name(newcollid))));
    3136             : 
    3137             :     /*
    3138             :      * Identity is never inherited by a regular inheritance child. Pick
    3139             :      * child's identity definition if there's one.
    3140             :      */
    3141         226 :     inhdef->identity = newdef->identity;
    3142             : 
    3143             :     /*
    3144             :      * Copy storage parameter
    3145             :      */
    3146         226 :     if (inhdef->storage == 0)
    3147           0 :         inhdef->storage = newdef->storage;
    3148         226 :     else if (newdef->storage != 0 && inhdef->storage != newdef->storage)
    3149           6 :         ereport(ERROR,
    3150             :                 (errcode(ERRCODE_DATATYPE_MISMATCH),
    3151             :                  errmsg("column \"%s\" has a storage parameter conflict",
    3152             :                         attributeName),
    3153             :                  errdetail("%s versus %s",
    3154             :                            storage_name(inhdef->storage),
    3155             :                            storage_name(newdef->storage))));
    3156             : 
    3157             :     /*
    3158             :      * Copy compression parameter
    3159             :      */
    3160         220 :     if (inhdef->compression == NULL)
    3161         214 :         inhdef->compression = newdef->compression;
    3162           6 :     else if (newdef->compression != NULL)
    3163             :     {
    3164           6 :         if (strcmp(inhdef->compression, newdef->compression) != 0)
    3165           6 :             ereport(ERROR,
    3166             :                     (errcode(ERRCODE_DATATYPE_MISMATCH),
    3167             :                      errmsg("column \"%s\" has a compression method conflict",
    3168             :                             attributeName),
    3169             :                      errdetail("%s versus %s", inhdef->compression, newdef->compression)));
    3170             :     }
    3171             : 
    3172             :     /*
    3173             :      * Merge of not-null constraints = OR 'em together
    3174             :      */
    3175         214 :     inhdef->is_not_null |= newdef->is_not_null;
    3176             : 
    3177             :     /*
    3178             :      * Check for conflicts related to generated columns.
    3179             :      *
    3180             :      * If the parent column is generated, the child column will be made a
    3181             :      * generated column if it isn't already.  If it is a generated column,
    3182             :      * we'll take its generation expression in preference to the parent's.  We
    3183             :      * must check that the child column doesn't specify a default value or
    3184             :      * identity, which matches the rules for a single column in
    3185             :      * parse_utilcmd.c.
    3186             :      *
    3187             :      * Conversely, if the parent column is not generated, the child column
    3188             :      * can't be either.  (We used to allow that, but it results in being able
    3189             :      * to override the generation expression via UPDATEs through the parent.)
    3190             :      */
    3191         214 :     if (inhdef->generated)
    3192             :     {
    3193          26 :         if (newdef->raw_default && !newdef->generated)
    3194           6 :             ereport(ERROR,
    3195             :                     (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
    3196             :                      errmsg("column \"%s\" inherits from generated column but specifies default",
    3197             :                             inhdef->colname)));
    3198          20 :         if (newdef->identity)
    3199           6 :             ereport(ERROR,
    3200             :                     (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
    3201             :                      errmsg("column \"%s\" inherits from generated column but specifies identity",
    3202             :                             inhdef->colname)));
    3203             :     }
    3204             :     else
    3205             :     {
    3206         188 :         if (newdef->generated)
    3207           6 :             ereport(ERROR,
    3208             :                     (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
    3209             :                      errmsg("child column \"%s\" specifies generation expression",
    3210             :                             inhdef->colname),
    3211             :                      errhint("A child table column cannot be generated unless its parent column is.")));
    3212             :     }
    3213             : 
    3214             :     /*
    3215             :      * If new def has a default, override previous default
    3216             :      */
    3217         196 :     if (newdef->raw_default != NULL)
    3218             :     {
    3219          18 :         inhdef->raw_default = newdef->raw_default;
    3220          18 :         inhdef->cooked_default = newdef->cooked_default;
    3221             :     }
    3222             : 
    3223             :     /* Mark the column as locally defined */
    3224         196 :     inhdef->is_local = true;
    3225         196 : }
    3226             : 
    3227             : /*
    3228             :  * MergeInheritedAttribute
    3229             :  *      Merge given parent attribute definition into specified attribute
    3230             :  *      inherited from the previous parents.
    3231             :  *
    3232             :  * Input arguments:
    3233             :  * 'inh_columns' is the list of previously inherited ColumnDefs.
    3234             :  * 'exist_attno' is the number the existing matching attribute in inh_columns.
    3235             :  * 'newdef' is the new parent column/attribute definition to be merged.
    3236             :  *
    3237             :  * The matching ColumnDef in 'inh_columns' list is modified and returned.
    3238             :  *
    3239             :  * Notes:
    3240             :  * - The attribute is merged according to the rules laid out in the prologue
    3241             :  *   of MergeAttributes().
    3242             :  * - If matching inherited attribute exists but the new attribute can not be
    3243             :  *   merged into it, the function throws respective errors.
    3244             :  * - A partition inherits from only a single parent. Hence this function is
    3245             :  *   applicable only to a regular inheritance.
    3246             :  */
    3247             : static ColumnDef *
    3248         230 : MergeInheritedAttribute(List *inh_columns,
    3249             :                         int exist_attno,
    3250             :                         const ColumnDef *newdef)
    3251             : {
    3252         230 :     char       *attributeName = newdef->colname;
    3253             :     ColumnDef  *prevdef;
    3254             :     Oid         prevtypeid,
    3255             :                 newtypeid;
    3256             :     int32       prevtypmod,
    3257             :                 newtypmod;
    3258             :     Oid         prevcollid,
    3259             :                 newcollid;
    3260             : 
    3261         230 :     ereport(NOTICE,
    3262             :             (errmsg("merging multiple inherited definitions of column \"%s\"",
    3263             :                     attributeName)));
    3264         230 :     prevdef = list_nth_node(ColumnDef, inh_columns, exist_attno - 1);
    3265             : 
    3266             :     /*
    3267             :      * Must have the same type and typmod
    3268             :      */
    3269         230 :     typenameTypeIdAndMod(NULL, prevdef->typeName, &prevtypeid, &prevtypmod);
    3270         230 :     typenameTypeIdAndMod(NULL, newdef->typeName, &newtypeid, &newtypmod);
    3271         230 :     if (prevtypeid != newtypeid || prevtypmod != newtypmod)
    3272           0 :         ereport(ERROR,
    3273             :                 (errcode(ERRCODE_DATATYPE_MISMATCH),
    3274             :                  errmsg("inherited column \"%s\" has a type conflict",
    3275             :                         attributeName),
    3276             :                  errdetail("%s versus %s",
    3277             :                            format_type_with_typemod(prevtypeid, prevtypmod),
    3278             :                            format_type_with_typemod(newtypeid, newtypmod))));
    3279             : 
    3280             :     /*
    3281             :      * Merge of not-null constraints = OR 'em together
    3282             :      */
    3283         230 :     prevdef->is_not_null |= newdef->is_not_null;
    3284             : 
    3285             :     /*
    3286             :      * Must have the same collation
    3287             :      */
    3288         230 :     prevcollid = GetColumnDefCollation(NULL, prevdef, prevtypeid);
    3289         230 :     newcollid = GetColumnDefCollation(NULL, newdef, newtypeid);
    3290         230 :     if (prevcollid != newcollid)
    3291           0 :         ereport(ERROR,
    3292             :                 (errcode(ERRCODE_COLLATION_MISMATCH),
    3293             :                  errmsg("inherited column \"%s\" has a collation conflict",
    3294             :                         attributeName),
    3295             :                  errdetail("\"%s\" versus \"%s\"",
    3296             :                            get_collation_name(prevcollid),
    3297             :                            get_collation_name(newcollid))));
    3298             : 
    3299             :     /*
    3300             :      * Copy/check storage parameter
    3301             :      */
    3302         230 :     if (prevdef->storage == 0)
    3303           0 :         prevdef->storage = newdef->storage;
    3304         230 :     else if (prevdef->storage != newdef->storage)
    3305           6 :         ereport(ERROR,
    3306             :                 (errcode(ERRCODE_DATATYPE_MISMATCH),
    3307             :                  errmsg("inherited column \"%s\" has a storage parameter conflict",
    3308             :                         attributeName),
    3309             :                  errdetail("%s versus %s",
    3310             :                            storage_name(prevdef->storage),
    3311             :                            storage_name(newdef->storage))));
    3312             : 
    3313             :     /*
    3314             :      * Copy/check compression parameter
    3315             :      */
    3316         224 :     if (prevdef->compression == NULL)
    3317         212 :         prevdef->compression = newdef->compression;
    3318          12 :     else if (newdef->compression != NULL)
    3319             :     {
    3320           6 :         if (strcmp(prevdef->compression, newdef->compression) != 0)
    3321           6 :             ereport(ERROR,
    3322             :                     (errcode(ERRCODE_DATATYPE_MISMATCH),
    3323             :                      errmsg("column \"%s\" has a compression method conflict",
    3324             :                             attributeName),
    3325             :                      errdetail("%s versus %s",
    3326             :                                prevdef->compression, newdef->compression)));
    3327             :     }
    3328             : 
    3329             :     /*
    3330             :      * Check for GENERATED conflicts
    3331             :      */
    3332         218 :     if (prevdef->generated != newdef->generated)
    3333          12 :         ereport(ERROR,
    3334             :                 (errcode(ERRCODE_DATATYPE_MISMATCH),
    3335             :                  errmsg("inherited column \"%s\" has a generation conflict",
    3336             :                         attributeName)));
    3337             : 
    3338             :     /*
    3339             :      * Default and other constraints are handled by the caller.
    3340             :      */
    3341             : 
    3342         206 :     prevdef->inhcount++;
    3343         206 :     if (prevdef->inhcount < 0)
    3344           0 :         ereport(ERROR,
    3345             :                 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
    3346             :                 errmsg("too many inheritance parents"));
    3347             : 
    3348         206 :     return prevdef;
    3349             : }
    3350             : 
    3351             : /*
    3352             :  * StoreCatalogInheritance
    3353             :  *      Updates the system catalogs with proper inheritance information.
    3354             :  *
    3355             :  * supers is a list of the OIDs of the new relation's direct ancestors.
    3356             :  */
    3357             : static void
    3358       51850 : StoreCatalogInheritance(Oid relationId, List *supers,
    3359             :                         bool child_is_partition)
    3360             : {
    3361             :     Relation    relation;
    3362             :     int32       seqNumber;
    3363             :     ListCell   *entry;
    3364             : 
    3365             :     /*
    3366             :      * sanity checks
    3367             :      */
    3368             :     Assert(OidIsValid(relationId));
    3369             : 
    3370       51850 :     if (supers == NIL)
    3371       42414 :         return;
    3372             : 
    3373             :     /*
    3374             :      * Store INHERITS information in pg_inherits using direct ancestors only.
    3375             :      * Also enter dependencies on the direct ancestors, and make sure they are
    3376             :      * marked with relhassubclass = true.
    3377             :      *
    3378             :      * (Once upon a time, both direct and indirect ancestors were found here
    3379             :      * and then entered into pg_ipl.  Since that catalog doesn't exist
    3380             :      * anymore, there's no need to look for indirect ancestors.)
    3381             :      */
    3382        9436 :     relation = table_open(InheritsRelationId, RowExclusiveLock);
    3383             : 
    3384        9436 :     seqNumber = 1;
    3385       19066 :     foreach(entry, supers)
    3386             :     {
    3387        9630 :         Oid         parentOid = lfirst_oid(entry);
    3388             : 
    3389        9630 :         StoreCatalogInheritance1(relationId, parentOid, seqNumber, relation,
    3390             :                                  child_is_partition);
    3391        9630 :         seqNumber++;
    3392             :     }
    3393             : 
    3394        9436 :     table_close(relation, RowExclusiveLock);
    3395             : }
    3396             : 
    3397             : /*
    3398             :  * Make catalog entries showing relationId as being an inheritance child
    3399             :  * of parentOid.  inhRelation is the already-opened pg_inherits catalog.
    3400             :  */
    3401             : static void
    3402       12320 : StoreCatalogInheritance1(Oid relationId, Oid parentOid,
    3403             :                          int32 seqNumber, Relation inhRelation,
    3404             :                          bool child_is_partition)
    3405             : {
    3406             :     ObjectAddress childobject,
    3407             :                 parentobject;
    3408             : 
    3409             :     /* store the pg_inherits row */
    3410       12320 :     StoreSingleInheritance(relationId, parentOid, seqNumber);
    3411             : 
    3412             :     /*
    3413             :      * Store a dependency too
    3414             :      */
    3415       12320 :     parentobject.classId = RelationRelationId;
    3416       12320 :     parentobject.objectId = parentOid;
    3417       12320 :     parentobject.objectSubId = 0;
    3418       12320 :     childobject.classId = RelationRelationId;
    3419       12320 :     childobject.objectId = relationId;
    3420       12320 :     childobject.objectSubId = 0;
    3421             : 
    3422       12320 :     recordDependencyOn(&childobject, &parentobject,
    3423             :                        child_dependency_type(child_is_partition));
    3424             : 
    3425             :     /*
    3426             :      * Post creation hook of this inheritance. Since object_access_hook
    3427             :      * doesn't take multiple object identifiers, we relay oid of parent
    3428             :      * relation using auxiliary_id argument.
    3429             :      */
    3430       12320 :     InvokeObjectPostAlterHookArg(InheritsRelationId,
    3431             :                                  relationId, 0,
    3432             :                                  parentOid, false);
    3433             : 
    3434             :     /*
    3435             :      * Mark the parent as having subclasses.
    3436             :      */
    3437       12320 :     SetRelationHasSubclass(parentOid, true);
    3438       12320 : }
    3439             : 
    3440             : /*
    3441             :  * Look for an existing column entry with the given name.
    3442             :  *
    3443             :  * Returns the index (starting with 1) if attribute already exists in columns,
    3444             :  * 0 if it doesn't.
    3445             :  */
    3446             : static int
    3447       22066 : findAttrByName(const char *attributeName, const List *columns)
    3448             : {
    3449             :     ListCell   *lc;
    3450       22066 :     int         i = 1;
    3451             : 
    3452       41200 :     foreach(lc, columns)
    3453             :     {
    3454       19608 :         if (strcmp(attributeName, lfirst_node(ColumnDef, lc)->colname) == 0)
    3455         474 :             return i;
    3456             : 
    3457       19134 :         i++;
    3458             :     }
    3459       21592 :     return 0;
    3460             : }
    3461             : 
    3462             : 
    3463             : /*
    3464             :  * SetRelationHasSubclass
    3465             :  *      Set the value of the relation's relhassubclass field in pg_class.
    3466             :  *
    3467             :  * It's always safe to set this field to true, because all SQL commands are
    3468             :  * ready to see true and then find no children.  On the other hand, commands
    3469             :  * generally assume zero children if this is false.
    3470             :  *
    3471             :  * Caller must hold any self-exclusive lock until end of transaction.  If the
    3472             :  * new value is false, caller must have acquired that lock before reading the
    3473             :  * evidence that justified the false value.  That way, it properly waits if
    3474             :  * another backend is simultaneously concluding no need to change the tuple
    3475             :  * (new and old values are true).
    3476             :  *
    3477             :  * NOTE: an important side-effect of this operation is that an SI invalidation
    3478             :  * message is sent out to all backends --- including me --- causing plans
    3479             :  * referencing the relation to be rebuilt with the new list of children.
    3480             :  * This must happen even if we find that no change is needed in the pg_class
    3481             :  * row.
    3482             :  */
    3483             : void
    3484       15680 : SetRelationHasSubclass(Oid relationId, bool relhassubclass)
    3485             : {
    3486             :     Relation    relationRelation;
    3487             :     HeapTuple   tuple;
    3488             :     Form_pg_class classtuple;
    3489             : 
    3490             :     Assert(CheckRelationOidLockedByMe(relationId,
    3491             :                                       ShareUpdateExclusiveLock, false) ||
    3492             :            CheckRelationOidLockedByMe(relationId,
    3493             :                                       ShareRowExclusiveLock, true));
    3494             : 
    3495             :     /*
    3496             :      * Fetch a modifiable copy of the tuple, modify it, update pg_class.
    3497             :      */
    3498       15680 :     relationRelation = table_open(RelationRelationId, RowExclusiveLock);
    3499       15680 :     tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relationId));
    3500       15680 :     if (!HeapTupleIsValid(tuple))
    3501           0 :         elog(ERROR, "cache lookup failed for relation %u", relationId);
    3502       15680 :     classtuple = (Form_pg_class) GETSTRUCT(tuple);
    3503             : 
    3504       15680 :     if (classtuple->relhassubclass != relhassubclass)
    3505             :     {
    3506        7260 :         classtuple->relhassubclass = relhassubclass;
    3507        7260 :         CatalogTupleUpdate(relationRelation, &tuple->t_self, tuple);
    3508             :     }
    3509             :     else
    3510             :     {
    3511             :         /* no need to change tuple, but force relcache rebuild anyway */
    3512        8420 :         CacheInvalidateRelcacheByTuple(tuple);
    3513             :     }
    3514             : 
    3515       15680 :     heap_freetuple(tuple);
    3516       15680 :     table_close(relationRelation, RowExclusiveLock);
    3517       15680 : }
    3518             : 
    3519             : /*
    3520             :  * CheckRelationTableSpaceMove
    3521             :  *      Check if relation can be moved to new tablespace.
    3522             :  *
    3523             :  * NOTE: The caller must hold AccessExclusiveLock on the relation.
    3524             :  *
    3525             :  * Returns true if the relation can be moved to the new tablespace; raises
    3526             :  * an error if it is not possible to do the move; returns false if the move
    3527             :  * would have no effect.
    3528             :  */
    3529             : bool
    3530         226 : CheckRelationTableSpaceMove(Relation rel, Oid newTableSpaceId)
    3531             : {
    3532             :     Oid         oldTableSpaceId;
    3533             : 
    3534             :     /*
    3535             :      * No work if no change in tablespace.  Note that MyDatabaseTableSpace is
    3536             :      * stored as 0.
    3537             :      */
    3538         226 :     oldTableSpaceId = rel->rd_rel->reltablespace;
    3539         226 :     if (newTableSpaceId == oldTableSpaceId ||
    3540         218 :         (newTableSpaceId == MyDatabaseTableSpace && oldTableSpaceId == 0))
    3541          10 :         return false;
    3542             : 
    3543             :     /*
    3544             :      * We cannot support moving mapped relations into different tablespaces.
    3545             :      * (In particular this eliminates all shared catalogs.)
    3546             :      */
    3547         216 :     if (RelationIsMapped(rel))
    3548           0 :         ereport(ERROR,
    3549             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    3550             :                  errmsg("cannot move system relation \"%s\"",
    3551             :                         RelationGetRelationName(rel))));
    3552             : 
    3553             :     /* Cannot move a non-shared relation into pg_global */
    3554         216 :     if (newTableSpaceId == GLOBALTABLESPACE_OID)
    3555          12 :         ereport(ERROR,
    3556             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    3557             :                  errmsg("only shared relations can be placed in pg_global tablespace")));
    3558             : 
    3559             :     /*
    3560             :      * Do not allow moving temp tables of other backends ... their local
    3561             :      * buffer manager is not going to cope.
    3562             :      */
    3563         204 :     if (RELATION_IS_OTHER_TEMP(rel))
    3564           0 :         ereport(ERROR,
    3565             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    3566             :                  errmsg("cannot move temporary tables of other sessions")));
    3567             : 
    3568         204 :     return true;
    3569             : }
    3570             : 
    3571             : /*
    3572             :  * SetRelationTableSpace
    3573             :  *      Set new reltablespace and relfilenumber in pg_class entry.
    3574             :  *
    3575             :  * newTableSpaceId is the new tablespace for the relation, and
    3576             :  * newRelFilenumber its new filenumber.  If newRelFilenumber is
    3577             :  * InvalidRelFileNumber, this field is not updated.
    3578             :  *
    3579             :  * NOTE: The caller must hold AccessExclusiveLock on the relation.
    3580             :  *
    3581             :  * The caller of this routine had better check if a relation can be
    3582             :  * moved to this new tablespace by calling CheckRelationTableSpaceMove()
    3583             :  * first, and is responsible for making the change visible with
    3584             :  * CommandCounterIncrement().
    3585             :  */
    3586             : void
    3587         204 : SetRelationTableSpace(Relation rel,
    3588             :                       Oid newTableSpaceId,
    3589             :                       RelFileNumber newRelFilenumber)
    3590             : {
    3591             :     Relation    pg_class;
    3592             :     HeapTuple   tuple;
    3593             :     Form_pg_class rd_rel;
    3594         204 :     Oid         reloid = RelationGetRelid(rel);
    3595             : 
    3596             :     Assert(CheckRelationTableSpaceMove(rel, newTableSpaceId));
    3597             : 
    3598             :     /* Get a modifiable copy of the relation's pg_class row. */
    3599         204 :     pg_class = table_open(RelationRelationId, RowExclusiveLock);
    3600             : 
    3601         204 :     tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(reloid));
    3602         204 :     if (!HeapTupleIsValid(tuple))
    3603           0 :         elog(ERROR, "cache lookup failed for relation %u", reloid);
    3604         204 :     rd_rel = (Form_pg_class) GETSTRUCT(tuple);
    3605             : 
    3606             :     /* Update the pg_class row. */
    3607         408 :     rd_rel->reltablespace = (newTableSpaceId == MyDatabaseTableSpace) ?
    3608         204 :         InvalidOid : newTableSpaceId;
    3609         204 :     if (RelFileNumberIsValid(newRelFilenumber))
    3610         160 :         rd_rel->relfilenode = newRelFilenumber;
    3611         204 :     CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
    3612             : 
    3613             :     /*
    3614             :      * Record dependency on tablespace.  This is only required for relations
    3615             :      * that have no physical storage.
    3616             :      */
    3617         204 :     if (!RELKIND_HAS_STORAGE(rel->rd_rel->relkind))
    3618          30 :         changeDependencyOnTablespace(RelationRelationId, reloid,
    3619             :                                      rd_rel->reltablespace);
    3620             : 
    3621         204 :     heap_freetuple(tuple);
    3622         204 :     table_close(pg_class, RowExclusiveLock);
    3623         204 : }
    3624             : 
    3625             : /*
    3626             :  *      renameatt_check         - basic sanity checks before attribute rename
    3627             :  */
    3628             : static void
    3629         968 : renameatt_check(Oid myrelid, Form_pg_class classform, bool recursing)
    3630             : {
    3631         968 :     char        relkind = classform->relkind;
    3632             : 
    3633         968 :     if (classform->reloftype && !recursing)
    3634           6 :         ereport(ERROR,
    3635             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    3636             :                  errmsg("cannot rename column of typed table")));
    3637             : 
    3638             :     /*
    3639             :      * Renaming the columns of sequences or toast tables doesn't actually
    3640             :      * break anything from the system's point of view, since internal
    3641             :      * references are by attnum.  But it doesn't seem right to allow users to
    3642             :      * change names that are hardcoded into the system, hence the following
    3643             :      * restriction.
    3644             :      */
    3645         962 :     if (relkind != RELKIND_RELATION &&
    3646          86 :         relkind != RELKIND_VIEW &&
    3647          86 :         relkind != RELKIND_MATVIEW &&
    3648          38 :         relkind != RELKIND_COMPOSITE_TYPE &&
    3649          38 :         relkind != RELKIND_INDEX &&
    3650          38 :         relkind != RELKIND_PARTITIONED_INDEX &&
    3651           0 :         relkind != RELKIND_FOREIGN_TABLE &&
    3652             :         relkind != RELKIND_PARTITIONED_TABLE)
    3653           0 :         ereport(ERROR,
    3654             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    3655             :                  errmsg("cannot rename columns of relation \"%s\"",
    3656             :                         NameStr(classform->relname)),
    3657             :                  errdetail_relkind_not_supported(relkind)));
    3658             : 
    3659             :     /*
    3660             :      * permissions checking.  only the owner of a class can change its schema.
    3661             :      */
    3662         962 :     if (!object_ownercheck(RelationRelationId, myrelid, GetUserId()))
    3663           0 :         aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(myrelid)),
    3664           0 :                        NameStr(classform->relname));
    3665         962 :     if (!allowSystemTableMods && IsSystemClass(myrelid, classform))
    3666           2 :         ereport(ERROR,
    3667             :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
    3668             :                  errmsg("permission denied: \"%s\" is a system catalog",
    3669             :                         NameStr(classform->relname))));
    3670         960 : }
    3671             : 
    3672             : /*
    3673             :  *      renameatt_internal      - workhorse for renameatt
    3674             :  *
    3675             :  * Return value is the attribute number in the 'myrelid' relation.
    3676             :  */
    3677             : static AttrNumber
    3678         534 : renameatt_internal(Oid myrelid,
    3679             :                    const char *oldattname,
    3680             :                    const char *newattname,
    3681             :                    bool recurse,
    3682             :                    bool recursing,
    3683             :                    int expected_parents,
    3684             :                    DropBehavior behavior)
    3685             : {
    3686             :     Relation    targetrelation;
    3687             :     Relation    attrelation;
    3688             :     HeapTuple   atttup;
    3689             :     Form_pg_attribute attform;
    3690             :     AttrNumber  attnum;
    3691             : 
    3692             :     /*
    3693             :      * Grab an exclusive lock on the target table, which we will NOT release
    3694             :      * until end of transaction.
    3695             :      */
    3696         534 :     targetrelation = relation_open(myrelid, AccessExclusiveLock);
    3697         534 :     renameatt_check(myrelid, RelationGetForm(targetrelation), recursing);
    3698             : 
    3699             :     /*
    3700             :      * if the 'recurse' flag is set then we are supposed to rename this
    3701             :      * attribute in all classes that inherit from 'relname' (as well as in
    3702             :      * 'relname').
    3703             :      *
    3704             :      * any permissions or problems with duplicate attributes will cause the
    3705             :      * whole transaction to abort, which is what we want -- all or nothing.
    3706             :      */
    3707         534 :     if (recurse)
    3708             :     {
    3709             :         List       *child_oids,
    3710             :                    *child_numparents;
    3711             :         ListCell   *lo,
    3712             :                    *li;
    3713             : 
    3714             :         /*
    3715             :          * we need the number of parents for each child so that the recursive
    3716             :          * calls to renameatt() can determine whether there are any parents
    3717             :          * outside the inheritance hierarchy being processed.
    3718             :          */
    3719         230 :         child_oids = find_all_inheritors(myrelid, AccessExclusiveLock,
    3720             :                                          &child_numparents);
    3721             : 
    3722             :         /*
    3723             :          * find_all_inheritors does the recursive search of the inheritance
    3724             :          * hierarchy, so all we have to do is process all of the relids in the
    3725             :          * list that it returns.
    3726             :          */
    3727         698 :         forboth(lo, child_oids, li, child_numparents)
    3728             :         {
    3729         498 :             Oid         childrelid = lfirst_oid(lo);
    3730         498 :             int         numparents = lfirst_int(li);
    3731             : 
    3732         498 :             if (childrelid == myrelid)
    3733         230 :                 continue;
    3734             :             /* note we need not recurse again */
    3735         268 :             renameatt_internal(childrelid, oldattname, newattname, false, true, numparents, behavior);
    3736             :         }
    3737             :     }
    3738             :     else
    3739             :     {
    3740             :         /*
    3741             :          * If we are told not to recurse, there had better not be any child
    3742             :          * tables; else the rename would put them out of step.
    3743             :          *
    3744             :          * expected_parents will only be 0 if we are not already recursing.
    3745             :          */
    3746         340 :         if (expected_parents == 0 &&
    3747          36 :             find_inheritance_children(myrelid, NoLock) != NIL)
    3748          12 :             ereport(ERROR,
    3749             :                     (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    3750             :                      errmsg("inherited column \"%s\" must be renamed in child tables too",
    3751             :                             oldattname)));
    3752             :     }
    3753             : 
    3754             :     /* rename attributes in typed tables of composite type */
    3755         492 :     if (targetrelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
    3756             :     {
    3757             :         List       *child_oids;
    3758             :         ListCell   *lo;
    3759             : 
    3760          24 :         child_oids = find_typed_table_dependencies(targetrelation->rd_rel->reltype,
    3761          24 :                                                    RelationGetRelationName(targetrelation),
    3762             :                                                    behavior);
    3763             : 
    3764          24 :         foreach(lo, child_oids)
    3765           6 :             renameatt_internal(lfirst_oid(lo), oldattname, newattname, true, true, 0, behavior);
    3766             :     }
    3767             : 
    3768         486 :     attrelation = table_open(AttributeRelationId, RowExclusiveLock);
    3769             : 
    3770         486 :     atttup = SearchSysCacheCopyAttName(myrelid, oldattname);
    3771         486 :     if (!HeapTupleIsValid(atttup))
    3772          24 :         ereport(ERROR,
    3773             :                 (errcode(ERRCODE_UNDEFINED_COLUMN),
    3774             :                  errmsg("column \"%s\" does not exist",
    3775             :                         oldattname)));
    3776         462 :     attform = (Form_pg_attribute) GETSTRUCT(atttup);
    3777             : 
    3778         462 :     attnum = attform->attnum;
    3779         462 :     if (attnum <= 0)
    3780           0 :         ereport(ERROR,
    3781             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    3782             :                  errmsg("cannot rename system column \"%s\"",
    3783             :                         oldattname)));
    3784             : 
    3785             :     /*
    3786             :      * if the attribute is inherited, forbid the renaming.  if this is a
    3787             :      * top-level call to renameatt(), then expected_parents will be 0, so the
    3788             :      * effect of this code will be to prohibit the renaming if the attribute
    3789             :      * is inherited at all.  if this is a recursive call to renameatt(),
    3790             :      * expected_parents will be the number of parents the current relation has
    3791             :      * within the inheritance hierarchy being processed, so we'll prohibit the
    3792             :      * renaming only if there are additional parents from elsewhere.
    3793             :      */
    3794         462 :     if (attform->attinhcount > expected_parents)
    3795          30 :         ereport(ERROR,
    3796             :                 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    3797             :                  errmsg("cannot rename inherited column \"%s\"",
    3798             :                         oldattname)));
    3799             : 
    3800             :     /* new name should not already exist */
    3801         432 :     (void) check_for_column_name_collision(targetrelation, newattname, false);
    3802             : 
    3803             :     /* apply the update */
    3804         420 :     namestrcpy(&(attform->attname), newattname);
    3805             : 
    3806         420 :     CatalogTupleUpdate(attrelation, &atttup->t_self, atttup);
    3807             : 
    3808         420 :     InvokeObjectPostAlterHook(RelationRelationId, myrelid, attnum);
    3809             : 
    3810         420 :     heap_freetuple(atttup);
    3811             : 
    3812         420 :     table_close(attrelation, RowExclusiveLock);
    3813             : 
    3814         420 :     relation_close(targetrelation, NoLock); /* close rel but keep lock */
    3815             : 
    3816         420 :     return attnum;
    3817             : }
    3818             : 
    3819             : /*
    3820             :  * Perform permissions and integrity checks before acquiring a relation lock.
    3821             :  */
    3822             : static void
    3823         392 : RangeVarCallbackForRenameAttribute(const RangeVar *rv, Oid relid, Oid oldrelid,
    3824             :                                    void *arg)
    3825             : {
    3826             :     HeapTuple   tuple;
    3827             :     Form_pg_class form;
    3828             : 
    3829         392 :     tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
    3830         392 :     if (!HeapTupleIsValid(tuple))
    3831          36 :         return;                 /* concurrently dropped */
    3832         356 :     form = (Form_pg_class) GETSTRUCT(tuple);
    3833         356 :     renameatt_check(relid, form, false);
    3834         348 :     ReleaseSysCache(tuple);
    3835             : }
    3836             : 
    3837             : /*
    3838             :  *      renameatt       - changes the name of an attribute in a relation
    3839             :  *
    3840             :  * The returned ObjectAddress is that of the renamed column.
    3841             :  */
    3842             : ObjectAddress
    3843         298 : renameatt(RenameStmt *stmt)
    3844             : {
    3845             :     Oid         relid;
    3846             :     AttrNumber  attnum;
    3847             :     ObjectAddress address;
    3848             : 
    3849             :     /* lock level taken here should match renameatt_internal */
    3850         298 :     relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
    3851         298 :                                      stmt->missing_ok ? RVR_MISSING_OK : 0,
    3852             :                                      RangeVarCallbackForRenameAttribute,
    3853             :                                      NULL);
    3854             : 
    3855         284 :     if (!OidIsValid(relid))
    3856             :     {
    3857          24 :         ereport(NOTICE,
    3858             :                 (errmsg("relation \"%s\" does not exist, skipping",
    3859             :                         stmt->relation->relname)));
    3860          24 :         return InvalidObjectAddress;
    3861             :     }
    3862             : 
    3863             :     attnum =
    3864         260 :         renameatt_internal(relid,
    3865         260 :                            stmt->subname,    /* old att name */
    3866         260 :                            stmt->newname,    /* new att name */
    3867         260 :                            stmt->relation->inh, /* recursive? */
    3868             :                            false,   /* recursing? */
    3869             :                            0,   /* expected inhcount */
    3870             :                            stmt->behavior);
    3871             : 
    3872         176 :     ObjectAddressSubSet(address, RelationRelationId, relid, attnum);
    3873             : 
    3874         176 :     return address;
    3875             : }
    3876             : 
    3877             : /*
    3878             :  * same logic as renameatt_internal
    3879             :  */
    3880             : static ObjectAddress
    3881          84 : rename_constraint_internal(Oid myrelid,
    3882             :                            Oid mytypid,
    3883             :                            const char *oldconname,
    3884             :                            const char *newconname,
    3885             :                            bool recurse,
    3886             :                            bool recursing,
    3887             :                            int expected_parents)
    3888             : {
    3889          84 :     Relation    targetrelation = NULL;
    3890             :     Oid         constraintOid;
    3891             :     HeapTuple   tuple;
    3892             :     Form_pg_constraint con;
    3893             :     ObjectAddress address;
    3894             : 
    3895             :     Assert(!myrelid || !mytypid);
    3896             : 
    3897          84 :     if (mytypid)
    3898             :     {
    3899           6 :         constraintOid = get_domain_constraint_oid(mytypid, oldconname, false);
    3900             :     }
    3901             :     else
    3902             :     {
    3903          78 :         targetrelation = relation_open(myrelid, AccessExclusiveLock);
    3904             : 
    3905             :         /*
    3906             :          * don't tell it whether we're recursing; we allow changing typed
    3907             :          * tables here
    3908             :          */
    3909          78 :         renameatt_check(myrelid, RelationGetForm(targetrelation), false);
    3910             : 
    3911          78 :         constraintOid = get_relation_constraint_oid(myrelid, oldconname, false);
    3912             :     }
    3913             : 
    3914          84 :     tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constraintOid));
    3915          84 :     if (!HeapTupleIsValid(tuple))
    3916           0 :         elog(ERROR, "cache lookup failed for constraint %u",
    3917             :              constraintOid);
    3918          84 :     con = (Form_pg_constraint) GETSTRUCT(tuple);
    3919             : 
    3920          84 :     if (myrelid && con->contype == CONSTRAINT_CHECK && !con->connoinherit)
    3921             :     {
    3922          48 :         if (recurse)
    3923             :         {
    3924             :             List       *child_oids,
    3925             :                        *child_numparents;
    3926             :             ListCell   *lo,
    3927             :                        *li;
    3928             : 
    3929          30 :             child_oids = find_all_inheritors(myrelid, AccessExclusiveLock,
    3930             :                                              &child_numparents);
    3931             : 
    3932          72 :             forboth(lo, child_oids, li, child_numparents)
    3933             :             {
    3934          42 :                 Oid         childrelid = lfirst_oid(lo);
    3935          42 :                 int         numparents = lfirst_int(li);
    3936             : 
    3937          42 :                 if (childrelid == myrelid)
    3938          30 :                     continue;
    3939             : 
    3940          12 :                 rename_constraint_internal(childrelid, InvalidOid, oldconname, newconname, false, true, numparents);
    3941             :             }
    3942             :         }
    3943             :         else
    3944             :         {
    3945          24 :             if (expected_parents == 0 &&
    3946           6 :                 find_inheritance_children(myrelid, NoLock) != NIL)
    3947           6 :                 ereport(ERROR,
    3948             :                         (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    3949             :                          errmsg("inherited constraint \"%s\" must be renamed in child tables too",
    3950             :                                 oldconname)));
    3951             :         }
    3952             : 
    3953          42 :         if (con->coninhcount > expected_parents)
    3954           6 :             ereport(ERROR,
    3955             :                     (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    3956             :                      errmsg("cannot rename inherited constraint \"%s\"",
    3957             :                             oldconname)));
    3958             :     }
    3959             : 
    3960          72 :     if (con->conindid
    3961          18 :         && (con->contype == CONSTRAINT_PRIMARY
    3962           6 :             || con->contype == CONSTRAINT_UNIQUE
    3963           0 :             || con->contype == CONSTRAINT_EXCLUSION))
    3964             :         /* rename the index; this renames the constraint as well */
    3965          18 :         RenameRelationInternal(con->conindid, newconname, false, true);
    3966             :     else
    3967          54 :         RenameConstraintById(constraintOid, newconname);
    3968             : 
    3969          72 :     ObjectAddressSet(address, ConstraintRelationId, constraintOid);
    3970             : 
    3971          72 :     ReleaseSysCache(tuple);
    3972             : 
    3973          72 :     if (targetrelation)
    3974             :     {
    3975             :         /*
    3976             :          * Invalidate relcache so as others can see the new constraint name.
    3977             :          */
    3978          66 :         CacheInvalidateRelcache(targetrelation);
    3979             : 
    3980          66 :         relation_close(targetrelation, NoLock); /* close rel but keep lock */
    3981             :     }
    3982             : 
    3983          72 :     return address;
    3984             : }
    3985             : 
    3986             : ObjectAddress
    3987          78 : RenameConstraint(RenameStmt *stmt)
    3988             : {
    3989          78 :     Oid         relid = InvalidOid;
    3990          78 :     Oid         typid = InvalidOid;
    3991             : 
    3992          78 :     if (stmt->renameType == OBJECT_DOMCONSTRAINT)
    3993             :     {
    3994             :         Relation    rel;
    3995             :         HeapTuple   tup;
    3996             : 
    3997           6 :         typid = typenameTypeId(NULL, makeTypeNameFromNameList(castNode(List, stmt->object)));
    3998           6 :         rel = table_open(TypeRelationId, RowExclusiveLock);
    3999           6 :         tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
    4000           6 :         if (!HeapTupleIsValid(tup))
    4001           0 :             elog(ERROR, "cache lookup failed for type %u", typid);
    4002           6 :         checkDomainOwner(tup);
    4003           6 :         ReleaseSysCache(tup);
    4004           6 :         table_close(rel, NoLock);
    4005             :     }
    4006             :     else
    4007             :     {
    4008             :         /* lock level taken here should match rename_constraint_internal */
    4009          72 :         relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
    4010          72 :                                          stmt->missing_ok ? RVR_MISSING_OK : 0,
    4011             :                                          RangeVarCallbackForRenameAttribute,
    4012             :                                          NULL);
    4013          72 :         if (!OidIsValid(relid))
    4014             :         {
    4015           6 :             ereport(NOTICE,
    4016             :                     (errmsg("relation \"%s\" does not exist, skipping",
    4017             :                             stmt->relation->relname)));
    4018           6 :             return InvalidObjectAddress;
    4019             :         }
    4020             :     }
    4021             : 
    4022             :     return
    4023          72 :         rename_constraint_internal(relid, typid,
    4024          72 :                                    stmt->subname,
    4025          72 :                                    stmt->newname,
    4026         138 :                                    (stmt->relation &&
    4027          66 :                                     stmt->relation->inh), /* recursive? */
    4028             :                                    false,   /* recursing? */
    4029             :                                    0 /* expected inhcount */ );
    4030             : }
    4031             : 
    4032             : /*
    4033             :  * Execute ALTER TABLE/INDEX/SEQUENCE/VIEW/MATERIALIZED VIEW/FOREIGN TABLE
    4034             :  * RENAME
    4035             :  */
    4036             : ObjectAddress
    4037         510 : RenameRelation(RenameStmt *stmt)
    4038             : {
    4039         510 :     bool        is_index_stmt = stmt->renameType == OBJECT_INDEX;
    4040             :     Oid         relid;
    4041             :     ObjectAddress address;
    4042             : 
    4043             :     /*
    4044             :      * Grab an exclusive lock on the target table, index, sequence, view,
    4045             :      * materialized view, or foreign table, which we will NOT release until
    4046             :      * end of transaction.
    4047             :      *
    4048             :      * Lock level used here should match RenameRelationInternal, to avoid lock
    4049             :      * escalation.  However, because ALTER INDEX can be used with any relation
    4050             :      * type, we mustn't believe without verification.
    4051             :      */
    4052             :     for (;;)
    4053          12 :     {
    4054             :         LOCKMODE    lockmode;
    4055             :         char        relkind;
    4056             :         bool        obj_is_index;
    4057             : 
    4058         522 :         lockmode = is_index_stmt ? ShareUpdateExclusiveLock : AccessExclusiveLock;
    4059             : 
    4060         522 :         relid = RangeVarGetRelidExtended(stmt->relation, lockmode,
    4061         522 :                                          stmt->missing_ok ? RVR_MISSING_OK : 0,
    4062             :                                          RangeVarCallbackForAlterRelation,
    4063             :                                          (void *) stmt);
    4064             : 
    4065         472 :         if (!OidIsValid(relid))
    4066             :         {
    4067          18 :             ereport(NOTICE,
    4068             :                     (errmsg("relation \"%s\" does not exist, skipping",
    4069             :                             stmt->relation->relname)));
    4070          18 :             return InvalidObjectAddress;
    4071             :         }
    4072             : 
    4073             :         /*
    4074             :          * We allow mismatched statement and object types (e.g., ALTER INDEX
    4075             :          * to rename a table), but we might've used the wrong lock level.  If
    4076             :          * that happens, retry with the correct lock level.  We don't bother
    4077             :          * if we already acquired AccessExclusiveLock with an index, however.
    4078             :          */
    4079         454 :         relkind = get_rel_relkind(relid);
    4080         454 :         obj_is_index = (relkind == RELKIND_INDEX ||
    4081             :                         relkind == RELKIND_PARTITIONED_INDEX);
    4082         454 :         if (obj_is_index || is_index_stmt == obj_is_index)
    4083             :             break;
    4084             : 
    4085          12 :         UnlockRelationOid(relid, lockmode);
    4086          12 :         is_index_stmt = obj_is_index;
    4087             :     }
    4088             : 
    4089             :     /* Do the work */
    4090         442 :     RenameRelationInternal(relid, stmt->newname, false, is_index_stmt);
    4091             : 
    4092         430 :     ObjectAddressSet(address, RelationRelationId, relid);
    4093             : 
    4094         430 :     return address;
    4095             : }
    4096             : 
    4097             : /*
    4098             :  *      RenameRelationInternal - change the name of a relation
    4099             :  */
    4100             : void
    4101        1616 : RenameRelationInternal(Oid myrelid, const char *newrelname, bool is_internal, bool is_index)
    4102             : {
    4103             :     Relation    targetrelation;
    4104             :     Relation    relrelation;    /* for RELATION relation */
    4105             :     HeapTuple   reltup;
    4106             :     Form_pg_class relform;
    4107             :     Oid         namespaceId;
    4108             : 
    4109             :     /*
    4110             :      * Grab a lock on the target relation, which we will NOT release until end
    4111             :      * of transaction.  We need at least a self-exclusive lock so that
    4112             :      * concurrent DDL doesn't overwrite the rename if they start updating
    4113             :      * while still seeing the old version.  The lock also guards against
    4114             :      * triggering relcache reloads in concurrent sessions, which might not
    4115             :      * handle this information changing under them.  For indexes, we can use a
    4116             :      * reduced lock level because RelationReloadIndexInfo() handles indexes
    4117             :      * specially.
    4118             :      */
    4119        1616 :     targetrelation = relation_open(myrelid, is_index ? ShareUpdateExclusiveLock : AccessExclusiveLock);
    4120        1616 :     namespaceId = RelationGetNamespace(targetrelation);
    4121             : 
    4122             :     /*
    4123             :      * Find relation's pg_class tuple, and make sure newrelname isn't in use.
    4124             :      */
    4125        1616 :     relrelation = table_open(RelationRelationId, RowExclusiveLock);
    4126             : 
    4127        1616 :     reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(myrelid));
    4128        1616 :     if (!HeapTupleIsValid(reltup))  /* shouldn't happen */
    4129           0 :         elog(ERROR, "cache lookup failed for relation %u", myrelid);
    4130        1616 :     relform = (Form_pg_class) GETSTRUCT(reltup);
    4131             : 
    4132        1616 :     if (get_relname_relid(newrelname, namespaceId) != InvalidOid)
    4133          12 :         ereport(ERROR,
    4134             :                 (errcode(ERRCODE_DUPLICATE_TABLE),
    4135             :                  errmsg("relation \"%s\" already exists",
    4136             :                         newrelname)));
    4137             : 
    4138             :     /*
    4139             :      * RenameRelation is careful not to believe the caller's idea of the
    4140             :      * relation kind being handled.  We don't have to worry about this, but
    4141             :      * let's not be totally oblivious to it.  We can process an index as
    4142             :      * not-an-index, but not the other way around.
    4143             :      */
    4144             :     Assert(!is_index ||
    4145             :            is_index == (targetrelation->rd_rel->relkind == RELKIND_INDEX ||
    4146             :                         targetrelation->rd_rel->relkind == RELKIND_PARTITIONED_INDEX));
    4147             : 
    4148             :     /*
    4149             :      * Update pg_class tuple with new relname.  (Scribbling on reltup is OK
    4150             :      * because it's a copy...)
    4151             :      */
    4152        1604 :     namestrcpy(&(relform->relname), newrelname);
    4153             : 
    4154        1604 :     CatalogTupleUpdate(relrelation, &reltup->t_self, reltup);
    4155             : 
    4156        1604 :     InvokeObjectPostAlterHookArg(RelationRelationId, myrelid, 0,
    4157             :                                  InvalidOid, is_internal);
    4158             : 
    4159        1604 :     heap_freetuple(reltup);
    4160        1604 :     table_close(relrelation, RowExclusiveLock);
    4161             : 
    4162             :     /*
    4163             :      * Also rename the associated type, if any.
    4164             :      */
    4165        1604 :     if (OidIsValid(targetrelation->rd_rel->reltype))
    4166         166 :         RenameTypeInternal(targetrelation->rd_rel->reltype,
    4167             :                            newrelname, namespaceId);
    4168             : 
    4169             :     /*
    4170             :      * Also rename the associated constraint, if any.
    4171             :      */
    4172        1604 :     if (targetrelation->rd_rel->relkind == RELKIND_INDEX ||
    4173         870 :         targetrelation->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
    4174             :     {
    4175         752 :         Oid         constraintId = get_index_constraint(myrelid);
    4176             : 
    4177         752 :         if (OidIsValid(constraintId))
    4178          36 :             RenameConstraintById(constraintId, newrelname);
    4179             :     }
    4180             : 
    4181             :     /*
    4182             :      * Close rel, but keep lock!
    4183             :      */
    4184        1604 :     relation_close(targetrelation, NoLock);
    4185        1604 : }
    4186             : 
    4187             : /*
    4188             :  *      ResetRelRewrite - reset relrewrite
    4189             :  */
    4190             : void
    4191         552 : ResetRelRewrite(Oid myrelid)
    4192             : {
    4193             :     Relation    relrelation;    /* for RELATION relation */
    4194             :     HeapTuple   reltup;
    4195             :     Form_pg_class relform;
    4196             : 
    4197             :     /*
    4198             :      * Find relation's pg_class tuple.
    4199             :      */
    4200         552 :     relrelation = table_open(RelationRelationId, RowExclusiveLock);
    4201             : 
    4202         552 :     reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(myrelid));
    4203         552 :     if (!HeapTupleIsValid(reltup))  /* shouldn't happen */
    4204           0 :         elog(ERROR, "cache lookup failed for relation %u", myrelid);
    4205         552 :     relform = (Form_pg_class) GETSTRUCT(reltup);
    4206             : 
    4207             :     /*
    4208             :      * Update pg_class tuple.
    4209             :      */
    4210         552 :     relform->relrewrite = InvalidOid;
    4211             : 
    4212         552 :     CatalogTupleUpdate(relrelation, &reltup->t_self, reltup);
    4213             : 
    4214         552 :     heap_freetuple(reltup);
    4215         552 :     table_close(relrelation, RowExclusiveLock);
    4216         552 : }
    4217             : 
    4218             : /*
    4219             :  * Disallow ALTER TABLE (and similar commands) when the current backend has
    4220             :  * any open reference to the target table besides the one just acquired by
    4221             :  * the calling command; this implies there's an open cursor or active plan.
    4222             :  * We need this check because our lock doesn't protect us against stomping
    4223             :  * on our own foot, only other people's feet!
    4224             :  *
    4225             :  * For ALTER TABLE, the only case known to cause serious trouble is ALTER
    4226             :  * COLUMN TYPE, and some changes are obviously pretty benign, so this could
    4227             :  * possibly be relaxed to only error out for certain types of alterations.
    4228             :  * But the use-case for allowing any of these things is not obvious, so we
    4229             :  * won't work hard at it for now.
    4230             :  *
    4231             :  * We also reject these commands if there are any pending AFTER trigger events
    4232             :  * for the rel.  This is certainly necessary for the rewriting variants of
    4233             :  * ALTER TABLE, because they don't preserve tuple TIDs and so the pending
    4234             :  * events would try to fetch the wrong tuples.  It might be overly cautious
    4235             :  * in other cases, but again it seems better to err on the side of paranoia.
    4236             :  *
    4237             :  * REINDEX calls this with "rel" referencing the index to be rebuilt; here
    4238             :  * we are worried about active indexscans on the index.  The trigger-event
    4239             :  * check can be skipped, since we are doing no damage to the parent table.
    4240             :  *
    4241             :  * The statement name (eg, "ALTER TABLE") is passed for use in error messages.
    4242             :  */
    4243             : void
    4244      128504 : CheckTableNotInUse(Relation rel, const char *stmt)
    4245             : {
    4246             :     int         expected_refcnt;
    4247             : 
    4248      128504 :     expected_refcnt = rel->rd_isnailed ? 2 : 1;
    4249      128504 :     if (rel->rd_refcnt != expected_refcnt)
    4250          24 :         ereport(ERROR,
    4251             :                 (errcode(ERRCODE_OBJECT_IN_USE),
    4252             :         /* translator: first %s is a SQL command, eg ALTER TABLE */
    4253             :                  errmsg("cannot %s \"%s\" because it is being used by active queries in this session",
    4254             :                         stmt, RelationGetRelationName(rel))));
    4255             : 
    4256      128480 :     if (rel->rd_rel->relkind != RELKIND_INDEX &&
    4257      199012 :         rel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX &&
    4258       98462 :         AfterTriggerPendingOnRel(RelationGetRelid(rel)))
    4259          18 :         ereport(ERROR,
    4260             :                 (errcode(ERRCODE_OBJECT_IN_USE),
    4261             :         /* translator: first %s is a SQL command, eg ALTER TABLE */
    4262             :                  errmsg("cannot %s \"%s\" because it has pending trigger events",
    4263             :                         stmt, RelationGetRelationName(rel))));
    4264      128462 : }
    4265             : 
    4266             : /*
    4267             :  * CheckAlterTableIsSafe
    4268             :  *      Verify that it's safe to allow ALTER TABLE on this relation.
    4269             :  *
    4270             :  * This consists of CheckTableNotInUse() plus a check that the relation
    4271             :  * isn't another session's temp table.  We must split out the temp-table
    4272             :  * check because there are callers of CheckTableNotInUse() that don't want
    4273             :  * that, notably DROP TABLE.  (We must allow DROP or we couldn't clean out
    4274             :  * an orphaned temp schema.)  Compare truncate_check_activity().
    4275             :  */
    4276             : static void
    4277       30264 : CheckAlterTableIsSafe(Relation rel)
    4278             : {
    4279             :     /*
    4280             :      * Don't allow ALTER on temp tables of other backends.  Their local buffer
    4281             :      * manager is not going to cope if we need to change the table's contents.
    4282             :      * Even if we don't, there may be optimizations that assume temp tables
    4283             :      * aren't subject to such interference.
    4284             :      */
    4285       30264 :     if (RELATION_IS_OTHER_TEMP(rel))
    4286           0 :         ereport(ERROR,
    4287             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    4288             :                  errmsg("cannot alter temporary tables of other sessions")));
    4289             : 
    4290             :     /*
    4291             :      * Also check for active uses of the relation in the current transaction,
    4292             :      * including open scans and pending AFTER trigger events.
    4293             :      */
    4294       30264 :     CheckTableNotInUse(rel, "ALTER TABLE");
    4295       30234 : }
    4296             : 
    4297             : /*
    4298             :  * AlterTableLookupRelation
    4299             :  *      Look up, and lock, the OID for the relation named by an alter table
    4300             :  *      statement.
    4301             :  */
    4302             : Oid
    4303       27252 : AlterTableLookupRelation(AlterTableStmt *stmt, LOCKMODE lockmode)
    4304             : {
    4305       54408 :     return RangeVarGetRelidExtended(stmt->relation, lockmode,
    4306       27252 :                                     stmt->missing_ok ? RVR_MISSING_OK : 0,
    4307             :                                     RangeVarCallbackForAlterRelation,
    4308             :                                     (void *) stmt);
    4309             : }
    4310             : 
    4311             : /*
    4312             :  * AlterTable
    4313             :  *      Execute ALTER TABLE, which can be a list of subcommands
    4314             :  *
    4315             :  * ALTER TABLE is performed in three phases:
    4316             :  *      1. Examine subcommands and perform pre-transformation checking.
    4317             :  *      2. Validate and transform subcommands, and update system catalogs.
    4318             :  *      3. Scan table(s) to check new constraints, and optionally recopy
    4319             :  *         the data into new table(s).
    4320             :  * Phase 3 is not performed unless one or more of the subcommands requires
    4321             :  * it.  The intention of this design is to allow multiple independent
    4322             :  * updates of the table schema to be performed with only one pass over the
    4323             :  * data.
    4324             :  *
    4325             :  * ATPrepCmd performs phase 1.  A "work queue" entry is created for
    4326             :  * each table to be affected (there may be multiple affected tables if the
    4327             :  * commands traverse a table inheritance hierarchy).  Also we do preliminary
    4328             :  * validation of the subcommands.  Because earlier subcommands may change
    4329             :  * the catalog state seen by later commands, there are limits to what can
    4330             :  * be done in this phase.  Generally, this phase acquires table locks,
    4331             :  * checks permissions and relkind, and recurses to find child tables.
    4332             :  *
    4333             :  * ATRewriteCatalogs performs phase 2 for each affected table.
    4334             :  * Certain subcommands need to be performed before others to avoid
    4335             :  * unnecessary conflicts; for example, DROP COLUMN should come before
    4336             :  * ADD COLUMN.  Therefore phase 1 divides the subcommands into multiple
    4337             :  * lists, one for each logical "pass" of phase 2.
    4338             :  *
    4339             :  * ATRewriteTables performs phase 3 for those tables that need it.
    4340             :  *
    4341             :  * For most subcommand types, phases 2 and 3 do no explicit recursion,
    4342             :  * since phase 1 already does it.  However, for certain subcommand types
    4343             :  * it is only possible to determine how to recurse at phase 2 time; for
    4344             :  * those cases, phase 1 sets the cmd->recurse flag.
    4345             :  *
    4346             :  * Thanks to the magic of MVCC, an error anywhere along the way rolls back
    4347             :  * the whole operation; we don't have to do anything special to clean up.
    4348             :  *
    4349             :  * The caller must lock the relation, with an appropriate lock level
    4350             :  * for the subcommands requested, using AlterTableGetLockLevel(stmt->cmds)
    4351             :  * or higher. We pass the lock level down
    4352             :  * so that we can apply it recursively to inherited tables. Note that the
    4353             :  * lock level we want as we recurse might well be higher than required for
    4354             :  * that specific subcommand. So we pass down the overall lock requirement,
    4355             :  * rather than reassess it at lower levels.
    4356             :  *
    4357             :  * The caller also provides a "context" which is to be passed back to
    4358             :  * utility.c when we need to execute a subcommand such as CREATE INDEX.
    4359             :  * Some of the fields therein, such as the relid, are used here as well.
    4360             :  */
    4361             : void
    4362       27018 : AlterTable(AlterTableStmt *stmt, LOCKMODE lockmode,
    4363             :            AlterTableUtilityContext *context)
    4364             : {
    4365             :     Relation    rel;
    4366             : 
    4367             :     /* Caller is required to provide an adequate lock. */
    4368       27018 :     rel = relation_open(context->relid, NoLock);
    4369             : 
    4370       27018 :     CheckAlterTableIsSafe(rel);
    4371             : 
    4372       27000 :     ATController(stmt, rel, stmt->cmds, stmt->relation->inh, lockmode, context);
    4373       24026 : }
    4374             : 
    4375             : /*
    4376             :  * AlterTableInternal
    4377             :  *
    4378             :  * ALTER TABLE with target specified by OID
    4379             :  *
    4380             :  * We do not reject if the relation is already open, because it's quite
    4381             :  * likely that one or more layers of caller have it open.  That means it
    4382             :  * is unsafe to use this entry point for alterations that could break
    4383             :  * existing query plans.  On the assumption it's not used for such, we
    4384             :  * don't have to reject pending AFTER triggers, either.
    4385             :  *
    4386             :  * Also, since we don't have an AlterTableUtilityContext, this cannot be
    4387             :  * used for any subcommand types that require parse transformation or
    4388             :  * could generate subcommands that have to be passed to ProcessUtility.
    4389             :  */
    4390             : void
    4391         278 : AlterTableInternal(Oid relid, List *cmds, bool recurse)
    4392             : {
    4393             :     Relation    rel;
    4394         278 :     LOCKMODE    lockmode = AlterTableGetLockLevel(cmds);
    4395             : 
    4396         278 :     rel = relation_open(relid, lockmode);
    4397             : 
    4398         278 :     EventTriggerAlterTableRelid(relid);
    4399             : 
    4400         278 :     ATController(NULL, rel, cmds, recurse, lockmode, NULL);
    4401         278 : }
    4402             : 
    4403             : /*
    4404             :  * AlterTableGetLockLevel
    4405             :  *
    4406             :  * Sets the overall lock level required for the supplied list of subcommands.
    4407             :  * Policy for doing this set according to needs of AlterTable(), see
    4408             :  * comments there for overall explanation.
    4409             :  *
    4410             :  * Function is called before and after parsing, so it must give same
    4411             :  * answer each time it is called. Some subcommands are transformed
    4412             :  * into other subcommand types, so the transform must never be made to a
    4413             :  * lower lock level than previously assigned. All transforms are noted below.
    4414             :  *
    4415             :  * Since this is called before we lock the table we cannot use table metadata
    4416             :  * to influence the type of lock we acquire.
    4417             :  *
    4418             :  * There should be no lockmodes hardcoded into the subcommand functions. All
    4419             :  * lockmode decisions for ALTER TABLE are made here only. The one exception is
    4420             :  * ALTER TABLE RENAME which is treated as a different statement type T_RenameStmt
    4421             :  * and does not travel through this section of code and cannot be combined with
    4422             :  * any of the subcommands given here.
    4423             :  *
    4424             :  * Note that Hot Standby only knows about AccessExclusiveLocks on the primary
    4425             :  * so any changes that might affect SELECTs running on standbys need to use
    4426             :  * AccessExclusiveLocks even if you think a lesser lock would do, unless you
    4427             :  * have a solution for that also.
    4428             :  *
    4429             :  * Also note that pg_dump uses only an AccessShareLock, meaning that anything
    4430             :  * that takes a lock less than AccessExclusiveLock can change object definitions
    4431             :  * while pg_dump is running. Be careful to check that the appropriate data is
    4432             :  * derived by pg_dump using an MVCC snapshot, rather than syscache lookups,
    4433             :  * otherwise we might end up with an inconsistent dump that can't restore.
    4434             :  */
    4435             : LOCKMODE
    4436       27530 : AlterTableGetLockLevel(List *cmds)
    4437             : {
    4438             :     /*
    4439             :      * This only works if we read catalog tables using MVCC snapshots.
    4440             :      */
    4441             :     ListCell   *lcmd;
    4442       27530 :     LOCKMODE    lockmode = ShareUpdateExclusiveLock;
    4443             : 
    4444       56016 :     foreach(lcmd, cmds)
    4445             :     {
    4446       28486 :         AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);
    4447       28486 :         LOCKMODE    cmd_lockmode = AccessExclusiveLock; /* default for compiler */
    4448             : 
    4449       28486 :         switch (cmd->subtype)
    4450             :         {
    4451             :                 /*
    4452             :                  * These subcommands rewrite the heap, so require full locks.
    4453             :                  */
    4454        3180 :             case AT_AddColumn:  /* may rewrite heap, in some cases and visible
    4455             :                                  * to SELECT */
    4456             :             case AT_SetAccessMethod:    /* must rewrite heap */
    4457             :             case AT_SetTableSpace:  /* must rewrite heap */
    4458             :             case AT_AlterColumnType:    /* must rewrite heap */
    4459        3180 :                 cmd_lockmode = AccessExclusiveLock;
    4460        3180 :                 break;
    4461             : 
    4462             :                 /*
    4463             :                  * These subcommands may require addition of toast tables. If
    4464             :                  * we add a toast table to a table currently being scanned, we
    4465             :                  * might miss data added to the new toast table by concurrent
    4466             :                  * insert transactions.
    4467             :                  */
    4468         212 :             case AT_SetStorage: /* may add toast tables, see
    4469             :                                  * ATRewriteCatalogs() */
    4470         212 :                 cmd_lockmode = AccessExclusiveLock;
    4471         212 :                 break;
    4472             : 
    4473             :                 /*
    4474             :                  * Removing constraints can affect SELECTs that have been
    4475             :                  * optimized assuming the constraint holds true. See also
    4476             :                  * CloneFkReferenced.
    4477             :                  */
    4478         780 :             case AT_DropConstraint: /* as DROP INDEX */
    4479             :             case AT_DropNotNull:    /* may change some SQL plans */
    4480         780 :                 cmd_lockmode = AccessExclusiveLock;
    4481         780 :                 break;
    4482             : 
    4483             :                 /*
    4484             :                  * Subcommands that may be visible to concurrent SELECTs
    4485             :                  */
    4486        1698 :             case AT_DropColumn: /* change visible to SELECT */
    4487             :             case AT_AddColumnToView:    /* CREATE VIEW */
    4488             :             case AT_DropOids:   /* used to equiv to DropColumn */
    4489             :             case AT_EnableAlwaysRule:   /* may change SELECT rules */
    4490             :             case AT_EnableReplicaRule:  /* may change SELECT rules */
    4491             :             case AT_EnableRule: /* may change SELECT rules */
    4492             :             case AT_DisableRule:    /* may change SELECT rules */
    4493        1698 :                 cmd_lockmode = AccessExclusiveLock;
    4494        1698 :                 break;
    4495             : 
    4496             :                 /*
    4497             :                  * Changing owner may remove implicit SELECT privileges
    4498             :                  */
    4499        1794 :             case AT_ChangeOwner:    /* change visible to SELECT */
    4500        1794 :                 cmd_lockmode = AccessExclusiveLock;
    4501        1794 :                 break;
    4502             : 
    4503             :                 /*
    4504             :                  * Changing foreign table options may affect optimization.
    4505             :                  */
    4506         238 :             case AT_GenericOptions:
    4507             :             case AT_AlterColumnGenericOptions:
    4508         238 :                 cmd_lockmode = AccessExclusiveLock;
    4509         238 :                 break;
    4510             : 
    4511             :                 /*
    4512             :                  * These subcommands affect write operations only.
    4513             :                  */
    4514         340 :             case AT_EnableTrig:
    4515             :             case AT_EnableAlwaysTrig:
    4516             :             case AT_EnableReplicaTrig:
    4517             :             case AT_EnableTrigAll:
    4518             :             case AT_EnableTrigUser:
    4519             :             case AT_DisableTrig:
    4520             :             case AT_DisableTrigAll:
    4521             :             case AT_DisableTrigUser:
    4522         340 :                 cmd_lockmode = ShareRowExclusiveLock;
    4523         340 :                 break;
    4524             : 
    4525             :                 /*
    4526             :                  * These subcommands affect write operations only. XXX
    4527             :                  * Theoretically, these could be ShareRowExclusiveLock.
    4528             :                  */
    4529        2412 :             case AT_ColumnDefault:
    4530             :             case AT_CookedColumnDefault:
    4531             :             case AT_AlterConstraint:
    4532             :             case AT_AddIndex:   /* from ADD CONSTRAINT */
    4533             :             case AT_AddIndexConstraint:
    4534             :             case AT_ReplicaIdentity:
    4535             :             case AT_SetNotNull:
    4536             :             case AT_EnableRowSecurity:
    4537             :             case AT_DisableRowSecurity:
    4538             :             case AT_ForceRowSecurity:
    4539             :             case AT_NoForceRowSecurity:
    4540             :             case AT_AddIdentity:
    4541             :             case AT_DropIdentity:
    4542             :             case AT_SetIdentity:
    4543             :             case AT_SetExpression:
    4544             :             case AT_DropExpression:
    4545             :             case AT_SetCompression:
    4546        2412 :                 cmd_lockmode = AccessExclusiveLock;
    4547        2412 :                 break;
    4548             : 
    4549       12118 :             case AT_AddConstraint:
    4550             :             case AT_ReAddConstraint:    /* becomes AT_AddConstraint */
    4551             :             case AT_ReAddDomainConstraint:  /* becomes AT_AddConstraint */
    4552       12118 :                 if (IsA(cmd->def, Constraint))
    4553             :                 {
    4554       12118 :                     Constraint *con = (Constraint *) cmd->def;
    4555             : 
    4556       12118 :                     switch (con->contype)
    4557             :                     {
    4558        9394 :                         case CONSTR_EXCLUSION:
    4559             :                         case CONSTR_PRIMARY:
    4560             :                         case CONSTR_UNIQUE:
    4561             : 
    4562             :                             /*
    4563             :                              * Cases essentially the same as CREATE INDEX. We
    4564             :                              * could reduce the lock strength to ShareLock if
    4565             :                              * we can work out how to allow concurrent catalog
    4566             :                              * updates. XXX Might be set down to
    4567             :                              * ShareRowExclusiveLock but requires further
    4568             :                              * analysis.
    4569             :                              */
    4570        9394 :                             cmd_lockmode = AccessExclusiveLock;
    4571        9394 :                             break;
    4572        2040 :                         case CONSTR_FOREIGN:
    4573             : 
    4574             :                             /*
    4575             :                              * We add triggers to both tables when we add a
    4576             :                              * Foreign Key, so the lock level must be at least
    4577             :                              * as strong as CREATE TRIGGER.
    4578             :                              */
    4579        2040 :                             cmd_lockmode = ShareRowExclusiveLock;
    4580        2040 :                             break;
    4581             : 
    4582         684 :                         default:
    4583         684 :                             cmd_lockmode = AccessExclusiveLock;
    4584             :                     }
    4585           0 :                 }
    4586       12118 :                 break;
    4587             : 
    4588             :                 /*
    4589             :                  * These subcommands affect inheritance behaviour. Queries
    4590             :                  * started before us will continue to see the old inheritance
    4591             :                  * behaviour, while queries started after we commit will see
    4592             :                  * new behaviour. No need to prevent reads or writes to the
    4593             :                  * subtable while we hook it up though. Changing the TupDesc
    4594             :                  * may be a problem, so keep highest lock.
    4595             :                  */
    4596         310 :             case AT_AddInherit:
    4597             :             case AT_DropInherit:
    4598         310 :                 cmd_lockmode = AccessExclusiveLock;
    4599         310 :                 break;
    4600             : 
    4601             :                 /*
    4602             :                  * These subcommands affect implicit row type conversion. They
    4603             :                  * have affects similar to CREATE/DROP CAST on queries. don't
    4604             :                  * provide for invalidating parse trees as a result of such
    4605             :                  * changes, so we keep these at AccessExclusiveLock.
    4606             :                  */
    4607          72 :             case AT_AddOf:
    4608             :             case AT_DropOf:
    4609          72 :                 cmd_lockmode = AccessExclusiveLock;
    4610          72 :                 break;
    4611             : 
    4612             :                 /*
    4613             :                  * Only used by CREATE OR REPLACE VIEW which must conflict
    4614             :                  * with an SELECTs currently using the view.
    4615             :                  */
    4616         194 :             case AT_ReplaceRelOptions:
    4617         194 :                 cmd_lockmode = AccessExclusiveLock;
    4618         194 :                 break;
    4619             : 
    4620             :                 /*
    4621             :                  * These subcommands affect general strategies for performance
    4622             :                  * and maintenance, though don't change the semantic results
    4623             :                  * from normal data reads and writes. Delaying an ALTER TABLE
    4624             :                  * behind currently active writes only delays the point where
    4625             :                  * the new strategy begins to take effect, so there is no
    4626             :                  * benefit in waiting. In this case the minimum restriction
    4627             :                  * applies: we don't currently allow concurrent catalog
    4628             :                  * updates.
    4629             :                  */
    4630         234 :             case AT_SetStatistics:  /* Uses MVCC in getTableAttrs() */
    4631             :             case AT_ClusterOn:  /* Uses MVCC in getIndexes() */
    4632             :             case AT_DropCluster:    /* Uses MVCC in getIndexes() */
    4633             :             case AT_SetOptions: /* Uses MVCC in getTableAttrs() */
    4634             :             case AT_ResetOptions:   /* Uses MVCC in getTableAttrs() */
    4635         234 :                 cmd_lockmode = ShareUpdateExclusiveLock;
    4636         234 :                 break;
    4637             : 
    4638          88 :             case AT_SetLogged:
    4639             :             case AT_SetUnLogged:
    4640          88 :                 cmd_lockmode = AccessExclusiveLock;
    4641          88 :                 break;
    4642             : 
    4643         388 :             case AT_ValidateConstraint: /* Uses MVCC in getConstraints() */
    4644         388 :                 cmd_lockmode = ShareUpdateExclusiveLock;
    4645         388 :                 break;
    4646             : 
    4647             :                 /*
    4648             :                  * Rel options are more complex than first appears. Options
    4649             :                  * are set here for tables, views and indexes; for historical
    4650             :                  * reasons these can all be used with ALTER TABLE, so we can't
    4651             :                  * decide between them using the basic grammar.
    4652             :                  */
    4653         752 :             case AT_SetRelOptions:  /* Uses MVCC in getIndexes() and
    4654             :                                      * getTables() */
    4655             :             case AT_ResetRelOptions:    /* Uses MVCC in getIndexes() and
    4656             :                                          * getTables() */
    4657         752 :                 cmd_lockmode = AlterTableGetRelOptionsLockLevel((List *) cmd->def);
    4658         752 :                 break;
    4659             : 
    4660        2624 :             case AT_AttachPartition:
    4661        2624 :                 cmd_lockmode = ShareUpdateExclusiveLock;
    4662        2624 :                 break;
    4663             : 
    4664         552 :             case AT_DetachPartition:
    4665         552 :                 if (((PartitionCmd *) cmd->def)->concurrent)
    4666         158 :                     cmd_lockmode = ShareUpdateExclusiveLock;
    4667             :                 else
    4668         394 :                     cmd_lockmode = AccessExclusiveLock;
    4669         552 :                 break;
    4670             : 
    4671          14 :             case AT_DetachPartitionFinalize:
    4672          14 :                 cmd_lockmode = ShareUpdateExclusiveLock;
    4673          14 :                 break;
    4674             : 
    4675         300 :             case AT_SplitPartition:
    4676         300 :                 cmd_lockmode = AccessExclusiveLock;
    4677         300 :                 break;
    4678             : 
    4679         186 :             case AT_MergePartitions:
    4680         186 :                 cmd_lockmode = AccessExclusiveLock;
    4681         186 :                 break;
    4682             : 
    4683           0 :             case AT_CheckNotNull:
    4684             : 
    4685             :                 /*
    4686             :                  * This only examines the table's schema; but lock must be
    4687             :                  * strong enough to prevent concurrent DROP NOT NULL.
    4688             :                  */
    4689           0 :                 cmd_lockmode = AccessShareLock;
    4690           0 :                 break;
    4691             : 
    4692           0 :             default:            /* oops */
    4693           0 :                 elog(ERROR, "unrecognized alter table type: %d",
    4694             :                      (int) cmd->subtype);
    4695             :                 break;
    4696             :         }
    4697             : 
    4698             :         /*
    4699             :          * Take the greatest lockmode from any subcommand
    4700             :          */
    4701       28486 :         if (cmd_lockmode > lockmode)
    4702       23546 :             lockmode = cmd_lockmode;
    4703             :     }
    4704             : 
    4705       27530 :     return lockmode;
    4706             : }
    4707             : 
    4708             : /*
    4709             :  * ATController provides top level control over the phases.
    4710             :  *
    4711             :  * parsetree is passed in to allow it to be passed to event triggers
    4712             :  * when requested.
    4713             :  */
    4714             : static void
    4715       27278 : ATController(AlterTableStmt *parsetree,
    4716             :              Relation rel, List *cmds, bool recurse, LOCKMODE lockmode,
    4717             :              AlterTableUtilityContext *context)
    4718             : {
    4719       27278 :     List       *wqueue = NIL;
    4720             :     ListCell   *lcmd;
    4721             : 
    4722             :     /* Phase 1: preliminary examination of commands, create work queue */
    4723       55222 :     foreach(lcmd, cmds)
    4724             :     {
    4725       28228 :         AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);
    4726             : 
    4727       28228 :         ATPrepCmd(&wqueue, rel, cmd, recurse, false, lockmode, context);
    4728             :     }
    4729             : 
    4730             :     /* Close the relation, but keep lock until commit */
    4731       26994 :     relation_close(rel, NoLock);
    4732             : 
    4733             :     /* Phase 2: update system catalogs */
    4734       26994 :     ATRewriteCatalogs(&wqueue, lockmode, context);
    4735             : 
    4736             :     /* Phase 3: scan/rewrite tables as needed, and run afterStmts */
    4737       24616 :     ATRewriteTables(parsetree, &wqueue, lockmode, context);
    4738       24304 : }
    4739             : 
    4740             : /*
    4741             :  * ATPrepCmd
    4742             :  *
    4743             :  * Traffic cop for ALTER TABLE Phase 1 operations, including simple
    4744             :  * recursion and permission checks.
    4745             :  *
    4746             :  * Caller must have acquired appropriate lock type on relation already.
    4747             :  * This lock should be held until commit.
    4748             :  */
    4749             : static void
    4750       28920 : ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
    4751             :           bool recurse, bool recursing, LOCKMODE lockmode,
    4752             :           AlterTableUtilityContext *context)
    4753             : {
    4754             :     AlteredTableInfo *tab;
    4755       28920 :     AlterTablePass pass = AT_PASS_UNSET;
    4756             : 
    4757             :     /* Find or create work queue entry for this table */
    4758       28920 :     tab = ATGetQueueEntry(wqueue, rel);
    4759             : 
    4760             :     /*
    4761             :      * Disallow any ALTER TABLE other than ALTER TABLE DETACH FINALIZE on
    4762             :      * partitions that are pending detach.
    4763             :      */
    4764       28920 :     if (rel->rd_rel->relispartition &&
    4765        2864 :         cmd->subtype != AT_DetachPartitionFinalize &&
    4766        1432 :         PartitionHasPendingDetach(RelationGetRelid(rel)))
    4767           2 :         ereport(ERROR,
    4768             :                 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    4769             :                 errmsg("cannot alter partition \"%s\" with an incomplete detach",
    4770             :                        RelationGetRelationName(rel)),
    4771             :                 errhint("Use ALTER TABLE ... DETACH PARTITION ... FINALIZE to complete the pending detach operation."));
    4772             : 
    4773             :     /*
    4774             :      * Copy the original subcommand for each table, so we can scribble on it.
    4775             :      * This avoids conflicts when different child tables need to make
    4776             :      * different parse transformations (for example, the same column may have
    4777             :      * different column numbers in different children).
    4778             :      */
    4779       28918 :     cmd = copyObject(cmd);
    4780             : 
    4781             :     /*
    4782             :      * Do permissions and relkind checking, recursion to child tables if
    4783             :      * needed, and any additional phase-1 processing needed.  (But beware of
    4784             :      * adding any processing that looks at table details that another
    4785             :      * subcommand could change.  In some cases we reject multiple subcommands
    4786             :      * that could try to change the same state in contrary ways.)
    4787             :      */
    4788       28918 :     switch (cmd->subtype)
    4789             :     {
    4790        1932 :         case AT_AddColumn:      /* ADD COLUMN */
    4791        1932 :             ATSimplePermissions(cmd->subtype, rel,
    4792             :                                 ATT_TABLE | ATT_COMPOSITE_TYPE | ATT_FOREIGN_TABLE);
    4793        1932 :             ATPrepAddColumn(wqueue, rel, recurse, recursing, false, cmd,
    4794             :                             lockmode, context);
    4795             :             /* Recursion occurs during execution phase */
    4796        1920 :             pass = AT_PASS_ADD_COL;
    4797        1920 :             break;
    4798          24 :         case AT_AddColumnToView:    /* add column via CREATE OR REPLACE VIEW */
    4799          24 :             ATSimplePermissions(cmd->subtype, rel, ATT_VIEW);
    4800          24 :             ATPrepAddColumn(wqueue, rel, recurse, recursing, true, cmd,
    4801             :                             lockmode, context);
    4802             :             /* Recursion occurs during execution phase */
    4803          24 :             pass = AT_PASS_ADD_COL;
    4804          24 :             break;
    4805         556 :         case AT_ColumnDefault:  /* ALTER COLUMN DEFAULT */
    4806             : 
    4807             :             /*
    4808             :              * We allow defaults on views so that INSERT into a view can have
    4809             :              * default-ish behavior.  This works because the rewriter
    4810             :              * substitutes default values into INSERTs before it expands
    4811             :              * rules.
    4812             :              */
    4813         556 :             ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_VIEW | ATT_FOREIGN_TABLE);
    4814         556 :             ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
    4815             :             /* No command-specific prep needed */
    4816         556 :             pass = cmd->def ? AT_PASS_ADD_OTHERCONSTR : AT_PASS_DROP;
    4817         556 :             break;
    4818         110 :         case AT_CookedColumnDefault:    /* add a pre-cooked default */
    4819             :             /* This is currently used only in CREATE TABLE */
    4820             :             /* (so the permission check really isn't necessary) */
    4821         110 :             ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
    4822             :             /* This command never recurses */
    4823         110 :             pass = AT_PASS_ADD_OTHERCONSTR;
    4824         110 :             break;
    4825         156 :         case AT_AddIdentity:
    4826         156 :             ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_VIEW | ATT_FOREIGN_TABLE);
    4827             :             /* Set up recursion for phase 2; no other prep needed */
    4828         156 :             if (recurse)
    4829         150 :                 cmd->recurse = true;
    4830         156 :             pass = AT_PASS_ADD_OTHERCONSTR;
    4831         156 :             break;
    4832          62 :         case AT_SetIdentity:
    4833          62 :             ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_VIEW | ATT_FOREIGN_TABLE);
    4834             :             /* Set up recursion for phase 2; no other prep needed */
    4835          62 :             if (recurse)
    4836          56 :                 cmd->recurse = true;
    4837             :             /* This should run after AddIdentity, so do it in MISC pass */
    4838          62 :             pass = AT_PASS_MISC;
    4839          62 :             break;
    4840          56 :         case AT_DropIdentity:
    4841          56 :             ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_VIEW | ATT_FOREIGN_TABLE);
    4842             :             /* Set up recursion for phase 2; no other prep needed */
    4843          56 :             if (recurse)
    4844          50 :                 cmd->recurse = true;
    4845          56 :             pass = AT_PASS_DROP;
    4846          56 :             break;
    4847         236 :         case AT_DropNotNull:    /* ALTER COLUMN DROP NOT NULL */
    4848         236 :             ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
    4849         224 :             ATPrepDropNotNull(rel, recurse, recursing);
    4850         218 :             ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
    4851         212 :             pass = AT_PASS_DROP;
    4852         212 :             break;
    4853         590 :         case AT_SetNotNull:     /* ALTER COLUMN SET NOT NULL */
    4854         590 :             ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
    4855             :             /* Need command-specific recursion decision */
    4856         584 :             ATPrepSetNotNull(wqueue, rel, cmd, recurse, recursing,
    4857             :                              lockmode, context);
    4858         584 :             pass = AT_PASS_COL_ATTRS;
    4859         584 :             break;
    4860          84 :         case AT_CheckNotNull:   /* check column is already marked NOT NULL */
    4861          84 :             ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
    4862          84 :             ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
    4863             :             /* No command-specific prep needed */
    4864          84 :             pass = AT_PASS_COL_ATTRS;
    4865          84 :             break;
    4866          84 :         case AT_SetExpression:  /* ALTER COLUMN SET EXPRESSION */
    4867          84 :             ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
    4868          84 :             ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
    4869          84 :             pass = AT_PASS_SET_EXPRESSION;
    4870          84 :             break;
    4871          44 :         case AT_DropExpression: /* ALTER COLUMN DROP EXPRESSION */
    4872          44 :             ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
    4873          44 :             ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
    4874          44 :             ATPrepDropExpression(rel, cmd, recurse, recursing, lockmode);
    4875          32 :             pass = AT_PASS_DROP;
    4876          32 :             break;
    4877         164 :         case AT_SetStatistics:  /* ALTER COLUMN SET STATISTICS */
    4878         164 :             ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_MATVIEW | ATT_INDEX | ATT_PARTITIONED_INDEX | ATT_FOREIGN_TABLE);
    4879         164 :             ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
    4880             :             /* No command-specific prep needed */
    4881         164 :             pass = AT_PASS_MISC;
    4882         164 :             break;
    4883          44 :         case AT_SetOptions:     /* ALTER COLUMN SET ( options ) */
    4884             :         case AT_ResetOptions:   /* ALTER COLUMN RESET ( options ) */
    4885          44 :             ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_MATVIEW | ATT_FOREIGN_TABLE);
    4886             :             /* This command never recurses */
    4887          32 :             pass = AT_PASS_MISC;
    4888          32 :             break;
    4889         234 :         case AT_SetStorage:     /* ALTER COLUMN SET STORAGE */
    4890         234 :             ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_MATVIEW | ATT_FOREIGN_TABLE);
    4891         234 :             ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
    4892             :             /* No command-specific prep needed */
    4893         234 :             pass = AT_PASS_MISC;
    4894         234 :             break;
    4895          68 :         case AT_SetCompression: /* ALTER COLUMN SET COMPRESSION */
    4896          68 :             ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_MATVIEW);
    4897             :             /* This command never recurses */
    4898             :             /* No command-specific prep needed */
    4899          68 :             pass = AT_PASS_MISC;
    4900          68 :             break;
    4901        1604 :         case AT_DropColumn:     /* DROP COLUMN */
    4902        1604 :             ATSimplePermissions(cmd->subtype, rel,
    4903             :                                 ATT_TABLE | ATT_COMPOSITE_TYPE | ATT_FOREIGN_TABLE);
    4904        1598 :             ATPrepDropColumn(wqueue, rel, recurse, recursing, cmd,
    4905             :                              lockmode, context);
    4906             :             /* Recursion occurs during execution phase */
    4907        1586 :             pass = AT_PASS_DROP;
    4908        1586 :             break;
    4909           0 :         case AT_AddIndex:       /* ADD INDEX */
    4910           0 :             ATSimplePermissions(cmd->subtype, rel, ATT_TABLE);
    4911             :             /* This command never recurses */
    4912             :             /* No command-specific prep needed */
    4913           0 :             pass = AT_PASS_ADD_INDEX;
    4914           0 :             break;
    4915       12092 :         case AT_AddConstraint:  /* ADD CONSTRAINT */
    4916       12092 :             ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
    4917             :             /* Recursion occurs during execution phase */
    4918             :             /* No command-specific prep needed except saving recurse flag */
    4919       12092 :             if (recurse)
    4920       11782 :                 cmd->recurse = true;
    4921       12092 :             pass = AT_PASS_ADD_CONSTR;
    4922       12092 :             break;
    4923           0 :         case AT_AddIndexConstraint: /* ADD CONSTRAINT USING INDEX */
    4924           0 :             ATSimplePermissions(cmd->subtype, rel, ATT_TABLE);
    4925             :             /* This command never recurses */
    4926             :             /* No command-specific prep needed */
    4927           0 :             pass = AT_PASS_ADD_INDEXCONSTR;
    4928           0 :             break;
    4929         564 :         case AT_DropConstraint: /* DROP CONSTRAINT */
    4930         564 :             ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
    4931         564 :             ATCheckPartitionsNotInUse(rel, lockmode);
    4932             :             /* Other recursion occurs during execution phase */
    4933             :             /* No command-specific prep needed except saving recurse flag */
    4934         558 :             if (recurse)
    4935         534 :                 cmd->recurse = true;
    4936         558 :             pass = AT_PASS_DROP;
    4937         558 :             break;
    4938        1128 :         case AT_AlterColumnType:    /* ALTER COLUMN TYPE */
    4939        1128 :             ATSimplePermissions(cmd->subtype, rel,
    4940             :                                 ATT_TABLE | ATT_COMPOSITE_TYPE | ATT_FOREIGN_TABLE);
    4941             :             /* See comments for ATPrepAlterColumnType */
    4942        1128 :             cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, recurse, lockmode,
    4943             :                                       AT_PASS_UNSET, context);
    4944             :             Assert(cmd != NULL);
    4945             :             /* Performs own recursion */
    4946        1122 :             ATPrepAlterColumnType(wqueue, tab, rel, recurse, recursing, cmd,
    4947             :                                   lockmode, context);
    4948         978 :             pass = AT_PASS_ALTER_TYPE;
    4949         978 :             break;
    4950         164 :         case AT_AlterColumnGenericOptions:
    4951         164 :             ATSimplePermissions(cmd->subtype, rel, ATT_FOREIGN_TABLE);
    4952             :             /* This command never recurses */
    4953             :             /* No command-specific prep needed */
    4954         164 :             pass = AT_PASS_MISC;
    4955         164 :             break;
    4956        1770 :         case AT_ChangeOwner:    /* ALTER OWNER */
    4957             :             /* This command never recurses */
    4958             :             /* No command-specific prep needed */
    4959        1770 :             pass = AT_PASS_MISC;
    4960        1770 :             break;
    4961          64 :         case AT_ClusterOn:      /* CLUSTER ON */
    4962             :         case AT_DropCluster:    /* SET WITHOUT CLUSTER */
    4963          64 :             ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_MATVIEW);
    4964             :             /* These commands never recurse */
    4965             :             /* No command-specific prep needed */
    4966          64 :             pass = AT_PASS_MISC;
    4967          64 :             break;
    4968          38 :         case AT_SetLogged:      /* SET LOGGED */
    4969          38 :             ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_SEQUENCE);
    4970          38 :             if (tab->chgPersistence)
    4971           0 :                 ereport(ERROR,
    4972             :                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    4973             :                          errmsg("cannot change persistence setting twice")));
    4974          38 :             tab->chgPersistence = ATPrepChangePersistence(rel, true);
    4975             :             /* force rewrite if necessary; see comment in ATRewriteTables */
    4976          32 :             if (tab->chgPersistence)
    4977             :             {
    4978          26 :                 tab->rewrite |= AT_REWRITE_ALTER_PERSISTENCE;
    4979          26 :                 tab->newrelpersistence = RELPERSISTENCE_PERMANENT;
    4980             :             }
    4981          32 :             pass = AT_PASS_MISC;
    4982          32 :             break;
    4983          50 :         case AT_SetUnLogged:    /* SET UNLOGGED */
    4984          50 :             ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_SEQUENCE);
    4985          50 :             if (tab->chgPersistence)
    4986           0 :                 ereport(ERROR,
    4987             :                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    4988             :                          errmsg("cannot change persistence setting twice")));
    4989          50 :             tab->chgPersistence = ATPrepChangePersistence(rel, false);
    4990             :             /* force rewrite if necessary; see comment in ATRewriteTables */
    4991          44 :             if (tab->chgPersistence)
    4992             :             {
    4993          38 :                 tab->rewrite |= AT_REWRITE_ALTER_PERSISTENCE;
    4994          38 :                 tab->newrelpersistence = RELPERSISTENCE_UNLOGGED;
    4995             :             }
    4996          44 :             pass = AT_PASS_MISC;
    4997          44 :             break;
    4998           6 :         case AT_DropOids:       /* SET WITHOUT OIDS */
    4999           6 :             ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
    5000           6 :             pass = AT_PASS_DROP;
    5001           6 :             break;
    5002         128 :         case AT_SetAccessMethod:    /* SET ACCESS METHOD */
    5003         128 :             ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_MATVIEW);
    5004             : 
    5005             :             /* check if another access method change was already requested */
    5006         128 :             if (tab->chgAccessMethod)
    5007          18 :                 ereport(ERROR,
    5008             :                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    5009             :                          errmsg("cannot have multiple SET ACCESS METHOD subcommands")));
    5010             : 
    5011         110 :             ATPrepSetAccessMethod(tab, rel, cmd->name);
    5012         110 :             pass = AT_PASS_MISC;    /* does not matter; no work in Phase 2 */
    5013         110 :             break;
    5014         158 :         case AT_SetTableSpace:  /* SET TABLESPACE */
    5015         158 :             ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_MATVIEW | ATT_INDEX |
    5016             :                                 ATT_PARTITIONED_INDEX);
    5017             :             /* This command never recurses */
    5018         158 :             ATPrepSetTableSpace(tab, rel, cmd->name, lockmode);
    5019         158 :             pass = AT_PASS_MISC;    /* doesn't actually matter */
    5020         158 :             break;
    5021         946 :         case AT_SetRelOptions:  /* SET (...) */
    5022             :         case AT_ResetRelOptions:    /* RESET (...) */
    5023             :         case AT_ReplaceRelOptions:  /* reset them all, then set just these */
    5024         946 :             ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_VIEW | ATT_MATVIEW | ATT_INDEX);
    5025             :             /* This command never recurses */
    5026             :             /* No command-specific prep needed */
    5027         946 :             pass = AT_PASS_MISC;
    5028         946 :             break;
    5029         272 :         case AT_AddInherit:     /* INHERIT */
    5030         272 :             ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
    5031             :             /* This command never recurses */
    5032         272 :             ATPrepAddInherit(rel);
    5033         254 :             pass = AT_PASS_MISC;
    5034         254 :             break;
    5035          38 :         case AT_DropInherit:    /* NO INHERIT */
    5036          38 :             ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
    5037             :             /* This command never recurses */
    5038             :             /* No command-specific prep needed */
    5039          38 :             pass = AT_PASS_MISC;
    5040          38 :             break;
    5041          72 :         case AT_AlterConstraint:    /* ALTER CONSTRAINT */
    5042          72 :             ATSimplePermissions(cmd->subtype, rel, ATT_TABLE);
    5043             :             /* Recursion occurs during execution phase */
    5044          66 :             pass = AT_PASS_MISC;
    5045          66 :             break;
    5046         388 :         case AT_ValidateConstraint: /* VALIDATE CONSTRAINT */
    5047         388 :             ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
    5048             :             /* Recursion occurs during execution phase */
    5049             :             /* No command-specific prep needed except saving recurse flag */
    5050         388 :             if (recurse)
    5051         388 :                 cmd->recurse = true;
    5052         388 :             pass = AT_PASS_MISC;
    5053         388 :             break;
    5054         418 :         case AT_ReplicaIdentity:    /* REPLICA IDENTITY ... */
    5055         418 :             ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_MATVIEW);
    5056         418 :             pass = AT_PASS_MISC;
    5057             :             /* This command never recurses */
    5058             :             /* No command-specific prep needed */
    5059         418 :             break;
    5060         340 :         case AT_EnableTrig:     /* ENABLE TRIGGER variants */
    5061             :         case AT_EnableAlwaysTrig:
    5062             :         case AT_EnableReplicaTrig:
    5063             :         case AT_EnableTrigAll:
    5064             :         case AT_EnableTrigUser:
    5065             :         case AT_DisableTrig:    /* DISABLE TRIGGER variants */
    5066             :         case AT_DisableTrigAll:
    5067             :         case AT_DisableTrigUser:
    5068         340 :             ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
    5069             :             /* Set up recursion for phase 2; no other prep needed */
    5070         340 :             if (recurse)
    5071         312 :                 cmd->recurse = true;
    5072         340 :             pass = AT_PASS_MISC;
    5073         340 :             break;
    5074         532 :         case AT_EnableRule:     /* ENABLE/DISABLE RULE variants */
    5075             :         case AT_EnableAlwaysRule:
    5076             :         case AT_EnableReplicaRule:
    5077             :         case AT_DisableRule:
    5078             :         case AT_AddOf:          /* OF */
    5079             :         case AT_DropOf:         /* NOT OF */
    5080             :         case AT_EnableRowSecurity:
    5081             :         case AT_DisableRowSecurity:
    5082             :         case AT_ForceRowSecurity:
    5083             :         case AT_NoForceRowSecurity:
    5084         532 :             ATSimplePermissions(cmd->subtype, rel, ATT_TABLE);
    5085             :             /* These commands never recurse */
    5086             :             /* No command-specific prep needed */
    5087         532 :             pass = AT_PASS_MISC;
    5088         532 :             break;
    5089          50 :         case AT_GenericOptions:
    5090          50 :             ATSimplePermissions(cmd->subtype, rel, ATT_FOREIGN_TABLE);
    5091             :             /* No command-specific prep needed */
    5092          50 :             pass = AT_PASS_MISC;
    5093          50 :             break;
    5094        2612 :         case AT_AttachPartition:
    5095        2612 :             ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_PARTITIONED_INDEX);
    5096             :             /* No command-specific prep needed */
    5097        2612 :             pass = AT_PASS_MISC;
    5098        2612 :             break;
    5099         552 :         case AT_DetachPartition:
    5100         552 :             ATSimplePermissions(cmd->subtype, rel, ATT_TABLE);
    5101             :             /* No command-specific prep needed */
    5102         546 :             pass = AT_PASS_MISC;
    5103         546 :             break;
    5104          14 :         case AT_DetachPartitionFinalize:
    5105          14 :             ATSimplePermissions(cmd->subtype, rel, ATT_TABLE);
    5106             :             /* No command-specific prep needed */
    5107          14 :             pass = AT_PASS_MISC;
    5108          14 :             break;
    5109         294 :         case AT_SplitPartition:
    5110         294 :             ATSimplePermissions(cmd->subtype, rel, ATT_TABLE);
    5111             :             /* No command-specific prep needed */
    5112         294 :             pass = AT_PASS_MISC;
    5113         294 :             break;
    5114         180 :         case AT_MergePartitions:
    5115         180 :             ATSimplePermissions(cmd->subtype, rel, ATT_TABLE);
    5116             :             /* No command-specific prep needed */
    5117         180 :             pass = AT_PASS_MISC;
    5118         180 :             break;
    5119           0 :         default:                /* oops */
    5120           0 :             elog(ERROR, "unrecognized alter table type: %d",
    5121             :                  (int) cmd->subtype);
    5122             :             pass = AT_PASS_UNSET;   /* keep compiler quiet */
    5123             :             break;
    5124             :     }
    5125             :     Assert(pass > AT_PASS_UNSET);
    5126             : 
    5127             :     /* Add the subcommand to the appropriate list for phase 2 */
    5128       28618 :     tab->subcmds[pass] = lappend(tab->subcmds[pass], cmd);
    5129       28618 : }
    5130             : 
    5131             : /*
    5132             :  * ATRewriteCatalogs
    5133             :  *
    5134             :  * Traffic cop for ALTER TABLE Phase 2 operations.  Subcommands are
    5135             :  * dispatched in a "safe" execution order (designed to avoid unnecessary
    5136             :  * conflicts).
    5137             :  */
    5138             : static void
    5139       26994 : ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode,
    5140             :                   AlterTableUtilityContext *context)
    5141             : {
    5142             :     ListCell   *ltab;
    5143             : 
    5144             :     /*
    5145             :      * We process all the tables "in parallel", one pass at a time.  This is
    5146             :      * needed because we may have to propagate work from one table to another
    5147             :      * (specifically, ALTER TYPE on a foreign key's PK has to dispatch the
    5148             :      * re-adding of the foreign key constraint to the other table).  Work can
    5149             :      * only be propagated into later passes, however.
    5150             :      */
    5151      340888 :     for (AlterTablePass pass = 0; pass < AT_NUM_PASSES; pass++)
    5152             :     {
    5153             :         /* Go through each table that needs to be processed */
    5154      644944 :         foreach(ltab, *wqueue)
    5155             :         {
    5156      331050 :             AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
    5157      331050 :             List       *subcmds = tab->subcmds[pass];
    5158             :             ListCell   *lcmd;
    5159             : 
    5160      331050 :             if (subcmds == NIL)
    5161      285302 :                 continue;
    5162             : 
    5163             :             /*
    5164             :              * Open the relation and store it in tab.  This allows subroutines
    5165             :              * close and reopen, if necessary.  Appropriate lock was obtained
    5166             :              * by phase 1, needn't get it again.
    5167             :              */
    5168       45748 :             tab->rel = relation_open(tab->relid, NoLock);
    5169             : 
    5170       91658 :             foreach(lcmd, subcmds)
    5171       48288 :                 ATExecCmd(wqueue, tab,
    5172       48288 :                           lfirst_node(AlterTableCmd, lcmd),
    5173             :                           lockmode, pass, context);
    5174             : 
    5175             :             /*
    5176             :              * After the ALTER TYPE or SET EXPRESSION pass, do cleanup work
    5177             :              * (this is not done in ATExecAlterColumnType since it should be
    5178             :              * done only once if multiple columns of a table are altered).
    5179             :              */
    5180       43370 :             if (pass == AT_PASS_ALTER_TYPE || pass == AT_PASS_SET_EXPRESSION)
    5181         960 :                 ATPostAlterTypeCleanup(wqueue, tab, lockmode);
    5182             : 
    5183       43370 :             if (tab->rel)
    5184             :             {
    5185       43370 :                 relation_close(tab->rel, NoLock);
    5186       43370 :                 tab->rel = NULL;
    5187             :             }
    5188             :         }
    5189             :     }
    5190             : 
    5191             :     /* Check to see if a toast table must be added. */
    5192       52904 :     foreach(ltab, *wqueue)
    5193             :     {
    5194       28288 :         AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
    5195             : 
    5196             :         /*
    5197             :          * If the table is source table of ATTACH PARTITION command, we did
    5198             :          * not modify anything about it that will change its toasting
    5199             :          * requirement, so no need to check.
    5200             :          */
    5201       28288 :         if (((tab->relkind == RELKIND_RELATION ||
    5202        5934 :               tab->relkind == RELKIND_PARTITIONED_TABLE) &&
    5203       26432 :              tab->partition_constraint == NULL) ||
    5204        3802 :             tab->relkind == RELKIND_MATVIEW)
    5205       24536 :             AlterTableCreateToastTable(tab->relid, (Datum) 0, lockmode);
    5206             :     }
    5207       24616 : }
    5208             : 
    5209             : /*
    5210             :  * ATExecCmd: dispatch a subcommand to appropriate execution routine
    5211             :  */
    5212             : static void
    5213       48288 : ATExecCmd(List **wqueue, AlteredTableInfo *tab,
    5214             :           AlterTableCmd *cmd, LOCKMODE lockmode, AlterTablePass cur_pass,
    5215             :           AlterTableUtilityContext *context)
    5216             : {
    5217       48288 :     ObjectAddress address = InvalidObjectAddress;
    5218       48288 :     Relation    rel = tab->rel;
    5219             : 
    5220       48288 :     switch (cmd->subtype)
    5221             :     {
    5222        1938 :         case AT_AddColumn:      /* ADD COLUMN */
    5223             :         case AT_AddColumnToView:    /* add column via CREATE OR REPLACE VIEW */
    5224        1938 :             address = ATExecAddColumn(wqueue, tab, rel, &cmd,
    5225        1938 :                                       cmd->recurse, false,
    5226             :                                       lockmode, cur_pass, context);
    5227        1824 :             break;
    5228         544 :         case AT_ColumnDefault:  /* ALTER COLUMN DEFAULT */
    5229         544 :             address = ATExecColumnDefault(rel, cmd->name, cmd->def, lockmode);
    5230         484 :             break;
    5231         110 :         case AT_CookedColumnDefault:    /* add a pre-cooked default */
    5232         110 :             address = ATExecCookedColumnDefault(rel, cmd->num, cmd->def);
    5233         110 :             break;
    5234         156 :         case AT_AddIdentity:
    5235         156 :             cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
    5236             :                                       cur_pass, context);
    5237             :             Assert(cmd != NULL);
    5238         150 :             address = ATExecAddIdentity(rel, cmd->name, cmd->def, lockmode, cmd->recurse, false);
    5239         102 :             break;
    5240          62 :         case AT_SetIdentity:
    5241          62 :             cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
    5242             :                                       cur_pass, context);
    5243             :             Assert(cmd != NULL);
    5244          62 :             address = ATExecSetIdentity(rel, cmd->name, cmd->def, lockmode, cmd->recurse, false);
    5245          38 :             break;
    5246          56 :         case AT_DropIdentity:
    5247          56 :             address = ATExecDropIdentity(rel, cmd->name, cmd->missing_ok, lockmode, cmd->recurse, false);
    5248          38 :             break;
    5249         200 :         case AT_DropNotNull:    /* ALTER COLUMN DROP NOT NULL */
    5250         200 :             address = ATExecDropNotNull(rel, cmd->name, lockmode);
    5251         134 :             break;
    5252        7598 :         case AT_SetNotNull:     /* ALTER COLUMN SET NOT NULL */
    5253        7598 :             address = ATExecSetNotNull(tab, rel, cmd->name, lockmode);
    5254        7562 :             break;
    5255          30 :         case AT_CheckNotNull:   /* check column is already marked NOT NULL */
    5256          30 :             ATExecCheckNotNull(tab, rel, cmd->name, lockmode);
    5257          18 :             break;
    5258          84 :         case AT_SetExpression:
    5259          84 :             address = ATExecSetExpression(tab, rel, cmd->name, cmd->def, lockmode);
    5260          78 :             break;
    5261          32 :         case AT_DropExpression:
    5262          32 :             address = ATExecDropExpression(rel, cmd->name, cmd->missing_ok, lockmode);
    5263          26 :             break;
    5264         164 :         case AT_SetStatistics:  /* ALTER COLUMN SET STATISTICS */
    5265         164 :             address = ATExecSetStatistics(rel, cmd->name, cmd->num, cmd->def, lockmode);
    5266         116 :             break;
    5267          26 :         case AT_SetOptions:     /* ALTER COLUMN SET ( options ) */
    5268          26 :             address = ATExecSetOptions(rel, cmd->name, cmd->def, false, lockmode);
    5269          26 :             break;
    5270           6 :         case AT_ResetOptions:   /* ALTER COLUMN RESET ( options ) */
    5271           6 :             address = ATExecSetOptions(rel, cmd->name, cmd->def, true, lockmode);
    5272           6 :             break;
    5273         234 :         case AT_SetStorage:     /* ALTER COLUMN SET STORAGE */
    5274         234 :             address = ATExecSetStorage(rel, cmd->name, cmd->def, lockmode);
    5275         222 :             break;
    5276          68 :         case AT_SetCompression: /* ALTER COLUMN SET COMPRESSION */
    5277          68 :             address = ATExecSetCompression(rel, cmd->name, cmd->def,
    5278             :                                            lockmode);
    5279          62 :             break;
    5280        1586 :         case AT_DropColumn:     /* DROP COLUMN */
    5281        1586 :             address = ATExecDropColumn(wqueue, rel, cmd->name,
    5282        1586 :                                        cmd->behavior, cmd->recurse, false,
    5283        1586 :                                        cmd->missing_ok, lockmode,
    5284             :                                        NULL);
    5285        1418 :             break;
    5286         934 :         case AT_AddIndex:       /* ADD INDEX */
    5287         934 :             address = ATExecAddIndex(tab, rel, (IndexStmt *) cmd->def, false,
    5288             :                                      lockmode);
    5289         812 :             break;
    5290         416 :         case AT_ReAddIndex:     /* ADD INDEX */
    5291         416 :             address = ATExecAddIndex(tab, rel, (IndexStmt *) cmd->def, true,
    5292             :                                      lockmode);
    5293         416 :             break;
    5294          14 :         case AT_ReAddStatistics:    /* ADD STATISTICS */
    5295          14 :             address = ATExecAddStatistics(tab, rel, (CreateStatsStmt *) cmd->def,
    5296             :                                           true, lockmode);
    5297          14 :             break;
    5298       14846 :         case AT_AddConstraint:  /* ADD CONSTRAINT */
    5299             :             /* Transform the command only during initial examination */
    5300       14846 :             if (cur_pass == AT_PASS_ADD_CONSTR)
    5301       12074 :                 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd,
    5302       12092 :                                           cmd->recurse, lockmode,
    5303             :                                           cur_pass, context);
    5304             :             /* Depending on constraint type, might be no more work to do now */
    5305       14828 :             if (cmd != NULL)
    5306             :                 address =
    5307        2754 :                     ATExecAddConstraint(wqueue, tab, rel,
    5308        2754 :                                         (Constraint *) cmd->def,
    5309        2754 :                                         cmd->recurse, false, lockmode);
    5310       14454 :             break;
    5311         144 :         case AT_ReAddConstraint:    /* Re-add pre-existing check constraint */
    5312             :             address =
    5313         144 :                 ATExecAddConstraint(wqueue, tab, rel, (Constraint *) cmd->def,
    5314             :                                     true, true, lockmode);
    5315         132 :             break;
    5316          14 :         case AT_ReAddDomainConstraint:  /* Re-add pre-existing domain check
    5317             :                                          * constraint */
    5318             :             address =
    5319          14 :                 AlterDomainAddConstraint(((AlterDomainStmt *) cmd->def)->typeName,
    5320          14 :                                          ((AlterDomainStmt *) cmd->def)->def,
    5321             :                                          NULL);
    5322           8 :             break;
    5323          54 :         case AT_ReAddComment:   /* Re-add existing comment */
    5324          54 :             address = CommentObject((CommentStmt *) cmd->def);
    5325          54 :             break;
    5326        8422 :         case AT_AddIndexConstraint: /* ADD CONSTRAINT USING INDEX */
    5327        8422 :             address = ATExecAddIndexConstraint(tab, rel, (IndexStmt *) cmd->def,
    5328             :                                                lockmode);
    5329        8410 :             break;
    5330          66 :         case AT_AlterConstraint:    /* ALTER CONSTRAINT */
    5331          66 :             address = ATExecAlterConstraint(rel, cmd, false, false, lockmode);
    5332          54 :             break;
    5333         388 :         case AT_ValidateConstraint: /* VALIDATE CONSTRAINT */
    5334         388 :             address = ATExecValidateConstraint(wqueue, rel, cmd->name, cmd->recurse,
    5335             :                                                false, lockmode);
    5336         388 :             break;
    5337         558 :         case AT_DropConstraint: /* DROP CONSTRAINT */
    5338         558 :             ATExecDropConstraint(rel, cmd->name, cmd->behavior,
    5339         558 :                                  cmd->recurse, false,
    5340         558 :                                  cmd->missing_ok, lockmode);
    5341         372 :             break;
    5342         948 :         case AT_AlterColumnType:    /* ALTER COLUMN TYPE */
    5343             :             /* parse transformation was done earlier */
    5344         948 :             address = ATExecAlterColumnType(tab, rel, cmd, lockmode);
    5345         912 :             break;
    5346         164 :         case AT_AlterColumnGenericOptions:  /* ALTER COLUMN OPTIONS */
    5347             :             address =
    5348         164 :                 ATExecAlterColumnGenericOptions(rel, cmd->name,
    5349         164 :                                                 (List *) cmd->def, lockmode);
    5350         158 :             break;
    5351        1770 :         case AT_ChangeOwner:    /* ALTER OWNER */
    5352        1764 :             ATExecChangeOwner(RelationGetRelid(rel),
    5353        1770 :                               get_rolespec_oid(cmd->newowner, false),
    5354             :                               false, lockmode);
    5355        1752 :             break;
    5356          64 :         case AT_ClusterOn:      /* CLUSTER ON */
    5357          64 :             address = ATExecClusterOn(rel, cmd->name, lockmode);
    5358          58 :             break;
    5359          18 :         case AT_DropCluster:    /* SET WITHOUT CLUSTER */
    5360          18 :             ATExecDropCluster(rel, lockmode);
    5361          12 :             break;
    5362          76 :         case AT_SetLogged:      /* SET LOGGED */
    5363             :         case AT_SetUnLogged:    /* SET UNLOGGED */
    5364          76 :             break;
    5365           6 :         case AT_DropOids:       /* SET WITHOUT OIDS */
    5366             :             /* nothing to do here, oid columns don't exist anymore */
    5367           6 :             break;
    5368          92 :         case AT_SetAccessMethod:    /* SET ACCESS METHOD */
    5369             : 
    5370             :             /*
    5371             :              * Only do this for partitioned tables, for which this is just a
    5372             :              * catalog change.  Tables with storage are handled by Phase 3.
    5373             :              */
    5374          92 :             if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&
    5375          50 :                 tab->chgAccessMethod)
    5376          44 :                 ATExecSetAccessMethodNoStorage(rel, tab->newAccessMethod);
    5377          92 :             break;
    5378         158 :         case AT_SetTableSpace:  /* SET TABLESPACE */
    5379             : 
    5380             :             /*
    5381             :              * Only do this for partitioned tables and indexes, for which this
    5382             :              * is just a catalog change.  Other relation types which have
    5383             :              * storage are handled by Phase 3.
    5384             :              */
    5385         158 :             if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
    5386         146 :                 rel->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
    5387          36 :                 ATExecSetTableSpaceNoStorage(rel, tab->newTableSpace);
    5388             : 
    5389         152 :             break;
    5390         946 :         case AT_SetRelOptions:  /* SET (...) */
    5391             :         case AT_ResetRelOptions:    /* RESET (...) */
    5392             :         case AT_ReplaceRelOptions:  /* replace entire option list */
    5393         946 :             ATExecSetRelOptions(rel, (List *) cmd->def, cmd->subtype, lockmode);
    5394         894 :             break;
    5395         122 :         case AT_EnableTrig:     /* ENABLE TRIGGER name */
    5396         122 :             ATExecEnableDisableTrigger(rel, cmd->name,
    5397             :                                        TRIGGER_FIRES_ON_ORIGIN, false,
    5398         122 :                                        cmd->recurse,
    5399             :                                        lockmode);
    5400         122 :             break;
    5401          40 :         case AT_EnableAlwaysTrig:   /* ENABLE ALWAYS TRIGGER name */
    5402          40 :             ATExecEnableDisableTrigger(rel, cmd->name,
    5403             :                                        TRIGGER_FIRES_ALWAYS, false,
    5404          40 :                                        cmd->recurse,
    5405             :                                        lockmode);
    5406          40 :             break;
    5407          16 :         case AT_EnableReplicaTrig:  /* ENABLE REPLICA TRIGGER name */
    5408          16 :             ATExecEnableDisableTrigger(rel, cmd->name,
    5409             :                                        TRIGGER_FIRES_ON_REPLICA, false,
    5410          16 :                                        cmd->recurse,
    5411             :                                        lockmode);
    5412          16 :             break;
    5413         138 :         case AT_DisableTrig:    /* DISABLE TRIGGER name */
    5414         138 :             ATExecEnableDisableTrigger(rel, cmd->name,
    5415             :                                        TRIGGER_DISABLED, false,
    5416         138 :                                        cmd->recurse,
    5417             :                                        lockmode);
    5418         138 :             break;
    5419           0 :         case AT_EnableTrigAll:  /* ENABLE TRIGGER ALL */
    5420           0 :             ATExecEnableDisableTrigger(rel, NULL,
    5421             :                                        TRIGGER_FIRES_ON_ORIGIN, false,
    5422           0 :                                        cmd->recurse,
    5423             :                                        lockmode);
    5424           0 :             break;
    5425          12 :         case AT_DisableTrigAll: /* DISABLE TRIGGER ALL */
    5426          12 :             ATExecEnableDisableTrigger(rel, NULL,
    5427             :                                        TRIGGER_DISABLED, false,
    5428          12 :                                        cmd->recurse,
    5429             :                                        lockmode);
    5430          12 :             break;
    5431           0 :         case AT_EnableTrigUser: /* ENABLE TRIGGER USER */
    5432           0 :             ATExecEnableDisableTrigger(rel, NULL,
    5433             :                                        TRIGGER_FIRES_ON_ORIGIN, true,
    5434           0 :                                        cmd->recurse,
    5435             :                                        lockmode);
    5436           0 :             break;
    5437          12 :         case AT_DisableTrigUser:    /* DISABLE TRIGGER USER */
    5438          12 :             ATExecEnableDisableTrigger(rel, NULL,
    5439             :                                        TRIGGER_DISABLED, true,
    5440          12 :                                        cmd->recurse,
    5441             :                                        lockmode);
    5442          12 :             break;
    5443             : 
    5444           8 :         case AT_EnableRule:     /* ENABLE RULE name */
    5445           8 :             ATExecEnableDisableRule(rel, cmd->name,
    5446             :                                     RULE_FIRES_ON_ORIGIN, lockmode);
    5447           8 :             break;
    5448           0 :         case AT_EnableAlwaysRule:   /* ENABLE ALWAYS RULE name */
    5449           0 :             ATExecEnableDisableRule(rel, cmd->name,
    5450             :                                     RULE_FIRES_ALWAYS, lockmode);
    5451           0 :             break;
    5452           6 :         case AT_EnableReplicaRule:  /* ENABLE REPLICA RULE name */
    5453           6 :             ATExecEnableDisableRule(rel, cmd->name,
    5454             :                                     RULE_FIRES_ON_REPLICA, lockmode);
    5455           6 :             break;
    5456          32 :         case AT_DisableRule:    /* DISABLE RULE name */
    5457          32 :             ATExecEnableDisableRule(rel, cmd->name,
    5458             :                                     RULE_DISABLED, lockmode);
    5459          32 :             break;
    5460             : 
    5461         254 :         case AT_AddInherit:
    5462         254 :             address = ATExecAddInherit(rel, (RangeVar *) cmd->def, lockmode);
    5463         182 :             break;
    5464          38 :         case AT_DropInherit:
    5465          38 :             address = ATExecDropInherit(rel, (RangeVar *) cmd->def, lockmode);
    5466          32 :             break;
    5467          66 :         case AT_AddOf:
    5468          66 :             address = ATExecAddOf(rel, (TypeName *) cmd->def, lockmode);
    5469          30 :             break;
    5470           6 :         case AT_DropOf:
    5471           6 :             ATExecDropOf(rel, lockmode);
    5472           6 :             break;
    5473         436 :         case AT_ReplicaIdentity:
    5474         436 :             ATExecReplicaIdentity(rel, (ReplicaIdentityStmt *) cmd->def, lockmode);
    5475         388 :             break;
    5476         284 :         case AT_EnableRowSecurity:
    5477         284 :             ATExecSetRowSecurity(rel, true);
    5478         284 :             break;
    5479          10 :         case AT_DisableRowSecurity:
    5480          10 :             ATExecSetRowSecurity(rel, false);
    5481          10 :             break;
    5482          88 :         case AT_ForceRowSecurity:
    5483          88 :             ATExecForceNoForceRowSecurity(rel, true);
    5484          88 :             break;
    5485          32 :         case AT_NoForceRowSecurity:
    5486          32 :             ATExecForceNoForceRowSecurity(rel, false);
    5487          32 :             break;
    5488          50 :         case AT_GenericOptions:
    5489          50 :             ATExecGenericOptions(rel, (List *) cmd->def);
    5490          48 :             break;
    5491        2612 :         case AT_AttachPartition:
    5492        2612 :             cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
    5493             :                                       cur_pass, context);
    5494             :             Assert(cmd != NULL);
    5495        2582 :             if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
    5496        2190 :                 address = ATExecAttachPartition(wqueue, rel, (PartitionCmd *) cmd->def,
    5497             :                                                 context);
    5498             :             else
    5499         392 :                 address = ATExecAttachPartitionIdx(wqueue, rel,
    5500         392 :                                                    ((PartitionCmd *) cmd->def)->name);
    5501        2258 :             break;
    5502         546 :         case AT_DetachPartition:
    5503         546 :             cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
    5504             :                                       cur_pass, context);
    5505             :             Assert(cmd != NULL);
    5506             :             /* ATPrepCmd ensures it must be a table */
    5507             :             Assert(rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
    5508         540 :             address = ATExecDetachPartition(wqueue, tab, rel,
    5509         540 :                                             ((PartitionCmd *) cmd->def)->name,
    5510         540 :                                             ((PartitionCmd *) cmd->def)->concurrent);
    5511         410 :             break;
    5512          14 :         case AT_DetachPartitionFinalize:
    5513          14 :             address = ATExecDetachPartitionFinalize(rel, ((PartitionCmd *) cmd->def)->name);
    5514          14 :             break;
    5515         294 :         case AT_SplitPartition:
    5516         294 :             cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
    5517             :                                       cur_pass, context);
    5518             :             Assert(cmd != NULL);
    5519             :             Assert(rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
    5520         156 :             ATExecSplitPartition(wqueue, tab, rel, (PartitionCmd *) cmd->def,
    5521             :                                  context);
    5522         150 :             break;
    5523         180 :         case AT_MergePartitions:
    5524         180 :             cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
    5525             :                                       cur_pass, context);
    5526             :             Assert(cmd != NULL);
    5527             :             Assert(rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
    5528         114 :             ATExecMergePartitions(wqueue, tab, rel, (PartitionCmd *) cmd->def,
    5529             :                                   context);
    5530         102 :             break;
    5531           0 :         default:                /* oops */
    5532           0 :             elog(ERROR, "unrecognized alter table type: %d",
    5533             :                  (int) cmd->subtype);
    5534             :             break;
    5535             :     }
    5536             : 
    5537             :     /*
    5538             :      * Report the subcommand to interested event triggers.
    5539             :      */
    5540       45910 :     if (cmd)
    5541       33836 :         EventTriggerCollectAlterTableSubcmd((Node *) cmd, address);
    5542             : 
    5543             :     /*
    5544             :      * Bump the command counter to ensure the next subcommand in the sequence
    5545             :      * can see the changes so far
    5546             :      */
    5547       45910 :     CommandCounterIncrement();
    5548       45910 : }
    5549             : 
    5550             : /*
    5551             :  * ATParseTransformCmd: perform parse transformation for one subcommand
    5552             :  *
    5553             :  * Returns the transformed subcommand tree, if there is one, else NULL.
    5554             :  *
    5555             :  * The parser may hand back additional AlterTableCmd(s) and/or other
    5556             :  * utility statements, either before or after the original subcommand.
    5557             :  * Other AlterTableCmds are scheduled into the appropriate slot of the
    5558             :  * AlteredTableInfo (they had better be for later passes than the current one).
    5559             :  * Utility statements that are supposed to happen before the AlterTableCmd
    5560             :  * are executed immediately.  Those that are supposed to happen afterwards
    5561             :  * are added to the tab->afterStmts list to be done at the very end.
    5562             :  */
    5563             : static AlterTableCmd *
    5564       18888 : ATParseTransformCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
    5565             :                     AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode,
    5566             :                     AlterTablePass cur_pass, AlterTableUtilityContext *context)
    5567             : {
    5568       18888 :     AlterTableCmd *newcmd = NULL;
    5569       18888 :     AlterTableStmt *atstmt = makeNode(AlterTableStmt);
    5570             :     List       *beforeStmts;
    5571             :     List       *afterStmts;
    5572             :     ListCell   *lc;
    5573             : 
    5574             :     /* Gin up an AlterTableStmt with just this subcommand and this table */
    5575       18888 :     atstmt->relation =
    5576       18888 :         makeRangeVar(get_namespace_name(RelationGetNamespace(rel)),
    5577       18888 :                      pstrdup(RelationGetRelationName(rel)),
    5578             :                      -1);
    5579       18888 :     atstmt->relation->inh = recurse;
    5580       18888 :     atstmt->cmds = list_make1(cmd);
    5581       18888 :     atstmt->objtype = OBJECT_TABLE; /* needn't be picky here */
    5582       18888 :     atstmt->missing_ok = false;
    5583             : 
    5584             :     /* Transform the AlterTableStmt */
    5585       18888 :     atstmt = transformAlterTableStmt(RelationGetRelid(rel),
    5586             :                                      atstmt,
    5587             :                                      context->queryString,
    5588             :                                      &beforeStmts,
    5589             :                                      &afterStmts);
    5590             : 
    5591             :     /* Execute any statements that should happen before these subcommand(s) */
    5592       19096 :     foreach(lc, beforeStmts)
    5593             :     {
    5594         478 :         Node       *stmt = (Node *) lfirst(lc);
    5595             : 
    5596         478 :         ProcessUtilityForAlterTable(stmt, context);
    5597         466 :         CommandCounterIncrement();
    5598             :     }
    5599             : 
    5600             :     /* Examine the transformed subcommands and schedule them appropriately */
    5601       44310 :     foreach(lc, atstmt->cmds)
    5602             :     {
    5603       25692 :         AlterTableCmd *cmd2 = lfirst_node(AlterTableCmd, lc);
    5604             :         AlterTablePass pass;
    5605             : 
    5606             :         /*
    5607             :          * This switch need only cover the subcommand types that can be added
    5608             :          * by parse_utilcmd.c; otherwise, we'll use the default strategy of
    5609             :          * executing the subcommand immediately, as a substitute for the
    5610             :          * original subcommand.  (Note, however, that this does cause
    5611             :          * AT_AddConstraint subcommands to be rescheduled into later passes,
    5612             :          * which is important for index and foreign key constraints.)
    5613             :          *
    5614             :          * We assume we needn't do any phase-1 checks for added subcommands.
    5615             :          */
    5616       25692 :         switch (cmd2->subtype)
    5617             :         {
    5618        7014 :             case AT_SetNotNull:
    5619             :                 /* Need command-specific recursion decision */
    5620        7014 :                 ATPrepSetNotNull(wqueue, rel, cmd2,
    5621             :                                  recurse, false,
    5622             :                                  lockmode, context);
    5623        7014 :                 pass = AT_PASS_COL_ATTRS;
    5624        7014 :                 break;
    5625         958 :             case AT_AddIndex:
    5626             :                 /* This command never recurses */
    5627             :                 /* No command-specific prep needed */
    5628         958 :                 pass = AT_PASS_ADD_INDEX;
    5629         958 :                 break;
    5630        8422 :             case AT_AddIndexConstraint:
    5631             :                 /* This command never recurses */
    5632             :                 /* No command-specific prep needed */
    5633        8422 :                 pass = AT_PASS_ADD_INDEXCONSTR;
    5634        8422 :                 break;
    5635        2754 :             case AT_AddConstraint:
    5636             :                 /* Recursion occurs during execution phase */
    5637        2754 :                 if (recurse)
    5638        2712 :                     cmd2->recurse = true;
    5639        2754 :                 switch (castNode(Constraint, cmd2->def)->contype)
    5640             :                 {
    5641           0 :                     case CONSTR_PRIMARY:
    5642             :                     case CONSTR_UNIQUE:
    5643             :                     case CONSTR_EXCLUSION:
    5644           0 :                         pass = AT_PASS_ADD_INDEXCONSTR;
    5645           0 :                         break;
    5646        2754 :                     default:
    5647        2754 :                         pass = AT_PASS_ADD_OTHERCONSTR;
    5648        2754 :                         break;
    5649             :                 }
    5650        2754 :                 break;
    5651           0 :             case AT_AlterColumnGenericOptions:
    5652             :                 /* This command never recurses */
    5653             :                 /* No command-specific prep needed */
    5654           0 :                 pass = AT_PASS_MISC;
    5655           0 :                 break;
    5656        6544 :             default:
    5657        6544 :                 pass = cur_pass;
    5658        6544 :                 break;
    5659             :         }
    5660             : 
    5661       25692 :         if (pass < cur_pass)
    5662             :         {
    5663             :             /* Cannot schedule into a pass we already finished */
    5664           0 :             elog(ERROR, "ALTER TABLE scheduling failure: too late for pass %d",
    5665             :                  pass);
    5666             :         }
    5667       25692 :         else if (pass > cur_pass)
    5668             :         {
    5669             :             /* OK, queue it up for later */
    5670       19148 :             tab->subcmds[pass] = lappend(tab->subcmds[pass], cmd2);
    5671             :         }
    5672             :         else
    5673             :         {
    5674             :             /*
    5675             :              * We should see at most one subcommand for the current pass,
    5676             :              * which is the transformed version of the original subcommand.
    5677             :              */
    5678        6544 :             if (newcmd == NULL && cmd->subtype == cmd2->subtype)
    5679             :             {
    5680             :                 /* Found the transformed version of our subcommand */
    5681        6544 :                 newcmd = cmd2;
    5682             :             }
    5683             :             else
    5684           0 :                 elog(ERROR, "ALTER TABLE scheduling failure: bogus item for pass %d",
    5685             :                      pass);
    5686             :         }
    5687             :     }
    5688             : 
    5689             :     /* Queue up any after-statements to happen at the end */
    5690       18618 :     tab->afterStmts = list_concat(tab->afterStmts, afterStmts);
    5691             : 
    5692       18618 :     return newcmd;
    5693             : }
    5694             : 
    5695             : /*
    5696             :  * ATRewriteTables: ALTER TABLE phase 3
    5697             :  */
    5698             : static void
    5699       24616 : ATRewriteTables(AlterTableStmt *parsetree, List **wqueue, LOCKMODE lockmode,
    5700             :                 AlterTableUtilityContext *context)
    5701             : {
    5702             :     ListCell   *ltab;
    5703             : 
    5704             :     /* Go through each table that needs to be checked or rewritten */
    5705       52648 :     foreach(ltab, *wqueue)
    5706             :     {
    5707       28276 :         AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
    5708             : 
    5709             :         /* Relations without storage may be ignored here */
    5710       28276 :         if (!RELKIND_HAS_STORAGE(tab->relkind))
    5711        5658 :             continue;
    5712             : 
    5713             :         /*
    5714             :          * If we change column data types, the operation has to be propagated
    5715             :          * to tables that use this table's rowtype as a column type.
    5716             :          * tab->newvals will also be non-NULL in the case where we're adding a
    5717             :          * column with a default.  We choose to forbid that case as well,
    5718             :          * since composite types might eventually support defaults.
    5719             :          *
    5720             :          * (Eventually we'll probably need to check for composite type
    5721             :          * dependencies even when we're just scanning the table without a
    5722             :          * rewrite, but at the moment a composite type does not enforce any
    5723             :          * constraints, so it's not necessary/appropriate to enforce them just
    5724             :          * during ALTER.)
    5725             :          */
    5726       22618 :         if (tab->newvals != NIL || tab->rewrite > 0)
    5727             :         {
    5728             :             Relation    rel;
    5729             : 
    5730        1454 :             rel = table_open(tab->relid, NoLock);
    5731        1454 :             find_composite_type_dependencies(rel->rd_rel->reltype, rel, NULL);
    5732        1442 :             table_close(rel, NoLock);
    5733             :         }
    5734             : 
    5735             :         /*
    5736             :          * We only need to rewrite the table if at least one column needs to
    5737             :          * be recomputed, or we are changing its persistence or access method.
    5738             :          *
    5739             :          * There are two reasons for requiring a rewrite when changing
    5740             :          * persistence: on one hand, we need to ensure that the buffers
    5741             :          * belonging to each of the two relations are marked with or without
    5742             :          * BM_PERMANENT properly.  On the other hand, since rewriting creates
    5743             :          * and assigns a new relfilenumber, we automatically create or drop an
    5744             :          * init fork for the relation as appropriate.
    5745             :          */
    5746       22606 :         if (tab->rewrite > 0 && tab->relkind != RELKIND_SEQUENCE)
    5747         830 :         {
    5748             :             /* Build a temporary relation and copy data */
    5749             :             Relation    OldHeap;
    5750             :             Oid         OIDNewHeap;
    5751             :             Oid         NewAccessMethod;
    5752             :             Oid         NewTableSpace;
    5753             :             char        persistence;
    5754             : 
    5755         868 :             OldHeap = table_open(tab->relid, NoLock);
    5756             : 
    5757             :             /*
    5758             :              * We don't support rewriting of system catalogs; there are too
    5759             :              * many corner cases and too little benefit.  In particular this
    5760             :              * is certainly not going to work for mapped catalogs.
    5761             :              */
    5762         868 :             if (IsSystemRelation(OldHeap))
    5763           0 :                 ereport(ERROR,
    5764             :                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    5765             :                          errmsg("cannot rewrite system relation \"%s\"",
    5766             :                                 RelationGetRelationName(OldHeap))));
    5767             : 
    5768         868 :             if (RelationIsUsedAsCatalogTable(OldHeap))
    5769           2 :                 ereport(ERROR,
    5770             :                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    5771             :                          errmsg("cannot rewrite table \"%s\" used as a catalog table",
    5772             :                                 RelationGetRelationName(OldHeap))));
    5773             : 
    5774             :             /*
    5775             :              * Don't allow rewrite on temp tables of other backends ... their
    5776             :              * local buffer manager is not going to cope.  (This is redundant
    5777             :              * with the check in CheckAlterTableIsSafe, but for safety we'll
    5778             :              * check here too.)
    5779             :              */
    5780         866 :             if (RELATION_IS_OTHER_TEMP(OldHeap))
    5781           0 :                 ereport(ERROR,
    5782             :                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    5783             :                          errmsg("cannot rewrite temporary tables of other sessions")));
    5784             : 
    5785             :             /*
    5786             :              * Select destination tablespace (same as original unless user
    5787             :              * requested a change)
    5788             :              */
    5789         866 :             if (tab->newTableSpace)
    5790           0 :                 NewTableSpace = tab->newTableSpace;
    5791             :             else
    5792         866 :                 NewTableSpace = OldHeap->rd_rel->reltablespace;
    5793             : 
    5794             :             /*
    5795             :              * Select destination access method (same as original unless user
    5796             :              * requested a change)
    5797             :              */
    5798         866 :             if (tab->chgAccessMethod)
    5799          36 :                 NewAccessMethod = tab->newAccessMethod;
    5800             :             else
    5801         830 :                 NewAccessMethod = OldHeap->rd_rel->relam;
    5802             : 
    5803             :             /*
    5804             :              * Select persistence of transient table (same as original unless
    5805             :              * user requested a change)
    5806             :              */
    5807         866 :             persistence = tab->chgPersistence ?
    5808         814 :                 tab->newrelpersistence : OldHeap->rd_rel->relpersistence;
    5809             : 
    5810         866 :             table_close(OldHeap, NoLock);
    5811             : 
    5812             :             /*
    5813             :              * Fire off an Event Trigger now, before actually rewriting the
    5814             :              * table.
    5815             :              *
    5816             :              * We don't support Event Trigger for nested commands anywhere,
    5817             :              * here included, and parsetree is given NULL when coming from
    5818             :              * AlterTableInternal.
    5819             :              *
    5820             :              * And fire it only once.
    5821             :              */
    5822         866 :             if (parsetree)
    5823         866 :                 EventTriggerTableRewrite((Node *) parsetree,
    5824             :                                          tab->relid,
    5825             :                                          tab->rewrite);
    5826             : 
    5827             :             /*
    5828             :              * Create transient table that will receive the modified data.
    5829             :              *
    5830             :              * Ensure it is marked correctly as logged or unlogged.  We have
    5831             :              * to do this here so that buffers for the new relfilenumber will
    5832             :              * have the right persistence set, and at the same time ensure
    5833             :              * that the original filenumbers's buffers will get read in with
    5834             :              * the correct setting (i.e. the original one).  Otherwise a
    5835             :              * rollback after the rewrite would possibly result with buffers
    5836             :              * for the original filenumbers having the wrong persistence
    5837             :              * setting.
    5838             :              *
    5839             :              * NB: This relies on swap_relation_files() also swapping the
    5840             :              * persistence. That wouldn't work for pg_class, but that can't be
    5841             :              * unlogged anyway.
    5842             :              */
    5843         860 :             OIDNewHeap = make_new_heap(tab->relid, NewTableSpace, NewAccessMethod,
    5844             :                                        persistence, lockmode);
    5845             : 
    5846             :             /*
    5847             :              * Copy the heap data into the new table with the desired
    5848             :              * modifications, and test the current data within the table
    5849             :              * against new constraints generated by ALTER TABLE commands.
    5850             :              */
    5851         860 :             ATRewriteTable(tab, OIDNewHeap, lockmode);
    5852             : 
    5853             :             /*
    5854             :              * Swap the physical files of the old and new heaps, then rebuild
    5855             :              * indexes and discard the old heap.  We can use RecentXmin for
    5856             :              * the table's new relfrozenxid because we rewrote all the tuples
    5857             :              * in ATRewriteTable, so no older Xid remains in the table.  Also,
    5858             :              * we never try to swap toast tables by content, since we have no
    5859             :              * interest in letting this code work on system catalogs.
    5860             :              */
    5861         836 :             finish_heap_swap(tab->relid, OIDNewHeap,
    5862             :                              false, false, true,
    5863         836 :                              !OidIsValid(tab->newTableSpace),
    5864             :                              RecentXmin,
    5865             :                              ReadNextMultiXactId(),
    5866             :                              persistence);
    5867             : 
    5868         830 :             InvokeObjectPostAlterHook(RelationRelationId, tab->relid, 0);
    5869             :         }
    5870       21738 :         else if (tab->rewrite > 0 && tab->relkind == RELKIND_SEQUENCE)
    5871             :         {
    5872          12 :             if (tab->chgPersistence)
    5873          12 :                 SequenceChangePersistence(tab->relid, tab->newrelpersistence);
    5874             :         }
    5875             :         else
    5876             :         {
    5877             :             /*
    5878             :              * If required, test the current data within the table against new
    5879             :              * constraints generated by ALTER TABLE commands, but don't
    5880             :              * rebuild data.
    5881             :              */
    5882       21726 :             if (tab->constraints != NIL || tab->verify_new_notnull ||
    5883       19540 :                 tab->partition_constraint != NULL)
    5884        3996 :                 ATRewriteTable(tab, InvalidOid, lockmode);
    5885             : 
    5886             :             /*
    5887             :              * If we had SET TABLESPACE but no reason to reconstruct tuples,
    5888             :              * just do a block-by-block copy.
    5889             :              */
    5890       21532 :             if (tab->newTableSpace)
    5891         122 :                 ATExecSetTableSpace(tab->relid, tab->newTableSpace, lockmode);
    5892             :         }
    5893             : 
    5894             :         /*
    5895             :          * Also change persistence of owned sequences, so that it matches the
    5896             :          * table persistence.
    5897             :          */
    5898       22374 :         if (tab->chgPersistence)
    5899             :         {
    5900          64 :             List       *seqlist = getOwnedSequences(tab->relid);
    5901             :             ListCell   *lc;
    5902             : 
    5903         112 :             foreach(lc, seqlist)
    5904             :             {
    5905          48 :                 Oid         seq_relid = lfirst_oid(lc);
    5906             : 
    5907          48 :                 SequenceChangePersistence(seq_relid, tab->newrelpersistence);
    5908             :             }
    5909             :         }
    5910             :     }
    5911             : 
    5912             :     /*
    5913             :      * Foreign key constraints are checked in a final pass, since (a) it's
    5914             :      * generally best to examine each one separately, and (b) it's at least
    5915             :      * theoretically possible that we have changed both relations of the
    5916             :      * foreign key, and we'd better have finished both rewrites before we try
    5917             :      * to read the tables.
    5918             :      */
    5919       52212 :     foreach(ltab, *wqueue)
    5920             :     {
    5921       27908 :         AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
    5922       27908 :         Relation    rel = NULL;
    5923             :         ListCell   *lcon;
    5924             : 
    5925             :         /* Relations without storage may be ignored here too */
    5926       27908 :         if (!RELKIND_HAS_STORAGE(tab->relkind))
    5927        5584 :             continue;
    5928             : 
    5929       23858 :         foreach(lcon, tab->constraints)
    5930             :         {
    5931        1602 :             NewConstraint *con = lfirst(lcon);
    5932             : 
    5933        1602 :             if (con->contype == CONSTR_FOREIGN)
    5934             :             {
    5935         944 :                 Constraint *fkconstraint = (Constraint *) con->qual;
    5936             :                 Relation    refrel;
    5937             : 
    5938         944 :                 if (rel == NULL)
    5939             :                 {
    5940             :                     /* Long since locked, no need for another */
    5941         932 :                     rel = table_open(tab->relid, NoLock);
    5942             :                 }
    5943             : 
    5944         944 :                 refrel = table_open(con->refrelid, RowShareLock);
    5945             : 
    5946         944 :                 validateForeignKeyConstraint(fkconstraint->conname, rel, refrel,
    5947             :                                              con->refindid,
    5948             :                                              con->conid);
    5949             : 
    5950             :                 /*
    5951             :                  * No need to mark the constraint row as validated, we did
    5952             :                  * that when we inserted the row earlier.
    5953             :                  */
    5954             : 
    5955         876 :                 table_close(refrel, NoLock);
    5956             :             }
    5957             :         }
    5958             : 
    5959       22256 :         if (rel)
    5960         864 :             table_close(rel, NoLock);
    5961             :     }
    5962             : 
    5963             :     /* Finally, run any afterStmts that were queued up */
    5964       52106 :     foreach(ltab, *wqueue)
    5965             :     {
    5966       27802 :         AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
    5967             :         ListCell   *lc;
    5968             : 
    5969       27888 :         foreach(lc, tab->afterStmts)
    5970             :         {
    5971          86 :             Node       *stmt = (Node *) lfirst(lc);
    5972             : 
    5973          86 :             ProcessUtilityForAlterTable(stmt, context);
    5974          86 :             CommandCounterIncrement();
    5975             :         }
    5976             :     }
    5977       24304 : }
    5978             : 
    5979             : /*
    5980             :  * ATRewriteTable: scan or rewrite one table
    5981             :  *
    5982             :  * OIDNewHeap is InvalidOid if we don't need to rewrite
    5983             :  */
    5984             : static void
    5985        4856 : ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
    5986             : {
    5987             :     Relation    oldrel;
    5988             :     Relation    newrel;
    5989             :     TupleDesc   oldTupDesc;
    5990             :     TupleDesc   newTupDesc;
    5991        4856 :     bool        needscan = false;
    5992             :     List       *notnull_attrs;
    5993             :     int         i;
    5994             :     ListCell   *l;
    5995             :     EState     *estate;
    5996             :     CommandId   mycid;
    5997             :     BulkInsertState bistate;
    5998             :     int         ti_options;
    5999        4856 :     ExprState  *partqualstate = NULL;
    6000             : 
    6001             :     /*
    6002             :      * Open the relation(s).  We have surely already locked the existing
    6003             :      * table.
    6004             :      */
    6005        4856 :     oldrel = table_open(tab->relid, NoLock);
    6006        4856 :     oldTupDesc = tab->oldDesc;
    6007        4856 :     newTupDesc = RelationGetDescr(oldrel);  /* includes all mods */
    6008             : 
    6009        4856 :     if (OidIsValid(OIDNewHeap))
    6010         860 :         newrel = table_open(OIDNewHeap, lockmode);
    6011             :     else
    6012        3996 :         newrel = NULL;
    6013             : 
    6014             :     /*
    6015             :      * Prepare a BulkInsertState and options for table_tuple_insert.  The FSM
    6016             :      * is empty, so don't bother using it.
    6017             :      */
    6018        4856 :     if (newrel)
    6019             :     {
    6020         860 :         mycid = GetCurrentCommandId(true);
    6021         860 :         bistate = GetBulkInsertState();
    6022         860 :         ti_options = TABLE_INSERT_SKIP_FSM;
    6023             :     }
    6024             :     else
    6025             :     {
    6026             :         /* keep compiler quiet about using these uninitialized */
    6027        3996 :         mycid = 0;
    6028        3996 :         bistate = NULL;
    6029        3996 :         ti_options = 0;
    6030             :     }
    6031             : 
    6032             :     /*
    6033             :      * Generate the constraint and default execution states
    6034             :      */
    6035             : 
    6036        4856 :     estate = CreateExecutorState();
    6037             : 
    6038             :     /* Build the needed expression execution states */
    6039        6554 :     foreach(l, tab->constraints)
    6040             :     {
    6041        1698 :         NewConstraint *con = lfirst(l);
    6042             : 
    6043        1698 :         switch (con->contype)
    6044             :         {
    6045         748 :             case CONSTR_CHECK:
    6046         748 :                 needscan = true;
    6047         748 :                 con->qualstate = ExecPrepareExpr((Expr *) con->qual, estate);
    6048         748 :                 break;
    6049         950 :             case CONSTR_FOREIGN:
    6050             :                 /* Nothing to do here */
    6051         950 :                 break;
    6052           0 :             default:
    6053           0 :                 elog(ERROR, "unrecognized constraint type: %d",
    6054             :                      (int) con->contype);
    6055             :         }
    6056             :     }
    6057             : 
    6058             :     /* Build expression execution states for partition check quals */
    6059        4856 :     if (tab->partition_constraint)
    6060             :     {
    6061        1940 :         needscan = true;
    6062        1940 :         partqualstate = ExecPrepareExpr(tab->partition_constraint, estate);
    6063             :     }
    6064             : 
    6065        5712 :     foreach(l, tab->newvals)
    6066             :     {
    6067         856 :         NewColumnValue *ex = lfirst(l);
    6068             : 
    6069             :         /* expr already planned */
    6070         856 :         ex->exprstate = ExecInitExpr((Expr *) ex->expr, NULL);
    6071             :     }
    6072             : 
    6073        4856 :     notnull_attrs = NIL;
    6074        4856 :     if (newrel || tab->verify_new_notnull)
    6075             :     {
    6076             :         /*
    6077             :          * If we are rebuilding the tuples OR if we added any new but not
    6078             :          * verified not-null constraints, check all not-null constraints. This
    6079             :          * is a bit of overkill but it minimizes risk of bugs, and
    6080             :          * heap_attisnull is a pretty cheap test anyway.
    6081             :          */
    6082        5600 :         for (i = 0; i < newTupDesc->natts; i++)
    6083             :         {
    6084        4106 :             Form_pg_attribute attr = TupleDescAttr(newTupDesc, i);
    6085             : 
    6086        4106 :             if (attr->attnotnull && !attr->attisdropped)
    6087        1528 :                 notnull_attrs = lappend_int(notnull_attrs, i);
    6088             :         }
    6089        1494 :         if (notnull_attrs)
    6090        1086 :             needscan = true;
    6091             :     }
    6092             : 
    6093        4856 :     if (newrel || needscan)
    6094             :     {
    6095             :         ExprContext *econtext;
    6096             :         TupleTableSlot *oldslot;
    6097             :         TupleTableSlot *newslot;
    6098             :         TableScanDesc scan;
    6099             :         MemoryContext oldCxt;
    6100        4090 :         List       *dropped_attrs = NIL;
    6101             :         ListCell   *lc;
    6102             :         Snapshot    snapshot;
    6103             : 
    6104        4090 :         if (newrel)
    6105         860 :             ereport(DEBUG1,
    6106             :                     (errmsg_internal("rewriting table \"%s\"",
    6107             :                                      RelationGetRelationName(oldrel))));
    6108             :         else
    6109        3230 :             ereport(DEBUG1,
    6110             :                     (errmsg_internal("verifying table \"%s\"",
    6111             :                                      RelationGetRelationName(oldrel))));
    6112             : 
    6113        4090 :         if (newrel)
    6114             :         {
    6115             :             /*
    6116             :              * All predicate locks on the tuples or pages are about to be made
    6117             :              * invalid, because we move tuples around.  Promote them to
    6118             :              * relation locks.
    6119             :              */
    6120         860 :             TransferPredicateLocksToHeapRelation(oldrel);
    6121             :         }
    6122             : 
    6123        4090 :         econtext = GetPerTupleExprContext(estate);
    6124             : 
    6125             :         /*
    6126             :          * Create necessary tuple slots. When rewriting, two slots are needed,
    6127             :          * otherwise one suffices. In the case where one slot suffices, we
    6128             :          * need to use the new tuple descriptor, otherwise some constraints
    6129             :          * can't be evaluated.  Note that even when the tuple layout is the
    6130             :          * same and no rewrite is required, the tupDescs might not be
    6131             :          * (consider ADD COLUMN without a default).
    6132             :          */
    6133        4090 :         if (tab->rewrite)
    6134             :         {
    6135             :             Assert(newrel != NULL);
    6136         860 :             oldslot = MakeSingleTupleTableSlot(oldTupDesc,
    6137             :                                                table_slot_callbacks(oldrel));
    6138         860 :             newslot = MakeSingleTupleTableSlot(newTupDesc,
    6139             :                                                table_slot_callbacks(newrel));
    6140             : 
    6141             :             /*
    6142             :              * Set all columns in the new slot to NULL initially, to ensure
    6143             :              * columns added as part of the rewrite are initialized to NULL.
    6144             :              * That is necessary as tab->newvals will not contain an
    6145             :              * expression for columns with a NULL default, e.g. when adding a
    6146             :              * column without a default together with a column with a default
    6147             :              * requiring an actual rewrite.
    6148             :              */
    6149         860 :             ExecStoreAllNullTuple(newslot);
    6150             :         }
    6151             :         else
    6152             :         {
    6153        3230 :             oldslot = MakeSingleTupleTableSlot(newTupDesc,
    6154             :                                                table_slot_callbacks(oldrel));
    6155        3230 :             newslot = NULL;
    6156             :         }
    6157             : 
    6158             :         /*
    6159             :          * Any attributes that are dropped according to the new tuple
    6160             :          * descriptor can be set to NULL. We precompute the list of dropped
    6161             :          * attributes to avoid needing to do so in the per-tuple loop.
    6162             :          */
    6163       14516 :         for (i = 0; i < newTupDesc->natts; i++)
    6164             :         {
    6165       10426 :             if (TupleDescAttr(newTupDesc, i)->attisdropped)
    6166         790 :                 dropped_attrs = lappend_int(dropped_attrs, i);
    6167             :         }
    6168             : 
    6169             :         /*
    6170             :          * Scan through the rows, generating a new row if needed and then
    6171             :          * checking all the constraints.
    6172             :          */
    6173        4090 :         snapshot = RegisterSnapshot(GetLatestSnapshot());
    6174        4090 :         scan = table_beginscan(oldrel, snapshot, 0, NULL);
    6175             : 
    6176             :         /*
    6177             :          * Switch to per-tuple memory context and reset it for each tuple
    6178             :          * produced, so we don't leak memory.
    6179             :          */
    6180        4090 :         oldCxt = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
    6181             : 
    6182      766644 :         while (table_scan_getnextslot(scan, ForwardScanDirection, oldslot))
    6183             :         {
    6184             :             TupleTableSlot *insertslot;
    6185             : 
    6186      762772 :             if (tab->rewrite > 0)
    6187             :             {
    6188             :                 /* Extract data from old tuple */
    6189       97564 :                 slot_getallattrs(oldslot);
    6190       97564 :                 ExecClearTuple(newslot);
    6191             : 
    6192             :                 /* copy attributes */
    6193       97564 :                 memcpy(newslot->tts_values, oldslot->tts_values,
    6194       97564 :                        sizeof(Datum) * oldslot->tts_nvalid);
    6195       97564 :                 memcpy(newslot->tts_isnull, oldslot->tts_isnull,
    6196       97564 :                        sizeof(bool) * oldslot->tts_nvalid);
    6197             : 
    6198             :                 /* Set dropped attributes to null in new tuple */
    6199       97650 :                 foreach(lc, dropped_attrs)
    6200          86 :                     newslot->tts_isnull[lfirst_int(lc)] = true;
    6201             : 
    6202             :                 /*
    6203             :                  * Constraints and GENERATED expressions might reference the
    6204             :                  * tableoid column, so fill tts_tableOid with the desired
    6205             :                  * value.  (We must do this each time, because it gets
    6206             :                  * overwritten with newrel's OID during storing.)
    6207             :                  */
    6208       97564 :                 newslot->tts_tableOid = RelationGetRelid(oldrel);
    6209             : 
    6210             :                 /*
    6211             :                  * Process supplied expressions to replace selected columns.
    6212             :                  *
    6213             :                  * First, evaluate expressions whose inputs come from the old
    6214             :                  * tuple.
    6215             :                  */
    6216       97564 :                 econtext->ecxt_scantuple = oldslot;
    6217             : 
    6218      200948 :                 foreach(l, tab->newvals)
    6219             :                 {
    6220      103396 :                     NewColumnValue *ex = lfirst(l);
    6221             : 
    6222      103396 :                     if (ex->is_generated)
    6223         150 :                         continue;
    6224             : 
    6225      103246 :                     newslot->tts_values[ex->attnum - 1]
    6226      103234 :                         = ExecEvalExpr(ex->exprstate,
    6227             :                                        econtext,
    6228      103246 :                                        &newslot->tts_isnull[ex->attnum - 1]);
    6229             :                 }
    6230             : 
    6231       97552 :                 ExecStoreVirtualTuple(newslot);
    6232             : 
    6233             :                 /*
    6234             :                  * Now, evaluate any expressions whose inputs come from the
    6235             :                  * new tuple.  We assume these columns won't reference each
    6236             :                  * other, so that there's no ordering dependency.
    6237             :                  */
    6238       97552 :                 econtext->ecxt_scantuple = newslot;
    6239             : 
    6240      200936 :                 foreach(l, tab->newvals)
    6241             :                 {
    6242      103384 :                     NewColumnValue *ex = lfirst(l);
    6243             : 
    6244      103384 :                     if (!ex->is_generated)
    6245      103234 :                         continue;
    6246             : 
    6247         150 :                     newslot->tts_values[ex->attnum - 1]
    6248         150 :                         = ExecEvalExpr(ex->exprstate,
    6249             :                                        econtext,
    6250         150 :                                        &newslot->tts_isnull[ex->attnum - 1]);
    6251             :                 }
    6252             : 
    6253       97552 :                 insertslot = newslot;
    6254             :             }
    6255             :             else
    6256             :             {
    6257             :                 /*
    6258             :                  * If there's no rewrite, old and new table are guaranteed to
    6259             :                  * have the same AM, so we can just use the old slot to verify
    6260             :                  * new constraints etc.
    6261             :                  */
    6262      665208 :                 insertslot = oldslot;
    6263             :             }
    6264             : 
    6265             :             /* Now check any constraints on the possibly-changed tuple */
    6266      762760 :             econtext->ecxt_scantuple = insertslot;
    6267             : 
    6268     3339042 :             foreach(l, notnull_attrs)
    6269             :             {
    6270     2576342 :                 int         attn = lfirst_int(l);
    6271             : 
    6272     2576342 :                 if (slot_attisnull(insertslot, attn + 1))
    6273             :                 {
    6274          60 :                     Form_pg_attribute attr = TupleDescAttr(newTupDesc, attn);
    6275             : 
    6276          60 :                     ereport(ERROR,
    6277             :                             (errcode(ERRCODE_NOT_NULL_VIOLATION),
    6278             :                              errmsg("column \"%s\" of relation \"%s\" contains null values",
    6279             :                                     NameStr(attr->attname),
    6280             :                                     RelationGetRelationName(oldrel)),
    6281             :                              errtablecol(oldrel, attn + 1)));
    6282             :                 }
    6283             :             }
    6284             : 
    6285      770800 :             foreach(l, tab->constraints)
    6286             :             {
    6287        8172 :                 NewConstraint *con = lfirst(l);
    6288             : 
    6289        8172 :                 switch (con->contype)
    6290             :                 {
    6291        8072 :                     case CONSTR_CHECK:
    6292        8072 :                         if (!ExecCheck(con->qualstate, econtext))
    6293          72 :                             ereport(ERROR,
    6294             :                                     (errcode(ERRCODE_CHECK_VIOLATION),
    6295             :                                      errmsg("check constraint \"%s\" of relation \"%s\" is violated by some row",
    6296             :                                             con->name,
    6297             :                                             RelationGetRelationName(oldrel)),
    6298             :                                      errtableconstraint(oldrel, con->name)));
    6299        8000 :                         break;
    6300         100 :                     case CONSTR_FOREIGN:
    6301             :                         /* Nothing to do here */
    6302         100 :                         break;
    6303           0 :                     default:
    6304           0 :                         elog(ERROR, "unrecognized constraint type: %d",
    6305             :                              (int) con->contype);
    6306             :                 }
    6307             :             }
    6308             : 
    6309      762628 :             if (partqualstate && !ExecCheck(partqualstate, econtext))
    6310             :             {
    6311          74 :                 if (tab->validate_default)
    6312          26 :                     ereport(ERROR,
    6313             :                             (errcode(ERRCODE_CHECK_VIOLATION),
    6314             :                              errmsg("updated partition constraint for default partition \"%s\" would be violated by some row",
    6315             :                                     RelationGetRelationName(oldrel)),
    6316             :                              errtable(oldrel)));
    6317             :                 else
    6318          48 :                     ereport(ERROR,
    6319             :                             (errcode(ERRCODE_CHECK_VIOLATION),
    6320             :                              errmsg("partition constraint of relation \"%s\" is violated by some row",
    6321             :                                     RelationGetRelationName(oldrel)),
    6322             :                              errtable(oldrel)));
    6323             :             }
    6324             : 
    6325             :             /* Write the tuple out to the new relation */
    6326      762554 :             if (newrel)
    6327       97540 :                 table_tuple_insert(newrel, insertslot, mycid,
    6328             :                                    ti_options, bistate);
    6329             : 
    6330      762554 :             ResetExprContext(econtext);
    6331             : 
    6332      762554 :             CHECK_FOR_INTERRUPTS();
    6333             :         }
    6334             : 
    6335        3872 :         MemoryContextSwitchTo(oldCxt);
    6336        3872 :         table_endscan(scan);
    6337        3872 :         UnregisterSnapshot(snapshot);
    6338             : 
    6339        3872 :         ExecDropSingleTupleTableSlot(oldslot);
    6340        3872 :         if (newslot)
    6341         836 :             ExecDropSingleTupleTableSlot(newslot);
    6342             :     }
    6343             : 
    6344        4638 :     FreeExecutorState(estate);
    6345             : 
    6346        4638 :     table_close(oldrel, NoLock);
    6347        4638 :     if (newrel)
    6348             :     {
    6349         836 :         FreeBulkInsertState(bistate);
    6350             : 
    6351         836 :         table_finish_bulk_insert(newrel, ti_options);
    6352             : 
    6353         836 :         table_close(newrel, NoLock);
    6354             :     }
    6355        4638 : }
    6356             : 
    6357             : /*
    6358             :  * ATGetQueueEntry: find or create an entry in the ALTER TABLE work queue
    6359             :  */
    6360             : static AlteredTableInfo *
    6361       33676 : ATGetQueueEntry(List **wqueue, Relation rel)
    6362             : {
    6363       33676 :     Oid         relid = RelationGetRelid(rel);
    6364             :     AlteredTableInfo *tab;
    6365             :     ListCell   *ltab;
    6366             : 
    6367       42414 :     foreach(ltab, *wqueue)
    6368             :     {
    6369       11244 :         tab = (AlteredTableInfo *) lfirst(ltab);
    6370       11244 :         if (tab->relid == relid)
    6371        2506 :             return tab;
    6372             :     }
    6373             : 
    6374             :     /*
    6375             :      * Not there, so add it.  Note that we make a copy of the relation's
    6376             :      * existing descriptor before anything interesting can happen to it.
    6377             :      */
    6378       31170 :     tab = (AlteredTableInfo *) palloc0(sizeof(AlteredTableInfo));
    6379       31170 :     tab->relid = relid;
    6380       31170 :     tab->rel = NULL;         /* set later */
    6381       31170 :     tab->relkind = rel->rd_rel->relkind;
    6382       31170 :     tab->oldDesc = CreateTupleDescCopyConstr(RelationGetDescr(rel));
    6383       31170 :     tab->newAccessMethod = InvalidOid;
    6384       31170 :     tab->chgAccessMethod = false;
    6385       31170 :     tab->newTableSpace = InvalidOid;
    6386       31170 :     tab->newrelpersistence = RELPERSISTENCE_PERMANENT;
    6387       31170 :     tab->chgPersistence = false;
    6388             : 
    6389       31170 :     *wqueue = lappend(*wqueue, tab);
    6390             : 
    6391       31170 :     return tab;
    6392             : }
    6393             : 
    6394             : static const char *
    6395          42 : alter_table_type_to_string(AlterTableType cmdtype)
    6396             : {
    6397          42 :     switch (cmdtype)
    6398             :     {
    6399           0 :         case AT_AddColumn:
    6400             :         case AT_AddColumnToView:
    6401           0 :             return "ADD COLUMN";
    6402           0 :         case AT_ColumnDefault:
    6403             :         case AT_CookedColumnDefault:
    6404           0 :             return "ALTER COLUMN ... SET DEFAULT";
    6405           6 :         case AT_DropNotNull:
    6406           6 :             return "ALTER COLUMN ... DROP NOT NULL";
    6407           6 :         case AT_SetNotNull:
    6408           6 :             return "ALTER COLUMN ... SET NOT NULL";
    6409           0 :         case AT_SetExpression:
    6410           0 :             return "ALTER COLUMN ... SET EXPRESSION";
    6411           0 :         case AT_DropExpression:
    6412           0 :             return "ALTER COLUMN ... DROP EXPRESSION";
    6413           0 :         case AT_CheckNotNull:
    6414           0 :             return NULL;        /* not real grammar */
    6415           0 :         case AT_SetStatistics:
    6416           0 :             return "ALTER COLUMN ... SET STATISTICS";
    6417          12 :         case AT_SetOptions:
    6418          12 :             return "ALTER COLUMN ... SET";
    6419           0 :         case AT_ResetOptions:
    6420           0 :             return "ALTER COLUMN ... RESET";
    6421           0 :         case AT_SetStorage:
    6422           0 :             return "ALTER COLUMN ... SET STORAGE";
    6423           0 :         case AT_SetCompression:
    6424           0 :             return "ALTER COLUMN ... SET COMPRESSION";
    6425           6 :         case AT_DropColumn:
    6426           6 :             return "DROP COLUMN";
    6427           0 :         case AT_AddIndex:
    6428             :         case AT_ReAddIndex:
    6429           0 :             return NULL;        /* not real grammar */
    6430           0 :         case AT_AddConstraint:
    6431             :         case AT_ReAddConstraint:
    6432             :         case AT_ReAddDomainConstraint:
    6433             :         case AT_AddIndexConstraint:
    6434           0 :             return "ADD CONSTRAINT";
    6435           6 :         case AT_AlterConstraint:
    6436           6 :             return "ALTER CONSTRAINT";
    6437           0 :         case AT_ValidateConstraint:
    6438           0 :             return "VALIDATE CONSTRAINT";
    6439           0 :         case AT_DropConstraint:
    6440           0 :             return "DROP CONSTRAINT";
    6441           0 :         case AT_ReAddComment:
    6442           0 :             return NULL;        /* not real grammar */
    6443           0 :         case AT_AlterColumnType:
    6444           0 :             return "ALTER COLUMN ... SET DATA TYPE";
    6445           0 :         case AT_AlterColumnGenericOptions:
    6446           0 :             return "ALTER COLUMN ... OPTIONS";
    6447           0 :         case AT_ChangeOwner:
    6448           0 :             return "OWNER TO";
    6449           0 :         case AT_ClusterOn:
    6450           0 :             return "CLUSTER ON";
    6451           0 :         case AT_DropCluster:
    6452           0 :             return "SET WITHOUT CLUSTER";
    6453           0 :         case AT_SetAccessMethod:
    6454           0 :             return "SET ACCESS METHOD";
    6455           0 :         case AT_SetLogged:
    6456           0 :             return "SET LOGGED";
    6457           0 :         case AT_SetUnLogged:
    6458           0 :             return "SET UNLOGGED";
    6459           0 :         case AT_DropOids:
    6460           0 :             return "SET WITHOUT OIDS";
    6461           0 :         case AT_SetTableSpace:
    6462           0 :             return "SET TABLESPACE";
    6463           0 :         case AT_SetRelOptions:
    6464           0 :             return "SET";
    6465           0 :         case AT_ResetRelOptions:
    6466           0 :             return "RESET";
    6467           0 :         case AT_ReplaceRelOptions:
    6468           0 :             return NULL;        /* not real grammar */
    6469           0 :         case AT_EnableTrig:
    6470           0 :             return "ENABLE TRIGGER";
    6471           0 :         case AT_EnableAlwaysTrig:
    6472           0 :             return "ENABLE ALWAYS TRIGGER";
    6473           0 :         case AT_EnableReplicaTrig:
    6474           0 :             return "ENABLE REPLICA TRIGGER";
    6475           0 :         case AT_DisableTrig:
    6476           0 :             return "DISABLE TRIGGER";
    6477           0 :         case AT_EnableTrigAll:
    6478           0 :             return "ENABLE TRIGGER ALL";
    6479           0 :         case AT_DisableTrigAll:
    6480           0 :             return "DISABLE TRIGGER ALL";
    6481           0 :         case AT_EnableTrigUser:
    6482           0 :             return "ENABLE TRIGGER USER";
    6483           0 :         case AT_DisableTrigUser:
    6484           0 :             return "DISABLE TRIGGER USER";
    6485           0 :         case AT_EnableRule:
    6486           0 :             return "ENABLE RULE";
    6487           0 :         case AT_EnableAlwaysRule:
    6488           0 :             return "ENABLE ALWAYS RULE";
    6489           0 :         case AT_EnableReplicaRule:
    6490           0 :             return "ENABLE REPLICA RULE";
    6491           0 :         case AT_DisableRule:
    6492           0 :             return "DISABLE RULE";
    6493           0 :         case AT_AddInherit:
    6494           0 :             return "INHERIT";
    6495           0 :         case AT_DropInherit:
    6496           0 :             return "NO INHERIT";
    6497           0 :         case AT_AddOf:
    6498           0 :             return "OF";
    6499           0 :         case AT_DropOf:
    6500           0 :             return "NOT OF";
    6501           0 :         case AT_ReplicaIdentity:
    6502           0 :             return "REPLICA IDENTITY";
    6503           0 :         case AT_EnableRowSecurity:
    6504           0 :             return "ENABLE ROW SECURITY";
    6505           0 :         case AT_DisableRowSecurity:
    6506           0 :             return "DISABLE ROW SECURITY";
    6507           0 :         case AT_ForceRowSecurity:
    6508           0 :             return "FORCE ROW SECURITY";
    6509           0 :         case AT_NoForceRowSecurity:
    6510           0 :             return "NO FORCE ROW SECURITY";
    6511           0 :         case AT_GenericOptions:
    6512           0 :             return "OPTIONS";
    6513           0 :         case AT_AttachPartition:
    6514           0 :             return "ATTACH PARTITION";
    6515           6 :         case AT_DetachPartition:
    6516           6 :             return "DETACH PARTITION";
    6517           0 :         case AT_DetachPartitionFinalize:
    6518           0 :             return "DETACH PARTITION ... FINALIZE";
    6519           0 :         case AT_SplitPartition:
    6520           0 :             return "SPLIT PARTITION";
    6521           0 :         case AT_MergePartitions:
    6522           0 :             return "MERGE PARTITIONS";
    6523           0 :         case AT_AddIdentity:
    6524           0 :             return "ALTER COLUMN ... ADD IDENTITY";
    6525           0 :         case AT_SetIdentity:
    6526           0 :             return "ALTER COLUMN ... SET";
    6527           0 :         case AT_DropIdentity:
    6528           0 :             return "ALTER COLUMN ... DROP IDENTITY";
    6529           0 :         case AT_ReAddStatistics:
    6530           0 :             return NULL;        /* not real grammar */
    6531             :     }
    6532             : 
    6533           0 :     return NULL;
    6534             : }
    6535             : 
    6536             : /*
    6537             :  * ATSimplePermissions
    6538             :  *
    6539             :  * - Ensure that it is a relation (or possibly a view)
    6540             :  * - Ensure this user is the owner
    6541             :  * - Ensure that it is not a system table
    6542             :  */
    6543             : static void
    6544       31300 : ATSimplePermissions(AlterTableType cmdtype, Relation rel, int allowed_targets)
    6545             : {
    6546             :     int         actual_target;
    6547             : 
    6548       31300 :     switch (rel->rd_rel->relkind)
    6549             :     {
    6550       29098 :         case RELKIND_RELATION:
    6551             :         case RELKIND_PARTITIONED_TABLE:
    6552       29098 :             actual_target = ATT_TABLE;
    6553       29098 :             break;
    6554         396 :         case RELKIND_VIEW:
    6555         396 :             actual_target = ATT_VIEW;
    6556         396 :             break;
    6557          46 :         case RELKIND_MATVIEW:
    6558          46 :             actual_target = ATT_MATVIEW;
    6559          46 :             break;
    6560         226 :         case RELKIND_INDEX:
    6561         226 :             actual_target = ATT_INDEX;
    6562         226 :             break;
    6563         434 :         case RELKIND_PARTITIONED_INDEX:
    6564         434 :             actual_target = ATT_PARTITIONED_INDEX;
    6565         434 :             break;
    6566         214 :         case RELKIND_COMPOSITE_TYPE:
    6567         214 :             actual_target = ATT_COMPOSITE_TYPE;
    6568         214 :             break;
    6569         874 :         case RELKIND_FOREIGN_TABLE:
    6570         874 :             actual_target = ATT_FOREIGN_TABLE;
    6571         874 :             break;
    6572          12 :         case RELKIND_SEQUENCE:
    6573          12 :             actual_target = ATT_SEQUENCE;
    6574          12 :             break;
    6575           0 :         default:
    6576           0 :             actual_target = 0;
    6577           0 :             break;
    6578             :     }
    6579             : 
    6580             :     /* Wrong target type? */
    6581       31300 :     if ((actual_target & allowed_targets) == 0)
    6582             :     {
    6583          42 :         const char *action_str = alter_table_type_to_string(cmdtype);
    6584             : 
    6585          42 :         if (action_str)
    6586          42 :             ereport(ERROR,
    6587             :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    6588             :             /* translator: %s is a group of some SQL keywords */
    6589             :                      errmsg("ALTER action %s cannot be performed on relation \"%s\"",
    6590             :                             action_str, RelationGetRelationName(rel)),
    6591             :                      errdetail_relkind_not_supported(rel->rd_rel->relkind)));
    6592             :         else
    6593             :             /* internal error? */
    6594           0 :             elog(ERROR, "invalid ALTER action attempted on relation \"%s\"",
    6595             :                  RelationGetRelationName(rel));
    6596             :     }
    6597             : 
    6598             :     /* Permissions checks */
    6599       31258 :     if (!object_ownercheck(RelationRelationId, RelationGetRelid(rel), GetUserId()))
    6600          12 :         aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(rel->rd_rel->relkind),
    6601          12 :                        RelationGetRelationName(rel));
    6602             : 
    6603       31246 :     if (!allowSystemTableMods && IsSystemRelation(rel))
    6604           0 :         ereport(ERROR,
    6605             :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
    6606             :                  errmsg("permission denied: \"%s\" is a system catalog",
    6607             :                         RelationGetRelationName(rel))));
    6608       31246 : }
    6609             : 
    6610             : /*
    6611             :  * ATSimpleRecursion
    6612             :  *
    6613             :  * Simple table recursion sufficient for most ALTER TABLE operations.
    6614             :  * All direct and indirect children are processed in an unspecified order.
    6615             :  * Note that if a child inherits from the original table via multiple
    6616             :  * inheritance paths, it will be visited just once.
    6617             :  */
    6618             : static void
    6619        8758 : ATSimpleRecursion(List **wqueue, Relation rel,
    6620             :                   AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode,
    6621             :                   AlterTableUtilityContext *context)
    6622             : {
    6623             :     /*
    6624             :      * Propagate to children, if desired and if there are (or might be) any
    6625             :      * children.
    6626             :      */
    6627        8758 :     if (recurse && rel->rd_rel->relhassubclass)
    6628             :     {
    6629         214 :         Oid         relid = RelationGetRelid(rel);
    6630             :         ListCell   *child;
    6631             :         List       *children;
    6632             : 
    6633         214 :         children = find_all_inheritors(relid, lockmode, NULL);
    6634             : 
    6635             :         /*
    6636             :          * find_all_inheritors does the recursive search of the inheritance
    6637             :          * hierarchy, so all we have to do is process all of the relids in the
    6638             :          * list that it returns.
    6639             :          */
    6640         870 :         foreach(child, children)
    6641             :         {
    6642         662 :             Oid         childrelid = lfirst_oid(child);
    6643             :             Relation    childrel;
    6644             : 
    6645         662 :             if (childrelid == relid)
    6646         214 :                 continue;
    6647             :             /* find_all_inheritors already got lock */
    6648         448 :             childrel = relation_open(childrelid, NoLock);
    6649         448 :             CheckAlterTableIsSafe(childrel);
    6650         448 :             ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode, context);
    6651         442 :             relation_close(childrel, NoLock);
    6652             :         }
    6653             :     }
    6654        8752 : }
    6655             : 
    6656             : /*
    6657             :  * Obtain list of partitions of the given table, locking them all at the given
    6658             :  * lockmode and ensuring that they all pass CheckAlterTableIsSafe.
    6659             :  *
    6660             :  * This function is a no-op if the given relation is not a partitioned table;
    6661             :  * in particular, nothing is done if it's a legacy inheritance parent.
    6662             :  */
    6663             : static void
    6664         564 : ATCheckPartitionsNotInUse(Relation rel, LOCKMODE lockmode)
    6665             : {
    6666         564 :     if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
    6667             :     {
    6668             :         List       *inh;
    6669             :         ListCell   *cell;
    6670             : 
    6671         122 :         inh = find_all_inheritors(RelationGetRelid(rel), lockmode, NULL);
    6672             :         /* first element is the parent rel; must ignore it */
    6673         412 :         for_each_from(cell, inh, 1)
    6674             :         {
    6675             :             Relation    childrel;
    6676             : 
    6677             :             /* find_all_inheritors already got lock */
    6678         296 :             childrel = table_open(lfirst_oid(cell), NoLock);
    6679         296 :             CheckAlterTableIsSafe(childrel);
    6680         290 :             table_close(childrel, NoLock);
    6681             :         }
    6682         116 :         list_free(inh);
    6683             :     }
    6684         558 : }
    6685             : 
    6686             : /*
    6687             :  * ATTypedTableRecursion
    6688             :  *
    6689             :  * Propagate ALTER TYPE operations to the typed tables of that type.
    6690             :  * Also check the RESTRICT/CASCADE behavior.  Given CASCADE, also permit
    6691             :  * recursion to inheritance children of the typed tables.
    6692             :  */
    6693             : static void
    6694         190 : ATTypedTableRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd,
    6695             :                       LOCKMODE lockmode, AlterTableUtilityContext *context)
    6696             : {
    6697             :     ListCell   *child;
    6698             :     List       *children;
    6699             : 
    6700             :     Assert(rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE);
    6701             : 
    6702         190 :     children = find_typed_table_dependencies(rel->rd_rel->reltype,
    6703         190 :                                              RelationGetRelationName(rel),
    6704             :                                              cmd->behavior);
    6705             : 
    6706         202 :     foreach(child, children)
    6707             :     {
    6708          30 :         Oid         childrelid = lfirst_oid(child);
    6709             :         Relation    childrel;
    6710             : 
    6711          30 :         childrel = relation_open(childrelid, lockmode);
    6712          30 :         CheckAlterTableIsSafe(childrel);
    6713          30 :         ATPrepCmd(wqueue, childrel, cmd, true, true, lockmode, context);
    6714          30 :         relation_close(childrel, NoLock);
    6715             :     }
    6716         172 : }
    6717             : 
    6718             : 
    6719             : /*
    6720             :  * find_composite_type_dependencies
    6721             :  *
    6722             :  * Check to see if the type "typeOid" is being used as a column in some table
    6723             :  * (possibly nested several levels deep in composite types, arrays, etc!).
    6724             :  * Eventually, we'd like to propagate the check or rewrite operation
    6725             :  * into such tables, but for now, just error out if we find any.
    6726             :  *
    6727             :  * Caller should provide either the associated relation of a rowtype,
    6728             :  * or a type name (not both) for use in the error message, if any.
    6729             :  *
    6730             :  * Note that "typeOid" is not necessarily a composite type; it could also be
    6731             :  * another container type such as an array or range, or a domain over one of
    6732             :  * these things.  The name of this function is therefore somewhat historical,
    6733             :  * but it's not worth changing.
    6734             :  *
    6735             :  * We assume that functions and views depending on the type are not reasons
    6736             :  * to reject the ALTER.  (How safe is this really?)
    6737             :  */
    6738             : void
    6739        3874 : find_composite_type_dependencies(Oid typeOid, Relation origRelation,
    6740             :                                  const char *origTypeName)
    6741             : {
    6742             :     Relation    depRel;
    6743             :     ScanKeyData key[2];
    6744             :     SysScanDesc depScan;
    6745             :     HeapTuple   depTup;
    6746             : 
    6747             :     /* since this function recurses, it could be driven to stack overflow */
    6748        3874 :     check_stack_depth();
    6749             : 
    6750             :     /*
    6751             :      * We scan pg_depend to find those things that depend on the given type.
    6752             :      * (We assume we can ignore refobjsubid for a type.)
    6753             :      */
    6754        3874 :     depRel = table_open(DependRelationId, AccessShareLock);
    6755             : 
    6756        3874 :     ScanKeyInit(&key[0],
    6757             :                 Anum_pg_depend_refclassid,
    6758             :                 BTEqualStrategyNumber, F_OIDEQ,
    6759             :                 ObjectIdGetDatum(TypeRelationId));
    6760        3874 :     ScanKeyInit(&key[1],
    6761             :                 Anum_pg_depend_refobjid,
    6762             :                 BTEqualStrategyNumber, F_OIDEQ,
    6763             :                 ObjectIdGetDatum(typeOid));
    6764             : 
    6765        3874 :     depScan = systable_beginscan(depRel, DependReferenceIndexId, true,
    6766             :                                  NULL, 2, key);
    6767             : 
    6768        5988 :     while (HeapTupleIsValid(depTup = systable_getnext(depScan)))
    6769             :     {
    6770        2210 :         Form_pg_depend pg_depend = (Form_pg_depend) GETSTRUCT(depTup);
    6771             :         Relation    rel;
    6772             :         TupleDesc   tupleDesc;
    6773             :         Form_pg_attribute att;
    6774             : 
    6775             :         /* Check for directly dependent types */
    6776        2210 :         if (pg_depend->classid == TypeRelationId)
    6777             :         {
    6778             :             /*
    6779             :              * This must be an array, domain, or range containing the given
    6780             :              * type, so recursively check for uses of this type.  Note that
    6781             :              * any error message will mention the original type not the
    6782             :              * container; this is intentional.
    6783             :              */
    6784        1864 :             find_composite_type_dependencies(pg_depend->objid,
    6785             :                                              origRelation, origTypeName);
    6786        1840 :             continue;
    6787             :         }
    6788             : 
    6789             :         /* Else, ignore dependees that aren't relations */
    6790         346 :         if (pg_depend->classid != RelationRelationId)
    6791         122 :             continue;
    6792             : 
    6793         224 :         rel = relation_open(pg_depend->objid, AccessShareLock);
    6794         224 :         tupleDesc = RelationGetDescr(rel);
    6795             : 
    6796             :         /*
    6797             :          * If objsubid identifies a specific column, refer to that in error
    6798             :          * messages.  Otherwise, search to see if there's a user column of the
    6799             :          * type.  (We assume system columns are never of interesting types.)
    6800             :          * The search is needed because an index containing an expression
    6801             :          * column of the target type will just be recorded as a whole-relation
    6802             :          * dependency.  If we do not find a column of the type, the dependency
    6803             :          * must indicate that the type is transiently referenced in an index
    6804             :          * expression but not stored on disk, which we assume is OK, just as
    6805             :          * we do for references in views.  (It could also be that the target
    6806             :          * type is embedded in some container type that is stored in an index
    6807             :          * column, but the previous recursion should catch such cases.)
    6808             :          */
    6809         224 :         if (pg_depend->objsubid > 0 && pg_depend->objsubid <= tupleDesc->natts)
    6810          66 :             att = TupleDescAttr(tupleDesc, pg_depend->objsubid - 1);
    6811             :         else
    6812             :         {
    6813         158 :             att = NULL;
    6814         406 :             for (int attno = 1; attno <= tupleDesc->natts; attno++)
    6815             :             {
    6816         254 :                 att = TupleDescAttr(tupleDesc, attno - 1);
    6817         254 :                 if (att->atttypid == typeOid && !att->attisdropped)
    6818           6 :                     break;
    6819         248 :                 att = NULL;
    6820             :             }
    6821         158 :             if (att == NULL)
    6822             :             {
    6823             :                 /* No such column, so assume OK */
    6824         152 :                 relation_close(rel, AccessShareLock);
    6825         152 :                 continue;
    6826             :             }
    6827             :         }
    6828             : 
    6829             :         /*
    6830             :          * We definitely should reject if the relation has storage.  If it's
    6831             :          * partitioned, then perhaps we don't have to reject: if there are
    6832             :          * partitions then we'll fail when we find one, else there is no
    6833             :          * stored data to worry about.  However, it's possible that the type
    6834             :          * change would affect conclusions about whether the type is sortable
    6835             :          * or hashable and thus (if it's a partitioning column) break the
    6836             :          * partitioning rule.  For now, reject for partitioned rels too.
    6837             :          */
    6838          72 :         if (RELKIND_HAS_STORAGE(rel->rd_rel->relkind) ||
    6839           0 :             RELKIND_HAS_PARTITIONS(rel->rd_rel->relkind))
    6840             :         {
    6841          72 :             if (origTypeName)
    6842          30 :                 ereport(ERROR,
    6843             :                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    6844             :                          errmsg("cannot alter type \"%s\" because column \"%s.%s\" uses it",
    6845             :                                 origTypeName,
    6846             :                                 RelationGetRelationName(rel),
    6847             :                                 NameStr(att->attname))));
    6848          42 :             else if (origRelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
    6849          18 :                 ereport(ERROR,
    6850             :                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    6851             :                          errmsg("cannot alter type \"%s\" because column \"%s.%s\" uses it",
    6852             :                                 RelationGetRelationName(origRelation),
    6853             :                                 RelationGetRelationName(rel),
    6854             :                                 NameStr(att->attname))));
    6855          24 :             else if (origRelation->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
    6856           6 :                 ereport(ERROR,
    6857             :                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    6858             :                          errmsg("cannot alter foreign table \"%s\" because column \"%s.%s\" uses its row type",
    6859             :                                 RelationGetRelationName(origRelation),
    6860             :                                 RelationGetRelationName(rel),
    6861             :                                 NameStr(att->attname))));
    6862             :             else
    6863          18 :                 ereport(ERROR,
    6864             :                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    6865             :                          errmsg("cannot alter table \"%s\" because column \"%s.%s\" uses its row type",
    6866             :                                 RelationGetRelationName(origRelation),
    6867             :                                 RelationGetRelationName(rel),
    6868             :                                 NameStr(att->attname))));
    6869             :         }
    6870           0 :         else if (OidIsValid(rel->rd_rel->reltype))
    6871             :         {
    6872             :             /*
    6873             :              * A view or composite type itself isn't a problem, but we must
    6874             :              * recursively check for indirect dependencies via its rowtype.
    6875             :              */
    6876           0 :             find_composite_type_dependencies(rel->rd_rel->reltype,
    6877             :                                              origRelation, origTypeName);
    6878             :         }
    6879             : 
    6880           0 :         relation_close(rel, AccessShareLock);
    6881             :     }
    6882             : 
    6883        3778 :     systable_endscan(depScan);
    6884             : 
    6885        3778 :     relation_close(depRel, AccessShareLock);
    6886        3778 : }
    6887             : 
    6888             : 
    6889             : /*
    6890             :  * find_typed_table_dependencies
    6891             :  *
    6892             :  * Check to see if a composite type is being used as the type of a
    6893             :  * typed table.  Abort if any are found and behavior is RESTRICT.
    6894             :  * Else return the list of tables.
    6895             :  */
    6896             : static List *
    6897         214 : find_typed_table_dependencies(Oid typeOid, const char *typeName, DropBehavior behavior)
    6898             : {
    6899             :     Relation    classRel;
    6900             :     ScanKeyData key[1];
    6901             :     TableScanDesc scan;
    6902             :     HeapTuple   tuple;
    6903         214 :     List       *result = NIL;
    6904             : 
    6905         214 :     classRel = table_open(RelationRelationId, AccessShareLock);
    6906             : 
    6907         214 :     ScanKeyInit(&key[0],
    6908             :                 Anum_pg_class_reloftype,
    6909             :                 BTEqualStrategyNumber, F_OIDEQ,
    6910             :                 ObjectIdGetDatum(typeOid));
    6911             : 
    6912         214 :     scan = table_beginscan_catalog(classRel, 1, key);
    6913             : 
    6914         250 :     while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
    6915             :     {
    6916          60 :         Form_pg_class classform = (Form_pg_class) GETSTRUCT(tuple);
    6917             : 
    6918          60 :         if (behavior == DROP_RESTRICT)
    6919          24 :             ereport(ERROR,
    6920             :                     (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
    6921             :                      errmsg("cannot alter type \"%s\" because it is the type of a typed table",
    6922             :                             typeName),
    6923             :                      errhint("Use ALTER ... CASCADE to alter the typed tables too.")));
    6924             :         else
    6925          36 :             result = lappend_oid(result, classform->oid);
    6926             :     }
    6927             : 
    6928         190 :     table_endscan(scan);
    6929         190 :     table_close(classRel, AccessShareLock);
    6930             : 
    6931         190 :     return result;
    6932             : }
    6933             : 
    6934             : 
    6935             : /*
    6936             :  * check_of_type
    6937             :  *
    6938             :  * Check whether a type is suitable for CREATE TABLE OF/ALTER TABLE OF.  If it
    6939             :  * isn't suitable, throw an error.  Currently, we require that the type
    6940             :  * originated with CREATE TYPE AS.  We could support any row type, but doing so
    6941             :  * would require handling a number of extra corner cases in the DDL commands.
    6942             :  * (Also, allowing domain-over-composite would open up a can of worms about
    6943             :  * whether and how the domain's constraints should apply to derived tables.)
    6944             :  */
    6945             : void
    6946         176 : check_of_type(HeapTuple typetuple)
    6947             : {
    6948         176 :     Form_pg_type typ = (Form_pg_type) GETSTRUCT(typetuple);
    6949         176 :     bool        typeOk = false;
    6950             : 
    6951         176 :     if (typ->typtype == TYPTYPE_COMPOSITE)
    6952             :     {
    6953             :         Relation    typeRelation;
    6954             : 
    6955             :         Assert(OidIsValid(typ->typrelid));
    6956         170 :         typeRelation = relation_open(typ->typrelid, AccessShareLock);
    6957         170 :         typeOk = (typeRelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE);
    6958             : 
    6959             :         /*
    6960             :          * Close the parent rel, but keep our AccessShareLock on it until xact
    6961             :          * commit.  That will prevent someone else from deleting or ALTERing
    6962             :          * the type before the typed table creation/conversion commits.
    6963             :          */
    6964         170 :         relation_close(typeRelation, NoLock);
    6965             : 
    6966         170 :         if (!typeOk)
    6967           6 :             ereport(ERROR,
    6968             :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    6969             :                      errmsg("type %s is the row type of another table",
    6970             :                             format_type_be(typ->oid)),
    6971             :                      errdetail("A typed table must use a stand-alone composite type created with CREATE TYPE.")));
    6972             :     }
    6973             :     else
    6974           6 :         ereport(ERROR,
    6975             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    6976             :                  errmsg("type %s is not a composite type",
    6977             :                         format_type_be(typ->oid))));
    6978         164 : }
    6979             : 
    6980             : 
    6981             : /*
    6982             :  * ALTER TABLE ADD COLUMN
    6983             :  *
    6984             :  * Adds an additional attribute to a relation making the assumption that
    6985             :  * CHECK, NOT NULL, and FOREIGN KEY constraints will be removed from the
    6986             :  * AT_AddColumn AlterTableCmd by parse_utilcmd.c and added as independent
    6987             :  * AlterTableCmd's.
    6988             :  *
    6989             :  * ADD COLUMN cannot use the normal ALTER TABLE recursion mechanism, because we
    6990             :  * have to decide at runtime whether to recurse or not depending on whether we
    6991             :  * actually add a column or merely merge with an existing column.  (We can't
    6992             :  * check this in a static pre-pass because it won't handle multiple inheritance
    6993             :  * situations correctly.)
    6994             :  */
    6995             : static void
    6996        1956 : ATPrepAddColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
    6997             :                 bool is_view, AlterTableCmd *cmd, LOCKMODE lockmode,
    6998             :                 AlterTableUtilityContext *context)
    6999             : {
    7000        1956 :     if (rel->rd_rel->reloftype && !recursing)
    7001           6 :         ereport(ERROR,
    7002             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    7003             :                  errmsg("cannot add column to typed table")));
    7004             : 
    7005        1950 :     if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
    7006          58 :         ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
    7007             : 
    7008        1944 :     if (recurse && !is_view)
    7009        1844 :         cmd->recurse = true;
    7010        1944 : }
    7011             : 
    7012             : /*
    7013             :  * Add a column to a table.  The return value is the address of the
    7014             :  * new column in the parent relation.
    7015             :  *
    7016             :  * cmd is pass-by-ref so that we can replace it with the parse-transformed
    7017             :  * copy (but that happens only after we check for IF NOT EXISTS).
    7018             :  */
    7019             : static ObjectAddress
    7020        2556 : ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
    7021             :                 AlterTableCmd **cmd, bool recurse, bool recursing,
    7022             :                 LOCKMODE lockmode, AlterTablePass cur_pass,
    7023             :                 AlterTableUtilityContext *context)
    7024             : {
    7025        2556 :     Oid         myrelid = RelationGetRelid(rel);
    7026        2556 :     ColumnDef  *colDef = castNode(ColumnDef, (*cmd)->def);
    7027        2556 :     bool        if_not_exists = (*cmd)->missing_ok;
    7028             :     Relation    pgclass,
    7029             :                 attrdesc;
    7030             :     HeapTuple   reltup;
    7031             :     Form_pg_attribute attribute;
    7032             :     int         newattnum;
    7033             :     char        relkind;
    7034             :     Expr       *defval;
    7035             :     List       *children;
    7036             :     ListCell   *child;
    7037             :     AlterTableCmd *childcmd;
    7038             :     ObjectAddress address;
    7039             :     TupleDesc   tupdesc;
    7040             : 
    7041             :     /* since this function recurses, it could be driven to stack overflow */
    7042        2556 :     check_stack_depth();
    7043             : 
    7044             :     /* At top level, permission check was done in ATPrepCmd, else do it */
    7045        2556 :     if (recursing)
    7046         618 :         ATSimplePermissions((*cmd)->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
    7047             : 
    7048        2556 :     if (rel->rd_rel->relispartition && !recursing)
    7049          12 :         ereport(ERROR,
    7050             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    7051             :                  errmsg("cannot add column to a partition")));
    7052             : 
    7053        2544 :     attrdesc = table_open(AttributeRelationId, RowExclusiveLock);
    7054             : 
    7055             :     /*
    7056             :      * Are we adding the column to a recursion child?  If so, check whether to
    7057             :      * merge with an existing definition for the column.  If we do merge, we
    7058             :      * must not recurse.  Children will already have the column, and recursing
    7059             :      * into them would mess up attinhcount.
    7060             :      */
    7061        2544 :     if (colDef->inhcount > 0)
    7062             :     {
    7063             :         HeapTuple   tuple;
    7064             : 
    7065             :         /* Does child already have a column by this name? */
    7066         618 :         tuple = SearchSysCacheCopyAttName(myrelid, colDef->colname);
    7067         618 :         if (HeapTupleIsValid(tuple))
    7068             :         {
    7069          36 :             Form_pg_attribute childatt = (Form_pg_attribute) GETSTRUCT(tuple);
    7070             :             Oid         ctypeId;
    7071             :             int32       ctypmod;
    7072             :             Oid         ccollid;
    7073             : 
    7074             :             /* Child column must match on type, typmod, and collation */
    7075          36 :             typenameTypeIdAndMod(NULL, colDef->typeName, &ctypeId, &ctypmod);
    7076          36 :             if (ctypeId != childatt->atttypid ||
    7077          36 :                 ctypmod != childatt->atttypmod)
    7078           0 :                 ereport(ERROR,
    7079             :                         (errcode(ERRCODE_DATATYPE_MISMATCH),
    7080             :                          errmsg("child table \"%s\" has different type for column \"%s\"",
    7081             :                                 RelationGetRelationName(rel), colDef->colname)));
    7082          36 :             ccollid = GetColumnDefCollation(NULL, colDef, ctypeId);
    7083          36 :             if (ccollid != childatt->attcollation)
    7084           0 :                 ereport(ERROR,
    7085             :                         (errcode(ERRCODE_COLLATION_MISMATCH),
    7086             :                          errmsg("child table \"%s\" has different collation for column \"%s\"",
    7087             :                                 RelationGetRelationName(rel), colDef->colname),
    7088             :                          errdetail("\"%s\" versus \"%s\"",
    7089             :                                    get_collation_name(ccollid),
    7090             :                                    get_collation_name(childatt->attcollation))));
    7091             : 
    7092             :             /* Bump the existing child att's inhcount */
    7093          36 :             childatt->attinhcount++;
    7094          36 :             if (childatt->attinhcount < 0)
    7095           0 :                 ereport(ERROR,
    7096             :                         errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
    7097             :                         errmsg("too many inheritance parents"));
    7098          36 :             CatalogTupleUpdate(attrdesc, &tuple->t_self, tuple);
    7099             : 
    7100          36 :             heap_freetuple(tuple);
    7101             : 
    7102             :             /* Inform the user about the merge */
    7103          36 :             ereport(NOTICE,
    7104             :                     (errmsg("merging definition of column \"%s\" for child \"%s\"",
    7105             :                             colDef->colname, RelationGetRelationName(rel))));
    7106             : 
    7107          36 :             table_close(attrdesc, RowExclusiveLock);
    7108             : 
    7109             :             /* Make the child column change visible */
    7110          36 :             CommandCounterIncrement();
    7111             : 
    7112          36 :             return InvalidObjectAddress;
    7113             :         }
    7114             :     }
    7115             : 
    7116             :     /* skip if the name already exists and if_not_exists is true */
    7117        2508 :     if (!check_for_column_name_collision(rel, colDef->colname, if_not_exists))
    7118             :     {
    7119          54 :         table_close(attrdesc, RowExclusiveLock);
    7120          54 :         return InvalidObjectAddress;
    7121             :     }
    7122             : 
    7123             :     /*
    7124             :      * Okay, we need to add the column, so go ahead and do parse
    7125             :      * transformation.  This can result in queueing up, or even immediately
    7126             :      * executing, subsidiary operations (such as creation of unique indexes);
    7127             :      * so we mustn't do it until we have made the if_not_exists check.
    7128             :      *
    7129             :      * When recursing, the command was already transformed and we needn't do
    7130             :      * so again.  Also, if context isn't given we can't transform.  (That
    7131             :      * currently happens only for AT_AddColumnToView; we expect that view.c
    7132             :      * passed us a ColumnDef that doesn't need work.)
    7133             :      */
    7134        2424 :     if (context != NULL && !recursing)
    7135             :     {
    7136        1818 :         *cmd = ATParseTransformCmd(wqueue, tab, rel, *cmd, recurse, lockmode,
    7137             :                                    cur_pass, context);
    7138             :         Assert(*cmd != NULL);
    7139        1818 :         colDef = castNode(ColumnDef, (*cmd)->def);
    7140             :     }
    7141             : 
    7142             :     /*
    7143             :      * Regular inheritance children are independent enough not to inherit the
    7144             :      * identity column from parent hence cannot recursively add identity
    7145             :      * column if the table has inheritance children.
    7146             :      *
    7147             :      * Partitions, on the other hand, are integral part of a partitioned table
    7148             :      * and inherit identity column.  Hence propagate identity column down the
    7149             :      * partition hierarchy.
    7150             :      */
    7151        2424 :     if (colDef->identity &&
    7152          54 :         recurse &&
    7153         102 :         rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE &&
    7154          48 :         find_inheritance_children(myrelid, NoLock) != NIL)
    7155           6 :         ereport(ERROR,
    7156             :                 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    7157             :                  errmsg("cannot recursively add identity column to table that has child tables")));
    7158             : 
    7159        2418 :     pgclass = table_open(RelationRelationId, RowExclusiveLock);
    7160             : 
    7161        2418 :     reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(myrelid));
    7162        2418 :     if (!HeapTupleIsValid(reltup))
    7163           0 :         elog(ERROR, "cache lookup failed for relation %u", myrelid);
    7164        2418 :     relkind = ((Form_pg_class) GETSTRUCT(reltup))->relkind;
    7165             : 
    7166             :     /* Determine the new attribute's number */
    7167        2418 :     newattnum = ((Form_pg_class) GETSTRUCT(reltup))->relnatts + 1;
    7168        2418 :     if (newattnum > MaxHeapAttributeNumber)
    7169           0 :         ereport(ERROR,
    7170             :                 (errcode(ERRCODE_TOO_MANY_COLUMNS),
    7171             :                  errmsg("tables can have at most %d columns",
    7172             :                         MaxHeapAttributeNumber)));
    7173             : 
    7174             :     /*
    7175             :      * Construct new attribute's pg_attribute entry.
    7176             :      */
    7177        2418 :     tupdesc = BuildDescForRelation(list_make1(colDef));
    7178             : 
    7179        2406 :     attribute = TupleDescAttr(tupdesc, 0);
    7180             : 
    7181             :     /* Fix up attribute number */
    7182        2406 :     attribute->attnum = newattnum;
    7183             : 
    7184             :     /* make sure datatype is legal for a column */
    7185        2406 :     CheckAttributeType(NameStr(attribute->attname), attribute->atttypid, attribute->attcollation,
    7186        2406 :                        list_make1_oid(rel->rd_rel->reltype),
    7187             :                        0);
    7188             : 
    7189        2376 :     InsertPgAttributeTuples(attrdesc, tupdesc, myrelid, NULL, NULL);
    7190             : 
    7191        2376 :     table_close(attrdesc, RowExclusiveLock);
    7192             : 
    7193             :     /*
    7194             :      * Update pg_class tuple as appropriate
    7195             :      */
    7196        2376 :     ((Form_pg_class) GETSTRUCT(reltup))->relnatts = newattnum;
    7197             : 
    7198        2376 :     CatalogTupleUpdate(pgclass, &reltup->t_self, reltup);
    7199             : 
    7200        2376 :     heap_freetuple(reltup);
    7201             : 
    7202             :     /* Post creation hook for new attribute */
    7203        2376 :     InvokeObjectPostCreateHook(RelationRelationId, myrelid, newattnum);
    7204             : 
    7205        2376 :     table_close(pgclass, RowExclusiveLock);
    7206             : 
    7207             :     /* Make the attribute's catalog entry visible */
    7208        2376 :     CommandCounterIncrement();
    7209             : 
    7210             :     /*
    7211             :      * Store the DEFAULT, if any, in the catalogs
    7212             :      */
    7213        2376 :     if (colDef->raw_default)
    7214             :     {
    7215             :         RawColumnDefault *rawEnt;
    7216             : 
    7217         708 :         rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
    7218         708 :         rawEnt->attnum = attribute->attnum;
    7219         708 :         rawEnt->raw_default = copyObject(colDef->raw_default);
    7220             : 
    7221             :         /*
    7222             :          * Attempt to skip a complete table rewrite by storing the specified
    7223             :          * DEFAULT value outside of the heap.  This may be disabled inside
    7224             :          * AddRelationNewConstraints if the optimization cannot be applied.
    7225             :          */
    7226         708 :         rawEnt->missingMode = (!colDef->generated);
    7227             : 
    7228         708 :         rawEnt->generated = colDef->generated;
    7229             : 
    7230             :         /*
    7231             :          * This function is intended for CREATE TABLE, so it processes a
    7232             :          * _list_ of defaults, but we just do one.
    7233             :          */
    7234         708 :         AddRelationNewConstraints(rel, list_make1(rawEnt), NIL,
    7235             :                                   false, true, false, NULL);
    7236             : 
    7237             :         /* Make the additional catalog changes visible */
    7238         696 :         CommandCounterIncrement();
    7239             : 
    7240             :         /*
    7241             :          * Did the request for a missing value work? If not we'll have to do a
    7242             :          * rewrite
    7243             :          */
    7244         696 :         if (!rawEnt->missingMode)
    7245         108 :             tab->rewrite |= AT_REWRITE_DEFAULT_VAL;
    7246             :     }
    7247             : 
    7248             :     /*
    7249             :      * Tell Phase 3 to fill in the default expression, if there is one.
    7250             :      *
    7251             :      * If there is no default, Phase 3 doesn't have to do anything, because
    7252             :      * that effectively means that the default is NULL.  The heap tuple access
    7253             :      * routines always check for attnum > # of attributes in tuple, and return
    7254             :      * NULL if so, so without any modification of the tuple data we will get
    7255             :      * the effect of NULL values in the new column.
    7256             :      *
    7257             :      * An exception occurs when the new column is of a domain type: the domain
    7258             :      * might have a not-null constraint, or a check constraint that indirectly
    7259             :      * rejects nulls.  If there are any domain constraints then we construct
    7260             :      * an explicit NULL default value that will be passed through
    7261             :      * CoerceToDomain processing.  (This is a tad inefficient, since it causes
    7262             :      * rewriting the table which we really don't have to do, but the present
    7263             :      * design of domain processing doesn't offer any simple way of checking
    7264             :      * the constraints more directly.)
    7265             :      *
    7266             :      * Note: we use build_column_default, and not just the cooked default
    7267             :      * returned by AddRelationNewConstraints, so that the right thing happens
    7268             :      * when a datatype's default applies.
    7269             :      *
    7270             :      * Note: it might seem that this should happen at the end of Phase 2, so
    7271             :      * that the effects of subsequent subcommands can be taken into account.
    7272             :      * It's intentional that we do it now, though.  The new column should be
    7273             :      * filled according to what is said in the ADD COLUMN subcommand, so that
    7274             :      * the effects are the same as if this subcommand had been run by itself
    7275             :      * and the later subcommands had been issued in new ALTER TABLE commands.
    7276             :      *
    7277             :      * We can skip this entirely for relations without storage, since Phase 3
    7278             :      * is certainly not going to touch them.  System attributes don't have
    7279             :      * interesting defaults, either.
    7280             :      */
    7281        2364 :     if (RELKIND_HAS_STORAGE(relkind))
    7282             :     {
    7283             :         /*
    7284             :          * For an identity column, we can't use build_column_default(),
    7285             :          * because the sequence ownership isn't set yet.  So do it manually.
    7286             :          */
    7287        2012 :         if (colDef->identity)
    7288             :         {
    7289          42 :             NextValueExpr *nve = makeNode(NextValueExpr);
    7290             : 
    7291          42 :             nve->seqid = RangeVarGetRelid(colDef->identitySequence, NoLock, false);
    7292          42 :             nve->typeId = attribute->atttypid;
    7293             : 
    7294          42 :             defval = (Expr *) nve;
    7295             : 
    7296             :             /* must do a rewrite for identity columns */
    7297          42 :             tab->rewrite |= AT_REWRITE_DEFAULT_VAL;
    7298             :         }
    7299             :         else
    7300        1970 :             defval = (Expr *) build_column_default(rel, attribute->attnum);
    7301             : 
    7302        2012 :         if (!defval && DomainHasConstraints(attribute->atttypid))
    7303             :         {
    7304             :             Oid         baseTypeId;
    7305             :             int32       baseTypeMod;
    7306             :             Oid         baseTypeColl;
    7307             : 
    7308           6 :             baseTypeMod = attribute->atttypmod;
    7309           6 :             baseTypeId = getBaseTypeAndTypmod(attribute->atttypid, &baseTypeMod);
    7310           6 :             baseTypeColl = get_typcollation(baseTypeId);
    7311           6 :             defval = (Expr *) makeNullConst(baseTypeId, baseTypeMod, baseTypeColl);
    7312           6 :             defval = (Expr *) coerce_to_target_type(NULL,
    7313             :                                                     (Node *) defval,
    7314             :                                                     baseTypeId,
    7315             :                                                     attribute->atttypid,
    7316             :                                                     attribute->atttypmod,
    7317             :                                                     COERCION_ASSIGNMENT,
    7318             :                                                     COERCE_IMPLICIT_CAST,
    7319             :                                                     -1);
    7320           6 :             if (defval == NULL) /* should not happen */
    7321           0 :                 elog(ERROR, "failed to coerce base type to domain");
    7322             :         }
    7323             : 
    7324        2012 :         if (defval)
    7325             :         {
    7326             :             NewColumnValue *newval;
    7327             : 
    7328         610 :             newval = (NewColumnValue *) palloc0(sizeof(NewColumnValue));
    7329         610 :             newval->attnum = attribute->attnum;
    7330         610 :             newval->expr = expression_planner(defval);
    7331         610 :             newval->is_generated = (colDef->generated != '\0');
    7332             : 
    7333         610 :             tab->newvals = lappend(tab->newvals, newval);
    7334             :         }
    7335             : 
    7336        2012 :         if (DomainHasConstraints(attribute->atttypid))
    7337          12 :             tab->rewrite |= AT_REWRITE_DEFAULT_VAL;
    7338             : 
    7339        2012 :         if (!TupleDescAttr(rel->rd_att, attribute->attnum - 1)->atthasmissing)
    7340             :         {
    7341             :             /*
    7342             :              * If the new column is NOT NULL, and there is no missing value,
    7343             :              * tell Phase 3 it needs to check for NULLs.
    7344             :              */
    7345        1558 :             tab->verify_new_notnull |= colDef->is_not_null;
    7346             :         }
    7347             :     }
    7348             : 
    7349             :     /*
    7350             :      * Add needed dependency entries for the new column.
    7351             :      */
    7352        2364 :     add_column_datatype_dependency(myrelid, newattnum, attribute->atttypid);
    7353        2364 :     add_column_collation_dependency(myrelid, newattnum, attribute->attcollation);
    7354             : 
    7355             :     /*
    7356             :      * Propagate to children as appropriate.  Unlike most other ALTER
    7357             :      * routines, we have to do this one level of recursion at a time; we can't
    7358             :      * use find_all_inheritors to do it in one pass.
    7359             :      */
    7360             :     children =
    7361        2364 :         find_inheritance_children(RelationGetRelid(rel), lockmode);
    7362             : 
    7363             :     /*
    7364             :      * If we are told not to recurse, there had better not be any child
    7365             :      * tables; else the addition would put them out of step.
    7366             :      */
    7367        2364 :     if (children && !recurse)
    7368          12 :         ereport(ERROR,
    7369             :                 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    7370             :                  errmsg("column must be added to child tables too")));
    7371             : 
    7372             :     /* Children should see column as singly inherited */
    7373        2352 :     if (!recursing)
    7374             :     {
    7375        1770 :         childcmd = copyObject(*cmd);
    7376        1770 :         colDef = castNode(ColumnDef, childcmd->def);
    7377        1770 :         colDef->inhcount = 1;
    7378        1770 :         colDef->is_local = false;
    7379             :     }
    7380             :     else
    7381         582 :         childcmd = *cmd;        /* no need to copy again */
    7382             : 
    7383        2970 :     foreach(child, children)
    7384             :     {
    7385         618 :         Oid         childrelid = lfirst_oid(child);
    7386             :         Relation    childrel;
    7387             :         AlteredTableInfo *childtab;
    7388             : 
    7389             :         /* find_inheritance_children already got lock */
    7390         618 :         childrel = table_open(childrelid, NoLock);
    7391         618 :         CheckAlterTableIsSafe(childrel);
    7392             : 
    7393             :         /* Find or create work queue entry for this table */
    7394         618 :         childtab = ATGetQueueEntry(wqueue, childrel);
    7395             : 
    7396             :         /* Recurse to child; return value is ignored */
    7397         618 :         ATExecAddColumn(wqueue, childtab, childrel,
    7398             :                         &childcmd, recurse, true,
    7399             :                         lockmode, cur_pass, context);
    7400             : 
    7401         618 :         table_close(childrel, NoLock);
    7402             :     }
    7403             : 
    7404        2352 :     ObjectAddressSubSet(address, RelationRelationId, myrelid, newattnum);
    7405        2352 :     return address;
    7406             : }
    7407             : 
    7408             : /*
    7409             :  * If a new or renamed column will collide with the name of an existing
    7410             :  * column and if_not_exists is false then error out, else do nothing.
    7411             :  */
    7412             : static bool
    7413        2940 : check_for_column_name_collision(Relation rel, const char *colname,
    7414             :                                 bool if_not_exists)
    7415             : {
    7416             :     HeapTuple   attTuple;
    7417             :     int         attnum;
    7418             : 
    7419             :     /*
    7420             :      * this test is deliberately not attisdropped-aware, since if one tries to
    7421             :      * add a column matching a dropped column name, it's gonna fail anyway.
    7422             :      */
    7423        2940 :     attTuple = SearchSysCache2(ATTNAME,
    7424             :                                ObjectIdGetDatum(RelationGetRelid(rel)),
    7425             :                                PointerGetDatum(colname));
    7426        2940 :     if (!HeapTupleIsValid(attTuple))
    7427        2844 :         return true;
    7428             : 
    7429          96 :     attnum = ((Form_pg_attribute) GETSTRUCT(attTuple))->attnum;
    7430          96 :     ReleaseSysCache(attTuple);
    7431             : 
    7432             :     /*
    7433             :      * We throw a different error message for conflicts with system column
    7434             :      * names, since they are normally not shown and the user might otherwise
    7435             :      * be confused about the reason for the conflict.
    7436             :      */
    7437          96 :     if (attnum <= 0)
    7438          12 :         ereport(ERROR,
    7439             :                 (errcode(ERRCODE_DUPLICATE_COLUMN),
    7440             :                  errmsg("column name \"%s\" conflicts with a system column name",
    7441             :                         colname)));
    7442             :     else
    7443             :     {
    7444          84 :         if (if_not_exists)
    7445             :         {
    7446          54 :             ereport(NOTICE,
    7447             :                     (errcode(ERRCODE_DUPLICATE_COLUMN),
    7448             :                      errmsg("column \"%s\" of relation \"%s\" already exists, skipping",
    7449             :                             colname, RelationGetRelationName(rel))));
    7450          54 :             return false;
    7451             :         }
    7452             : 
    7453          30 :         ereport(ERROR,
    7454             :                 (errcode(ERRCODE_DUPLICATE_COLUMN),
    7455             :                  errmsg("column \"%s\" of relation \"%s\" already exists",
    7456             :                         colname, RelationGetRelationName(rel))));
    7457             :     }
    7458             : 
    7459             :     return true;
    7460             : }
    7461             : 
    7462             : /*
    7463             :  * Install a column's dependency on its datatype.
    7464             :  */
    7465             : static void
    7466        3276 : add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid)
    7467             : {
    7468             :     ObjectAddress myself,
    7469             :                 referenced;
    7470             : 
    7471        3276 :     myself.classId = RelationRelationId;
    7472        3276 :     myself.objectId = relid;
    7473        3276 :     myself.objectSubId = attnum;
    7474        3276 :     referenced.classId = TypeRelationId;
    7475        3276 :     referenced.objectId = typid;
    7476        3276 :     referenced.objectSubId = 0;
    7477        3276 :     recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
    7478        3276 : }
    7479             : 
    7480             : /*
    7481             :  * Install a column's dependency on its collation.
    7482             :  */
    7483             : static void
    7484        3276 : add_column_collation_dependency(Oid relid, int32 attnum, Oid collid)
    7485             : {
    7486             :     ObjectAddress myself,
    7487             :                 referenced;
    7488             : 
    7489             :     /* We know the default collation is pinned, so don't bother recording it */
    7490        3276 :     if (OidIsValid(collid) && collid != DEFAULT_COLLATION_OID)
    7491             :     {
    7492          18 :         myself.classId = RelationRelationId;
    7493          18 :         myself.objectId = relid;
    7494          18 :         myself.objectSubId = attnum;
    7495          18 :         referenced.classId = CollationRelationId;
    7496          18 :         referenced.objectId = collid;
    7497          18 :         referenced.objectSubId = 0;
    7498          18 :         recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
    7499             :     }
    7500        3276 : }
    7501             : 
    7502             : /*
    7503             :  * ALTER TABLE ALTER COLUMN DROP NOT NULL
    7504             :  */
    7505             : 
    7506             : static void
    7507         224 : ATPrepDropNotNull(Relation rel, bool recurse, bool recursing)
    7508             : {
    7509             :     /*
    7510             :      * If the parent is a partitioned table, like check constraints, we do not
    7511             :      * support removing the NOT NULL while partitions exist.
    7512             :      */
    7513         224 :     if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
    7514             :     {
    7515          18 :         PartitionDesc partdesc = RelationGetPartitionDesc(rel, true);
    7516             : 
    7517             :         Assert(partdesc != NULL);
    7518          18 :         if (partdesc->nparts > 0 && !recurse && !recursing)
    7519           6 :             ereport(ERROR,
    7520             :                     (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    7521             :                      errmsg("cannot remove constraint from only the partitioned table when partitions exist"),
    7522             :                      errhint("Do not specify the ONLY keyword.")));
    7523             :     }
    7524         218 : }
    7525             : 
    7526             : /*
    7527             :  * Return the address of the modified column.  If the column was already
    7528             :  * nullable, InvalidObjectAddress is returned.
    7529             :  */
    7530             : static ObjectAddress
    7531         200 : ATExecDropNotNull(Relation rel, const char *colName, LOCKMODE lockmode)
    7532             : {
    7533             :     HeapTuple   tuple;
    7534             :     Form_pg_attribute attTup;
    7535             :     AttrNumber  attnum;
    7536             :     Relation    attr_rel;
    7537             :     List       *indexoidlist;
    7538             :     ObjectAddress address;
    7539             : 
    7540             :     /*
    7541             :      * lookup the attribute
    7542             :      */
    7543         200 :     attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
    7544             : 
    7545         200 :     tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
    7546         200 :     if (!HeapTupleIsValid(tuple))
    7547          18 :         ereport(ERROR,
    7548             :                 (errcode(ERRCODE_UNDEFINED_COLUMN),
    7549             :                  errmsg("column \"%s\" of relation \"%s\" does not exist",
    7550             :                         colName, RelationGetRelationName(rel))));
    7551         182 :     attTup = (Form_pg_attribute) GETSTRUCT(tuple);
    7552         182 :     attnum = attTup->attnum;
    7553             : 
    7554             :     /* Prevent them from altering a system attribute */
    7555         182 :     if (attnum <= 0)
    7556           0 :         ereport(ERROR,
    7557             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    7558             :                  errmsg("cannot alter system column \"%s\"",
    7559             :                         colName)));
    7560             : 
    7561         182 :     if (attTup->attidentity)
    7562          18 :         ereport(ERROR,
    7563             :                 (errcode(ERRCODE_SYNTAX_ERROR),
    7564             :                  errmsg("column \"%s\" of relation \"%s\" is an identity column",
    7565             :                         colName, RelationGetRelationName(rel))));
    7566             : 
    7567             :     /*
    7568             :      * Check that the attribute is not in a primary key or in an index used as
    7569             :      * a replica identity.
    7570             :      *
    7571             :      * Note: we'll throw error even if the pkey index is not valid.
    7572             :      */
    7573             : 
    7574             :     /* Loop over all indexes on the relation */
    7575         164 :     indexoidlist = RelationGetIndexList(rel);
    7576             : 
    7577         328 :     foreach_oid(indexoid, indexoidlist)
    7578             :     {
    7579             :         HeapTuple   indexTuple;
    7580             :         Form_pg_index indexStruct;
    7581             : 
    7582          24 :         indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexoid));
    7583          24 :         if (!HeapTupleIsValid(indexTuple))
    7584           0 :             elog(ERROR, "cache lookup failed for index %u", indexoid);
    7585          24 :         indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
    7586             : 
    7587             :         /*
    7588             :          * If the index is not a primary key or an index used as replica
    7589             :          * identity, skip the check.
    7590             :          */
    7591          24 :         if (indexStruct->indisprimary || indexStruct->indisreplident)
    7592             :         {
    7593             :             /*
    7594             :              * Loop over each attribute in the primary key or the index used
    7595             :              * as replica identity and see if it matches the to-be-altered
    7596             :              * attribute.
    7597             :              */
    7598          24 :             for (int i = 0; i < indexStruct->indnkeyatts; i++)
    7599             :             {
    7600          18 :                 if (indexStruct->indkey.values[i] == attnum)
    7601             :                 {
    7602          12 :                     if (indexStruct->indisprimary)
    7603           6 :                         ereport(ERROR,
    7604             :                                 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    7605             :                                  errmsg("column \"%s\" is in a primary key",
    7606             :                                         colName)));
    7607             :                     else
    7608           6 :                         ereport(ERROR,
    7609             :                                 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    7610             :                                  errmsg("column \"%s\" is in index used as replica identity",
    7611             :                                         colName)));
    7612             :                 }
    7613             :             }
    7614             :         }
    7615             : 
    7616          12 :         ReleaseSysCache(indexTuple);
    7617             :     }
    7618             : 
    7619         152 :     list_free(indexoidlist);
    7620             : 
    7621             :     /* If rel is partition, shouldn't drop NOT NULL if parent has the same */
    7622         152 :     if (rel->rd_rel->relispartition)
    7623             :     {
    7624          18 :         Oid         parentId = get_partition_parent(RelationGetRelid(rel), false);
    7625          18 :         Relation    parent = table_open(parentId, AccessShareLock);
    7626          18 :         TupleDesc   tupDesc = RelationGetDescr(parent);
    7627             :         AttrNumber  parent_attnum;
    7628             : 
    7629          18 :         parent_attnum = get_attnum(parentId, colName);
    7630          18 :         if (TupleDescAttr(tupDesc, parent_attnum - 1)->attnotnull)
    7631          18 :             ereport(ERROR,
    7632             :                     (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    7633             :                      errmsg("column \"%s\" is marked NOT NULL in parent table",
    7634             :                             colName)));
    7635           0 :         table_close(parent, AccessShareLock);
    7636             :     }
    7637             : 
    7638             :     /*
    7639             :      * Okay, actually perform the catalog change ... if needed
    7640             :      */
    7641         134 :     if (attTup->attnotnull)
    7642             :     {
    7643         128 :         attTup->attnotnull = false;
    7644             : 
    7645         128 :         CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
    7646             : 
    7647         128 :         ObjectAddressSubSet(address, RelationRelationId,
    7648             :                             RelationGetRelid(rel), attnum);
    7649             :     }
    7650             :     else
    7651           6 :         address = InvalidObjectAddress;
    7652             : 
    7653         134 :     InvokeObjectPostAlterHook(RelationRelationId,
    7654             :                               RelationGetRelid(rel), attnum);
    7655             : 
    7656         134 :     table_close(attr_rel, RowExclusiveLock);
    7657             : 
    7658         134 :     return address;
    7659             : }
    7660             : 
    7661             : /*
    7662             :  * ALTER TABLE ALTER COLUMN SET NOT NULL
    7663             :  */
    7664             : 
    7665             : static void
    7666        7598 : ATPrepSetNotNull(List **wqueue, Relation rel,
    7667             :                  AlterTableCmd *cmd, bool recurse, bool recursing,
    7668             :                  LOCKMODE lockmode, AlterTableUtilityContext *context)
    7669             : {
    7670             :     /*
    7671             :      * If we're already recursing, there's nothing to do; the topmost
    7672             :      * invocation of ATSimpleRecursion already visited all children.
    7673             :      */
    7674        7598 :     if (recursing)
    7675         168 :         return;
    7676             : 
    7677             :     /*
    7678             :      * If the target column is already marked NOT NULL, we can skip recursing
    7679             :      * to children, because their columns should already be marked NOT NULL as
    7680             :      * well.  But there's no point in checking here unless the relation has
    7681             :      * some children; else we can just wait till execution to check.  (If it
    7682             :      * does have children, however, this can save taking per-child locks
    7683             :      * unnecessarily.  This greatly improves concurrency in some parallel
    7684             :      * restore scenarios.)
    7685             :      *
    7686             :      * Unfortunately, we can only apply this optimization to partitioned
    7687             :      * tables, because traditional inheritance doesn't enforce that child
    7688             :      * columns be NOT NULL when their parent is.  (That's a bug that should
    7689             :      * get fixed someday.)
    7690             :      */
    7691        7430 :     if (rel->rd_rel->relhassubclass &&
    7692         200 :         rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
    7693             :     {
    7694             :         HeapTuple   tuple;
    7695             :         bool        attnotnull;
    7696             : 
    7697         156 :         tuple = SearchSysCacheAttName(RelationGetRelid(rel), cmd->name);
    7698             : 
    7699             :         /* Might as well throw the error now, if name is bad */
    7700         156 :         if (!HeapTupleIsValid(tuple))
    7701           0 :             ereport(ERROR,
    7702             :                     (errcode(ERRCODE_UNDEFINED_COLUMN),
    7703             :                      errmsg("column \"%s\" of relation \"%s\" does not exist",
    7704             :                             cmd->name, RelationGetRelationName(rel))));
    7705             : 
    7706         156 :         attnotnull = ((Form_pg_attribute) GETSTRUCT(tuple))->attnotnull;
    7707         156 :         ReleaseSysCache(tuple);
    7708         156 :         if (attnotnull)
    7709          56 :             return;
    7710             :     }
    7711             : 
    7712             :     /*
    7713             :      * If we have ALTER TABLE ONLY ... SET NOT NULL on a partitioned table,
    7714             :      * apply ALTER TABLE ... CHECK NOT NULL to every child.  Otherwise, use
    7715             :      * normal recursion logic.
    7716             :      */
    7717        7374 :     if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&
    7718         136 :         !recurse)
    7719          36 :     {
    7720          36 :         AlterTableCmd *newcmd = makeNode(AlterTableCmd);
    7721             : 
    7722          36 :         newcmd->subtype = AT_CheckNotNull;
    7723          36 :         newcmd->name = pstrdup(cmd->name);
    7724          36 :         ATSimpleRecursion(wqueue, rel, newcmd, true, lockmode, context);
    7725             :     }
    7726             :     else
    7727        7338 :         ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
    7728             : }
    7729             : 
    7730             : /*
    7731             :  * Return the address of the modified column.  If the column was already NOT
    7732             :  * NULL, InvalidObjectAddress is returned.
    7733             :  */
    7734             : static ObjectAddress
    7735        7598 : ATExecSetNotNull(AlteredTableInfo *tab, Relation rel,
    7736             :                  const char *colName, LOCKMODE lockmode)
    7737             : {
    7738             :     HeapTuple   tuple;
    7739             :     AttrNumber  attnum;
    7740             :     Relation    attr_rel;
    7741             :     ObjectAddress address;
    7742             : 
    7743             :     /*
    7744             :      * lookup the attribute
    7745             :      */
    7746        7598 :     attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
    7747             : 
    7748        7598 :     tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
    7749             : 
    7750        7598 :     if (!HeapTupleIsValid(tuple))
    7751          36 :         ereport(ERROR,
    7752             :                 (errcode(ERRCODE_UNDEFINED_COLUMN),
    7753             :                  errmsg("column \"%s\" of relation \"%s\" does not exist",
    7754             :                         colName, RelationGetRelationName(rel))));
    7755             : 
    7756        7562 :     attnum = ((Form_pg_attribute) GETSTRUCT(tuple))->attnum;
    7757             : 
    7758             :     /* Prevent them from altering a system attribute */
    7759        7562 :     if (attnum <= 0)
    7760           0 :         ereport(ERROR,
    7761             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    7762             :                  errmsg("cannot alter system column \"%s\"",
    7763             :                         colName)));
    7764             : 
    7765             :     /*
    7766             :      * Okay, actually perform the catalog change ... if needed
    7767             :      */
    7768        7562 :     if (!((Form_pg_attribute) GETSTRUCT(tuple))->attnotnull)
    7769             :     {
    7770         928 :         ((Form_pg_attribute) GETSTRUCT(tuple))->attnotnull = true;
    7771             : 
    7772         928 :         CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
    7773             : 
    7774             :         /*
    7775             :          * Ordinarily phase 3 must ensure that no NULLs exist in columns that
    7776             :          * are set NOT NULL; however, if we can find a constraint which proves
    7777             :          * this then we can skip that.  We needn't bother looking if we've
    7778             :          * already found that we must verify some other not-null constraint.
    7779             :          */
    7780         928 :         if (!tab->verify_new_notnull &&
    7781         836 :             !NotNullImpliedByRelConstraints(rel, (Form_pg_attribute) GETSTRUCT(tuple)))
    7782             :         {
    7783             :             /* Tell Phase 3 it needs to test the constraint */
    7784         806 :             tab->verify_new_notnull = true;
    7785             :         }
    7786             : 
    7787         928 :         ObjectAddressSubSet(address, RelationRelationId,
    7788             :                             RelationGetRelid(rel), attnum);
    7789             :     }
    7790             :     else
    7791        6634 :         address = InvalidObjectAddress;
    7792             : 
    7793        7562 :     InvokeObjectPostAlterHook(RelationRelationId,
    7794             :                               RelationGetRelid(rel), attnum);
    7795             : 
    7796        7562 :     table_close(attr_rel, RowExclusiveLock);
    7797             : 
    7798        7562 :     return address;
    7799             : }
    7800             : 
    7801             : /*
    7802             :  * ALTER TABLE ALTER COLUMN CHECK NOT NULL
    7803             :  *
    7804             :  * This doesn't exist in the grammar, but we generate AT_CheckNotNull
    7805             :  * commands against the partitions of a partitioned table if the user
    7806             :  * writes ALTER TABLE ONLY ... SET NOT NULL on the partitioned table,
    7807             :  * or tries to create a primary key on it (which internally creates
    7808             :  * AT_SetNotNull on the partitioned table).   Such a command doesn't
    7809             :  * allow us to actually modify any partition, but we want to let it
    7810             :  * go through if the partitions are already properly marked.
    7811             :  *
    7812             :  * In future, this might need to adjust the child table's state, likely
    7813             :  * by incrementing an inheritance count for the attnotnull constraint.
    7814             :  * For now we need only check for the presence of the flag.
    7815             :  */
    7816             : static void
    7817          30 : ATExecCheckNotNull(AlteredTableInfo *tab, Relation rel,
    7818             :                    const char *colName, LOCKMODE lockmode)
    7819             : {
    7820             :     HeapTuple   tuple;
    7821             : 
    7822          30 :     tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
    7823             : 
    7824          30 :     if (!HeapTupleIsValid(tuple))
    7825           0 :         ereport(ERROR,
    7826             :                 errcode(ERRCODE_UNDEFINED_COLUMN),
    7827             :                 errmsg("column \"%s\" of relation \"%s\" does not exist",
    7828             :                        colName, RelationGetRelationName(rel)));
    7829             : 
    7830          30 :     if (!((Form_pg_attribute) GETSTRUCT(tuple))->attnotnull)
    7831          12 :         ereport(ERROR,
    7832             :                 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    7833             :                  errmsg("constraint must be added to child tables too"),
    7834             :                  errdetail("Column \"%s\" of relation \"%s\" is not already NOT NULL.",
    7835             :                            colName, RelationGetRelationName(rel)),
    7836             :                  errhint("Do not specify the ONLY keyword.")));
    7837             : 
    7838          18 :     ReleaseSysCache(tuple);
    7839          18 : }
    7840             : 
    7841             : /*
    7842             :  * NotNullImpliedByRelConstraints
    7843             :  *      Does rel's existing constraints imply NOT NULL for the given attribute?
    7844             :  */
    7845             : static bool
    7846         836 : NotNullImpliedByRelConstraints(Relation rel, Form_pg_attribute attr)
    7847             : {
    7848         836 :     NullTest   *nnulltest = makeNode(NullTest);
    7849             : 
    7850        1672 :     nnulltest->arg = (Expr *) makeVar(1,
    7851         836 :                                       attr->attnum,
    7852             :                                       attr->atttypid,
    7853             :                                       attr->atttypmod,
    7854             :                                       attr->attcollation,
    7855             :                                       0);
    7856         836 :     nnulltest->nulltesttype = IS_NOT_NULL;
    7857             : 
    7858             :     /*
    7859             :      * argisrow = false is correct even for a composite column, because
    7860             :      * attnotnull does not represent a SQL-spec IS NOT NULL test in such a
    7861             :      * case, just IS DISTINCT FROM NULL.
    7862             :      */
    7863         836 :     nnulltest->argisrow = false;
    7864         836 :     nnulltest->location = -1;
    7865             : 
    7866         836 :     if (ConstraintImpliedByRelConstraint(rel, list_make1(nnulltest), NIL))
    7867             :     {
    7868          30 :         ereport(DEBUG1,
    7869             :                 (errmsg_internal("existing constraints on column \"%s.%s\" are sufficient to prove that it does not contain nulls",
    7870             :                                  RelationGetRelationName(rel), NameStr(attr->attname))));
    7871          30 :         return true;
    7872             :     }
    7873             : 
    7874         806 :     return false;
    7875             : }
    7876             : 
    7877             : /*
    7878             :  * ALTER TABLE ALTER COLUMN SET/DROP DEFAULT
    7879             :  *
    7880             :  * Return the address of the affected column.
    7881             :  */
    7882             : static ObjectAddress
    7883         544 : ATExecColumnDefault(Relation rel, const char *colName,
    7884             :                     Node *newDefault, LOCKMODE lockmode)
    7885             : {
    7886         544 :     TupleDesc   tupdesc = RelationGetDescr(rel);
    7887             :     AttrNumber  attnum;
    7888             :     ObjectAddress address;
    7889             : 
    7890             :     /*
    7891             :      * get the number of the attribute
    7892             :      */
    7893         544 :     attnum = get_attnum(RelationGetRelid(rel), colName);
    7894         544 :     if (attnum == InvalidAttrNumber)
    7895          30 :         ereport(ERROR,
    7896             :                 (errcode(ERRCODE_UNDEFINED_COLUMN),
    7897             :                  errmsg("column \"%s\" of relation \"%s\" does not exist",
    7898             :                         colName, RelationGetRelationName(rel))));
    7899             : 
    7900             :     /* Prevent them from altering a system attribute */
    7901         514 :     if (attnum <= 0)
    7902           0 :         ereport(ERROR,
    7903             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    7904             :                  errmsg("cannot alter system column \"%s\"",
    7905             :                         colName)));
    7906             : 
    7907         514 :     if (TupleDescAttr(tupdesc, attnum - 1)->attidentity)
    7908          18 :         ereport(ERROR,
    7909             :                 (errcode(ERRCODE_SYNTAX_ERROR),
    7910             :                  errmsg("column \"%s\" of relation \"%s\" is an identity column",
    7911             :                         colName, RelationGetRelationName(rel)),
    7912             :         /* translator: %s is an SQL ALTER command */
    7913             :                  newDefault ? 0 : errhint("Use %s instead.",
    7914             :                                           "ALTER TABLE ... ALTER COLUMN ... DROP IDENTITY")));
    7915             : 
    7916         496 :     if (TupleDescAttr(tupdesc, attnum - 1)->attgenerated)
    7917           6 :         ereport(ERROR,
    7918             :                 (errcode(ERRCODE_SYNTAX_ERROR),
    7919             :                  errmsg("column \"%s\" of relation \"%s\" is a generated column",
    7920             :                         colName, RelationGetRelationName(rel)),
    7921             :                  newDefault ?
    7922             :         /* translator: %s is an SQL ALTER command */
    7923             :                  errhint("Use %s instead.", "ALTER TABLE ... ALTER COLUMN ... SET EXPRESSION") :
    7924             :                  (TupleDescAttr(tupdesc, attnum - 1)->attgenerated == ATTRIBUTE_GENERATED_STORED ?
    7925             :                   errhint("Use %s instead.", "ALTER TABLE ... ALTER COLUMN ... DROP EXPRESSION") : 0)));
    7926             : 
    7927             :     /*
    7928             :      * Remove any old default for the column.  We use RESTRICT here for
    7929             :      * safety, but at present we do not expect anything to depend on the
    7930             :      * default.
    7931             :      *
    7932             :      * We treat removing the existing default as an internal operation when it
    7933             :      * is preparatory to adding a new default, but as a user-initiated
    7934             :      * operation when the user asked for a drop.
    7935             :      */
    7936         490 :     RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT, false,
    7937             :                       newDefault != NULL);
    7938             : 
    7939         490 :     if (newDefault)
    7940             :     {
    7941             :         /* SET DEFAULT */
    7942             :         RawColumnDefault *rawEnt;
    7943             : 
    7944         316 :         rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
    7945         316 :         rawEnt->attnum = attnum;
    7946         316 :         rawEnt->raw_default = newDefault;
    7947         316 :         rawEnt->missingMode = false;
    7948         316 :         rawEnt->generated = '\0';
    7949             : 
    7950             :         /*
    7951             :          * This function is intended for CREATE TABLE, so it processes a
    7952             :          * _list_ of defaults, but we just do one.
    7953             :          */
    7954         316 :         AddRelationNewConstraints(rel, list_make1(rawEnt), NIL,
    7955             :                                   false, true, false, NULL);
    7956             :     }
    7957             : 
    7958         484 :     ObjectAddressSubSet(address, RelationRelationId,
    7959             :                         RelationGetRelid(rel), attnum);
    7960         484 :     return address;
    7961             : }
    7962             : 
    7963             : /*
    7964             :  * Add a pre-cooked default expression.
    7965             :  *
    7966             :  * Return the address of the affected column.
    7967             :  */
    7968             : static ObjectAddress
    7969         110 : ATExecCookedColumnDefault(Relation rel, AttrNumber attnum,
    7970             :                           Node *newDefault)
    7971             : {
    7972             :     ObjectAddress address;
    7973             : 
    7974             :     /* We assume no checking is required */
    7975             : 
    7976             :     /*
    7977             :      * Remove any old default for the column.  We use RESTRICT here for
    7978             :      * safety, but at present we do not expect anything to depend on the
    7979             :      * default.  (In ordinary cases, there could not be a default in place
    7980             :      * anyway, but it's possible when combining LIKE with inheritance.)
    7981             :      */
    7982         110 :     RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT, false,
    7983             :                       true);
    7984             : 
    7985         110 :     (void) StoreAttrDefault(rel, attnum, newDefault, true, false);
    7986             : 
    7987         110 :     ObjectAddressSubSet(address, RelationRelationId,
    7988             :                         RelationGetRelid(rel), attnum);
    7989         110 :     return address;
    7990             : }
    7991             : 
    7992             : /*
    7993             :  * ALTER TABLE ALTER COLUMN ADD IDENTITY
    7994             :  *
    7995             :  * Return the address of the affected column.
    7996             :  */
    7997             : static ObjectAddress
    7998         156 : ATExecAddIdentity(Relation rel, const char *colName,
    7999             :                   Node *def, LOCKMODE lockmode, bool recurse, bool recursing)
    8000             : {
    8001             :     Relation    attrelation;
    8002             :     HeapTuple   tuple;
    8003             :     Form_pg_attribute attTup;
    8004             :     AttrNumber  attnum;
    8005             :     ObjectAddress address;
    8006         156 :     ColumnDef  *cdef = castNode(ColumnDef, def);
    8007             :     bool        ispartitioned;
    8008             : 
    8009         156 :     ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
    8010         156 :     if (ispartitioned && !recurse)
    8011           6 :         ereport(ERROR,
    8012             :                 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    8013             :                  errmsg("cannot add identity to a column of only the partitioned table"),
    8014             :                  errhint("Do not specify the ONLY keyword.")));
    8015             : 
    8016         150 :     if (rel->rd_rel->relispartition && !recursing)
    8017          12 :         ereport(ERROR,
    8018             :                 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    8019             :                 errmsg("cannot add identity to a column of a partition"));
    8020             : 
    8021         138 :     attrelation = table_open(AttributeRelationId, RowExclusiveLock);
    8022             : 
    8023         138 :     tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
    8024         138 :     if (!HeapTupleIsValid(tuple))
    8025           0 :         ereport(ERROR,
    8026             :                 (errcode(ERRCODE_UNDEFINED_COLUMN),
    8027             :                  errmsg("column \"%s\" of relation \"%s\" does not exist",
    8028             :                         colName, RelationGetRelationName(rel))));
    8029         138 :     attTup = (Form_pg_attribute) GETSTRUCT(tuple);
    8030         138 :     attnum = attTup->attnum;
    8031             : 
    8032             :     /* Can't alter a system attribute */
    8033         138 :     if (attnum <= 0)
    8034           0 :         ereport(ERROR,
    8035             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    8036             :                  errmsg("cannot alter system column \"%s\"",
    8037             :                         colName)));
    8038             : 
    8039             :     /*
    8040             :      * Creating a column as identity implies NOT NULL, so adding the identity
    8041             :      * to an existing column that is not NOT NULL would create a state that
    8042             :      * cannot be reproduced without contortions.
    8043             :      */
    8044         138 :     if (!attTup->attnotnull)
    8045           6 :         ereport(ERROR,
    8046             :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    8047             :                  errmsg("column \"%s\" of relation \"%s\" must be declared NOT NULL before identity can be added",
    8048             :                         colName, RelationGetRelationName(rel))));
    8049             : 
    8050         132 :     if (attTup->attidentity)
    8051          18 :         ereport(ERROR,
    8052             :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    8053             :                  errmsg("column \"%s\" of relation \"%s\" is already an identity column",
    8054             :                         colName, RelationGetRelationName(rel))));
    8055             : 
    8056         114 :     if (attTup->atthasdef)
    8057           6 :         ereport(ERROR,
    8058             :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    8059             :                  errmsg("column \"%s\" of relation \"%s\" already has a default value",
    8060             :                         colName, RelationGetRelationName(rel))));
    8061             : 
    8062         108 :     attTup->attidentity = cdef->identity;
    8063         108 :     CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
    8064             : 
    8065         108 :     InvokeObjectPostAlterHook(RelationRelationId,
    8066             :                               RelationGetRelid(rel),
    8067             :                               attTup->attnum);
    8068         108 :     ObjectAddressSubSet(address, RelationRelationId,
    8069             :                         RelationGetRelid(rel), attnum);
    8070         108 :     heap_freetuple(tuple);
    8071             : 
    8072         108 :     table_close(attrelation, RowExclusiveLock);
    8073             : 
    8074             :     /*
    8075             :      * Recurse to propagate the identity column to partitions.  Identity is
    8076             :      * not inherited in regular inheritance children.
    8077             :      */
    8078         108 :     if (recurse && ispartitioned)
    8079             :     {
    8080             :         List       *children;
    8081             :         ListCell   *lc;
    8082             : 
    8083          10 :         children = find_inheritance_children(RelationGetRelid(rel), lockmode);
    8084             : 
    8085          16 :         foreach(lc, children)
    8086             :         {
    8087             :             Relation    childrel;
    8088             : 
    8089           6 :             childrel = table_open(lfirst_oid(lc), NoLock);
    8090           6 :             ATExecAddIdentity(childrel, colName, def, lockmode, recurse, true);
    8091           6 :             table_close(childrel, NoLock);
    8092             :         }
    8093             :     }
    8094             : 
    8095         108 :     return address;
    8096             : }
    8097             : 
    8098             : /*
    8099             :  * ALTER TABLE ALTER COLUMN SET { GENERATED or sequence options }
    8100             :  *
    8101             :  * Return the address of the affected column.
    8102             :  */
    8103             : static ObjectAddress
    8104          74 : ATExecSetIdentity(Relation rel, const char *colName, Node *def,
    8105             :                   LOCKMODE lockmode, bool recurse, bool recursing)
    8106             : {
    8107             :     ListCell   *option;
    8108          74 :     DefElem    *generatedEl = NULL;
    8109             :     HeapTuple   tuple;
    8110             :     Form_pg_attribute attTup;
    8111             :     AttrNumber  attnum;
    8112             :     Relation    attrelation;
    8113             :     ObjectAddress address;
    8114             :     bool        ispartitioned;
    8115             : 
    8116          74 :     ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
    8117          74 :     if (ispartitioned && !recurse)
    8118           6 :         ereport(ERROR,
    8119             :                 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    8120             :                  errmsg("cannot change identity column of only the partitioned table"),
    8121             :                  errhint("Do not specify the ONLY keyword.")));
    8122             : 
    8123          68 :     if (rel->rd_rel->relispartition && !recursing)
    8124          12 :         ereport(ERROR,
    8125             :                 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    8126             :                 errmsg("cannot change identity column of a partition"));
    8127             : 
    8128         100 :     foreach(option, castNode(List, def))
    8129             :     {
    8130          44 :         DefElem    *defel = lfirst_node(DefElem, option);
    8131             : 
    8132          44 :         if (strcmp(defel->defname, "generated") == 0)
    8133             :         {
    8134          44 :             if (generatedEl)
    8135           0 :                 ereport(ERROR,
    8136             :                         (errcode(ERRCODE_SYNTAX_ERROR),
    8137             :                          errmsg("conflicting or redundant options")));
    8138          44 :             generatedEl = defel;
    8139             :         }
    8140             :         else
    8141           0 :             elog(ERROR, "option \"%s\" not recognized",
    8142             :                  defel->defname);
    8143             :     }
    8144             : 
    8145             :     /*
    8146             :      * Even if there is nothing to change here, we run all the checks.  There
    8147             :      * will be a subsequent ALTER SEQUENCE that relies on everything being
    8148             :      * there.
    8149             :      */
    8150             : 
    8151          56 :     attrelation = table_open(AttributeRelationId, RowExclusiveLock);
    8152          56 :     tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
    8153          56 :     if (!HeapTupleIsValid(tuple))
    8154           0 :         ereport(ERROR,
    8155             :                 (errcode(ERRCODE_UNDEFINED_COLUMN),
    8156             :                  errmsg("column \"%s\" of relation \"%s\" does not exist",
    8157             :                         colName, RelationGetRelationName(rel))));
    8158             : 
    8159          56 :     attTup = (Form_pg_attribute) GETSTRUCT(tuple);
    8160          56 :     attnum = attTup->attnum;
    8161             : 
    8162          56 :     if (attnum <= 0)
    8163           0 :         ereport(ERROR,
    8164             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    8165             :                  errmsg("cannot alter system column \"%s\"",
    8166             :                         colName)));
    8167             : 
    8168          56 :     if (!attTup->attidentity)
    8169           6 :         ereport(ERROR,
    8170             :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    8171             :                  errmsg("column \"%s\" of relation \"%s\" is not an identity column",
    8172             :                         colName, RelationGetRelationName(rel))));
    8173             : 
    8174          50 :     if (generatedEl)
    8175             :     {
    8176          44 :         attTup->attidentity = defGetInt32(generatedEl);
    8177          44 :         CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
    8178             : 
    8179          44 :         InvokeObjectPostAlterHook(RelationRelationId,
    8180             :                                   RelationGetRelid(rel),
    8181             :                                   attTup->attnum);
    8182          44 :         ObjectAddressSubSet(address, RelationRelationId,
    8183             :                             RelationGetRelid(rel), attnum);
    8184             :     }
    8185             :     else
    8186           6 :         address = InvalidObjectAddress;
    8187             : 
    8188          50 :     heap_freetuple(tuple);
    8189          50 :     table_close(attrelation, RowExclusiveLock);
    8190             : 
    8191             :     /*
    8192             :      * Recurse to propagate the identity change to partitions. Identity is not
    8193             :      * inherited in regular inheritance children.
    8194             :      */
    8195          50 :     if (generatedEl && recurse && ispartitioned)
    8196             :     {
    8197             :         List       *children;
    8198             :         ListCell   *lc;
    8199             : 
    8200           6 :         children = find_inheritance_children(RelationGetRelid(rel), lockmode);
    8201             : 
    8202          18 :         foreach(lc, children)
    8203             :         {
    8204             :             Relation    childrel;
    8205             : 
    8206          12 :             childrel = table_open(lfirst_oid(lc), NoLock);
    8207          12 :             ATExecSetIdentity(childrel, colName, def, lockmode, recurse, true);
    8208          12 :             table_close(childrel, NoLock);
    8209             :         }
    8210             :     }
    8211             : 
    8212          50 :     return address;
    8213             : }
    8214             : 
    8215             : /*
    8216             :  * ALTER TABLE ALTER COLUMN DROP IDENTITY
    8217             :  *
    8218             :  * Return the address of the affected column.
    8219             :  */
    8220             : static ObjectAddress
    8221          92 : ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode,
    8222             :                    bool recurse, bool recursing)
    8223             : {
    8224             :     HeapTuple   tuple;
    8225             :     Form_pg_attribute attTup;
    8226             :     AttrNumber  attnum;
    8227             :     Relation    attrelation;
    8228             :     ObjectAddress address;
    8229             :     Oid         seqid;
    8230             :     ObjectAddress seqaddress;
    8231             :     bool        ispartitioned;
    8232             : 
    8233          92 :     ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
    8234          92 :     if (ispartitioned && !recurse)
    8235           6 :         ereport(ERROR,
    8236             :                 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    8237             :                  errmsg("cannot drop identity from a column of only the partitioned table"),
    8238             :                  errhint("Do not specify the ONLY keyword.")));
    8239             : 
    8240          86 :     if (rel->rd_rel->relispartition && !recursing)
    8241           6 :         ereport(ERROR,
    8242             :                 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    8243             :                 errmsg("cannot drop identity from a column of a partition"));
    8244             : 
    8245          80 :     attrelation = table_open(AttributeRelationId, RowExclusiveLock);
    8246          80 :     tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
    8247          80 :     if (!HeapTupleIsValid(tuple))
    8248           0 :         ereport(ERROR,
    8249             :                 (errcode(ERRCODE_UNDEFINED_COLUMN),
    8250             :                  errmsg("column \"%s\" of relation \"%s\" does not exist",
    8251             :                         colName, RelationGetRelationName(rel))));
    8252             : 
    8253          80 :     attTup = (Form_pg_attribute) GETSTRUCT(tuple);
    8254          80 :     attnum = attTup->attnum;
    8255             : 
    8256          80 :     if (attnum <= 0)
    8257           0 :         ereport(ERROR,
    8258             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    8259             :                  errmsg("cannot alter system column \"%s\"",
    8260             :                         colName)));
    8261             : 
    8262          80 :     if (!attTup->attidentity)
    8263             :     {
    8264          12 :         if (!missing_ok)
    8265           6 :             ereport(ERROR,
    8266             :                     (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    8267             :                      errmsg("column \"%s\" of relation \"%s\" is not an identity column",
    8268             :                             colName, RelationGetRelationName(rel))));
    8269             :         else
    8270             :         {
    8271           6 :             ereport(NOTICE,
    8272             :                     (errmsg("column \"%s\" of relation \"%s\" is not an identity column, skipping",
    8273             :                             colName, RelationGetRelationName(rel))));
    8274           6 :             heap_freetuple(tuple);
    8275           6 :             table_close(attrelation, RowExclusiveLock);
    8276           6 :             return InvalidObjectAddress;
    8277             :         }
    8278             :     }
    8279             : 
    8280          68 :     attTup->attidentity = '\0';
    8281          68 :     CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
    8282             : 
    8283          68 :     InvokeObjectPostAlterHook(RelationRelationId,
    8284             :                               RelationGetRelid(rel),
    8285             :                               attTup->attnum);
    8286          68 :     ObjectAddressSubSet(address, RelationRelationId,
    8287             :                         RelationGetRelid(rel), attnum);
    8288          68 :     heap_freetuple(tuple);
    8289             : 
    8290          68 :     table_close(attrelation, RowExclusiveLock);
    8291             : 
    8292             :     /*
    8293             :      * Recurse to drop the identity from column in partitions.  Identity is
    8294             :      * not inherited in regular inheritance children so ignore them.
    8295             :      */
    8296          68 :     if (recurse && ispartitioned)
    8297             :     {
    8298             :         List       *children;
    8299             :         ListCell   *lc;
    8300             : 
    8301           6 :         children = find_inheritance_children(RelationGetRelid(rel), lockmode);
    8302             : 
    8303          12 :         foreach(lc, children)
    8304             :         {
    8305             :             Relation    childrel;
    8306             : 
    8307           6 :             childrel = table_open(lfirst_oid(lc), NoLock);
    8308           6 :             ATExecDropIdentity(childrel, colName, false, lockmode, recurse, true);
    8309           6 :             table_close(childrel, NoLock);
    8310             :         }
    8311             :     }
    8312             : 
    8313          68 :     if (!recursing)
    8314             :     {
    8315             :         /* drop the internal sequence */
    8316          32 :         seqid = getIdentitySequence(rel, attnum, false);
    8317          32 :         deleteDependencyRecordsForClass(RelationRelationId, seqid,
    8318             :                                         RelationRelationId, DEPENDENCY_INTERNAL);
    8319          32 :         CommandCounterIncrement();
    8320          32 :         seqaddress.classId = RelationRelationId;
    8321          32 :         seqaddress.objectId = seqid;
    8322          32 :         seqaddress.objectSubId = 0;
    8323          32 :         performDeletion(&seqaddress, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
    8324             :     }
    8325             : 
    8326          68 :     return address;
    8327             : }
    8328             : 
    8329             : /*
    8330             :  * ALTER TABLE ALTER COLUMN SET EXPRESSION
    8331             :  *
    8332             :  * Return the address of the affected column.
    8333             :  */
    8334             : static ObjectAddress
    8335          84 : ATExecSetExpression(AlteredTableInfo *tab, Relation rel, const char *colName,
    8336             :                     Node *newExpr, LOCKMODE lockmode)
    8337             : {
    8338             :     HeapTuple   tuple;
    8339             :     Form_pg_attribute attTup;
    8340             :     AttrNumber  attnum;
    8341             :     Oid         attrdefoid;
    8342             :     ObjectAddress address;
    8343             :     Expr       *defval;
    8344             :     NewColumnValue *newval;
    8345             :     RawColumnDefault *rawEnt;
    8346             : 
    8347          84 :     tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
    8348          84 :     if (!HeapTupleIsValid(tuple))
    8349           0 :         ereport(ERROR,
    8350             :                 (errcode(ERRCODE_UNDEFINED_COLUMN),
    8351             :                  errmsg("column \"%s\" of relation \"%s\" does not exist",
    8352             :                         colName, RelationGetRelationName(rel))));
    8353             : 
    8354          84 :     attTup = (Form_pg_attribute) GETSTRUCT(tuple);
    8355          84 :     attnum = attTup->attnum;
    8356             : 
    8357          84 :     if (attnum <= 0)
    8358           0 :         ereport(ERROR,
    8359             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    8360             :                  errmsg("cannot alter system column \"%s\"",
    8361             :                         colName)));
    8362             : 
    8363          84 :     if (attTup->attgenerated != ATTRIBUTE_GENERATED_STORED)
    8364           6 :         ereport(ERROR,
    8365             :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    8366             :                  errmsg("column \"%s\" of relation \"%s\" is not a generated column",
    8367             :                         colName, RelationGetRelationName(rel))));
    8368          78 :     ReleaseSysCache(tuple);
    8369             : 
    8370             :     /*
    8371             :      * Clear all the missing values if we're rewriting the table, since this
    8372             :      * renders them pointless.
    8373             :      */
    8374          78 :     RelationClearMissing(rel);
    8375             : 
    8376             :     /* make sure we don't conflict with later attribute modifications */
    8377          78 :     CommandCounterIncrement();
    8378             : 
    8379             :     /*
    8380             :      * Find everything that depends on the column (constraints, indexes, etc),
    8381             :      * and record enough information to let us recreate the objects after
    8382             :      * rewrite.
    8383             :      */
    8384          78 :     RememberAllDependentForRebuilding(tab, AT_SetExpression, rel, attnum, colName);
    8385             : 
    8386             :     /*
    8387             :      * Drop the dependency records of the GENERATED expression, in particular
    8388             :      * its INTERNAL dependency on the column, which would otherwise cause
    8389             :      * dependency.c to refuse to perform the deletion.
    8390             :      */
    8391          78 :     attrdefoid = GetAttrDefaultOid(RelationGetRelid(rel), attnum);
    8392          78 :     if (!OidIsValid(attrdefoid))
    8393           0 :         elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
    8394             :              RelationGetRelid(rel), attnum);
    8395          78 :     (void) deleteDependencyRecordsFor(AttrDefaultRelationId, attrdefoid, false);
    8396             : 
    8397             :     /* Make above changes visible */
    8398          78 :     CommandCounterIncrement();
    8399             : 
    8400             :     /*
    8401             :      * Get rid of the GENERATED expression itself.  We use RESTRICT here for
    8402             :      * safety, but at present we do not expect anything to depend on the
    8403             :      * expression.
    8404             :      */
    8405          78 :     RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT,
    8406             :                       false, false);
    8407             : 
    8408             :     /* Prepare to store the new expression, in the catalogs */
    8409          78 :     rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
    8410          78 :     rawEnt->attnum = attnum;
    8411          78 :     rawEnt->raw_default = newExpr;
    8412          78 :     rawEnt->missingMode = false;
    8413          78 :     rawEnt->generated = ATTRIBUTE_GENERATED_STORED;
    8414             : 
    8415             :     /* Store the generated expression */
    8416          78 :     AddRelationNewConstraints(rel, list_make1(rawEnt), NIL,
    8417             :                               false, true, false, NULL);
    8418             : 
    8419             :     /* Make above new expression visible */
    8420          78 :     CommandCounterIncrement();
    8421             : 
    8422             :     /* Prepare for table rewrite */
    8423          78 :     defval = (Expr *) build_column_default(rel, attnum);
    8424             : 
    8425          78 :     newval = (NewColumnValue *) palloc0(sizeof(NewColumnValue));
    8426          78 :     newval->attnum = attnum;
    8427          78 :     newval->expr = expression_planner(defval);
    8428          78 :     newval->is_generated = true;
    8429             : 
    8430          78 :     tab->newvals = lappend(tab->newvals, newval);
    8431          78 :     tab->rewrite |= AT_REWRITE_DEFAULT_VAL;
    8432             : 
    8433             :     /* Drop any pg_statistic entry for the column */
    8434          78 :     RemoveStatistics(RelationGetRelid(rel), attnum);
    8435             : 
    8436          78 :     InvokeObjectPostAlterHook(RelationRelationId,
    8437             :                               RelationGetRelid(rel), attnum);
    8438             : 
    8439          78 :     ObjectAddressSubSet(address, RelationRelationId,
    8440             :                         RelationGetRelid(rel), attnum);
    8441          78 :     return address;
    8442             : }
    8443             : 
    8444             : /*
    8445             :  * ALTER TABLE ALTER COLUMN DROP EXPRESSION
    8446             :  */
    8447             : static void
    8448          44 : ATPrepDropExpression(Relation rel, AlterTableCmd *cmd, bool recurse, bool recursing, LOCKMODE lockmode)
    8449             : {
    8450             :     /*
    8451             :      * Reject ONLY if there are child tables.  We could implement this, but it
    8452             :      * is a bit complicated.  GENERATED clauses must be attached to the column
    8453             :      * definition and cannot be added later like DEFAULT, so if a child table
    8454             :      * has a generation expression that the parent does not have, the child
    8455             :      * column will necessarily be an attislocal column.  So to implement ONLY
    8456             :      * here, we'd need extra code to update attislocal of the direct child
    8457             :      * tables, somewhat similar to how DROP COLUMN does it, so that the
    8458             :      * resulting state can be properly dumped and restored.
    8459             :      */
    8460          56 :     if (!recurse &&
    8461          12 :         find_inheritance_children(RelationGetRelid(rel), lockmode))
    8462           6 :         ereport(ERROR,
    8463             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    8464             :                  errmsg("ALTER TABLE / DROP EXPRESSION must be applied to child tables too")));
    8465             : 
    8466             :     /*
    8467             :      * Cannot drop generation expression from inherited columns.
    8468             :      */
    8469          38 :     if (!recursing)
    8470             :     {
    8471             :         HeapTuple   tuple;
    8472             :         Form_pg_attribute attTup;
    8473             : 
    8474          32 :         tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), cmd->name);
    8475          32 :         if (!HeapTupleIsValid(tuple))
    8476           0 :             ereport(ERROR,
    8477             :                     (errcode(ERRCODE_UNDEFINED_COLUMN),
    8478             :                      errmsg("column \"%s\" of relation \"%s\" does not exist",
    8479             :                             cmd->name, RelationGetRelationName(rel))));
    8480             : 
    8481          32 :         attTup = (Form_pg_attribute) GETSTRUCT(tuple);
    8482             : 
    8483          32 :         if (attTup->attinhcount > 0)
    8484           6 :             ereport(ERROR,
    8485             :                     (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    8486             :                      errmsg("cannot drop generation expression from inherited column")));
    8487             :     }
    8488          32 : }
    8489             : 
    8490             : /*
    8491             :  * Return the address of the affected column.
    8492             :  */
    8493             : static ObjectAddress
    8494          32 : ATExecDropExpression(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode)
    8495             : {
    8496             :     HeapTuple   tuple;
    8497             :     Form_pg_attribute attTup;
    8498             :     AttrNumber  attnum;
    8499             :     Relation    attrelation;
    8500             :     Oid         attrdefoid;
    8501             :     ObjectAddress address;
    8502             : 
    8503          32 :     attrelation = table_open(AttributeRelationId, RowExclusiveLock);
    8504          32 :     tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
    8505          32 :     if (!HeapTupleIsValid(tuple))
    8506           0 :         ereport(ERROR,
    8507             :                 (errcode(ERRCODE_UNDEFINED_COLUMN),
    8508             :                  errmsg("column \"%s\" of relation \"%s\" does not exist",
    8509             :                         colName, RelationGetRelationName(rel))));
    8510             : 
    8511          32 :     attTup = (Form_pg_attribute) GETSTRUCT(tuple);
    8512          32 :     attnum = attTup->attnum;
    8513             : 
    8514          32 :     if (attnum <= 0)
    8515           0 :         ereport(ERROR,
    8516             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    8517             :                  errmsg("cannot alter system column \"%s\"",
    8518             :                         colName)));
    8519             : 
    8520          32 :     if (attTup->attgenerated != ATTRIBUTE_GENERATED_STORED)
    8521             :     {
    8522          12 :         if (!missing_ok)
    8523           6 :             ereport(ERROR,
    8524             :                     (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    8525             :                      errmsg("column \"%s\" of relation \"%s\" is not a stored generated column",
    8526             :                             colName, RelationGetRelationName(rel))));
    8527             :         else
    8528             :         {
    8529           6 :             ereport(NOTICE,
    8530             :                     (errmsg("column \"%s\" of relation \"%s\" is not a stored generated column, skipping",
    8531             :                             colName, RelationGetRelationName(rel))));
    8532           6 :             heap_freetuple(tuple);
    8533           6 :             table_close(attrelation, RowExclusiveLock);
    8534           6 :             return InvalidObjectAddress;
    8535             :         }
    8536             :     }
    8537             : 
    8538             :     /*
    8539             :      * Mark the column as no longer generated.  (The atthasdef flag needs to
    8540             :      * get cleared too, but RemoveAttrDefault will handle that.)
    8541             :      */
    8542          20 :     attTup->attgenerated = '\0';
    8543          20 :     CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
    8544             : 
    8545          20 :     InvokeObjectPostAlterHook(RelationRelationId,
    8546             :                               RelationGetRelid(rel),
    8547             :                               attnum);
    8548          20 :     heap_freetuple(tuple);
    8549             : 
    8550          20 :     table_close(attrelation, RowExclusiveLock);
    8551             : 
    8552             :     /*
    8553             :      * Drop the dependency records of the GENERATED expression, in particular
    8554             :      * its INTERNAL dependency on the column, which would otherwise cause
    8555             :      * dependency.c to refuse to perform the deletion.
    8556             :      */
    8557          20 :     attrdefoid = GetAttrDefaultOid(RelationGetRelid(rel), attnum);
    8558          20 :     if (!OidIsValid(attrdefoid))
    8559           0 :         elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
    8560             :              RelationGetRelid(rel), attnum);
    8561          20 :     (void) deleteDependencyRecordsFor(AttrDefaultRelationId, attrdefoid, false);
    8562             : 
    8563             :     /* Make above changes visible */
    8564          20 :     CommandCounterIncrement();
    8565             : 
    8566             :     /*
    8567             :      * Get rid of the GENERATED expression itself.  We use RESTRICT here for
    8568             :      * safety, but at present we do not expect anything to depend on the
    8569             :      * default.
    8570             :      */
    8571          20 :     RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT,
    8572             :                       false, false);
    8573             : 
    8574          20 :     ObjectAddressSubSet(address, RelationRelationId,
    8575             :                         RelationGetRelid(rel), attnum);
    8576          20 :     return address;
    8577             : }
    8578             : 
    8579             : /*
    8580             :  * ALTER TABLE ALTER COLUMN SET STATISTICS
    8581             :  *
    8582             :  * Return value is the address of the modified column
    8583             :  */
    8584             : static ObjectAddress
    8585         164 : ATExecSetStatistics(Relation rel, const char *colName, int16 colNum, Node *newValue, LOCKMODE lockmode)
    8586             : {
    8587         164 :     int         newtarget = 0;
    8588             :     bool        newtarget_default;
    8589             :     Relation    attrelation;
    8590             :     HeapTuple   tuple,
    8591             :                 newtuple;
    8592             :     Form_pg_attribute attrtuple;
    8593             :     AttrNumber  attnum;
    8594             :     ObjectAddress address;
    8595             :     Datum       repl_val[Natts_pg_attribute];
    8596             :     bool        repl_null[Natts_pg_attribute];
    8597             :     bool        repl_repl[Natts_pg_attribute];
    8598             : 
    8599             :     /*
    8600             :      * We allow referencing columns by numbers only for indexes, since table
    8601             :      * column numbers could contain gaps if columns are later dropped.
    8602             :      */
    8603         164 :     if (rel->rd_rel->relkind != RELKIND_INDEX &&
    8604         100 :         rel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX &&
    8605             :         !colName)
    8606           0 :         ereport(ERROR,
    8607             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    8608             :                  errmsg("cannot refer to non-index column by number")));
    8609             : 
    8610             :     /* -1 was used in previous versions for the default setting */
    8611         164 :     if (newValue && intVal(newValue) != -1)
    8612             :     {
    8613         120 :         newtarget = intVal(newValue);
    8614         120 :         newtarget_default = false;
    8615             :     }
    8616             :     else
    8617          44 :         newtarget_default = true;
    8618             : 
    8619         164 :     if (!newtarget_default)
    8620             :     {
    8621             :         /*
    8622             :          * Limit target to a sane range
    8623             :          */
    8624         120 :         if (newtarget < 0)
    8625             :         {
    8626           0 :             ereport(ERROR,
    8627             :                     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    8628             :                      errmsg("statistics target %d is too low",
    8629             :                             newtarget)));
    8630             :         }
    8631         120 :         else if (newtarget > MAX_STATISTICS_TARGET)
    8632             :         {
    8633           0 :             newtarget = MAX_STATISTICS_TARGET;
    8634           0 :             ereport(WARNING,
    8635             :                     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    8636             :                      errmsg("lowering statistics target to %d",
    8637             :                             newtarget)));
    8638             :         }
    8639             :     }
    8640             : 
    8641         164 :     attrelation = table_open(AttributeRelationId, RowExclusiveLock);
    8642             : 
    8643         164 :     if (colName)
    8644             :     {
    8645         100 :         tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
    8646             : 
    8647         100 :         if (!HeapTupleIsValid(tuple))
    8648          12 :             ereport(ERROR,
    8649             :                     (errcode(ERRCODE_UNDEFINED_COLUMN),
    8650             :                      errmsg("column \"%s\" of relation \"%s\" does not exist",
    8651             :                             colName, RelationGetRelationName(rel))));
    8652             :     }
    8653             :     else
    8654             :     {
    8655          64 :         tuple = SearchSysCacheAttNum(RelationGetRelid(rel), colNum);
    8656             : 
    8657          64 :         if (!HeapTupleIsValid(tuple))
    8658          12 :             ereport(ERROR,
    8659             :                     (errcode(ERRCODE_UNDEFINED_COLUMN),
    8660             :                      errmsg("column number %d of relation \"%s\" does not exist",
    8661             :                             colNum, RelationGetRelationName(rel))));
    8662             :     }
    8663             : 
    8664         140 :     attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
    8665             : 
    8666         140 :     attnum = attrtuple->attnum;
    8667         140 :     if (attnum <= 0)
    8668           0 :         ereport(ERROR,
    8669             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    8670             :                  errmsg("cannot alter system column \"%s\"",
    8671             :                         colName)));
    8672             : 
    8673         140 :     if (rel->rd_rel->relkind == RELKIND_INDEX ||
    8674          88 :         rel->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
    8675             :     {
    8676          52 :         if (attnum > rel->rd_index->indnkeyatts)
    8677           6 :             ereport(ERROR,
    8678             :                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    8679             :                      errmsg("cannot alter statistics on included column \"%s\" of index \"%s\"",
    8680             :                             NameStr(attrtuple->attname), RelationGetRelationName(rel))));
    8681          46 :         else if (rel->rd_index->indkey.values[attnum - 1] != 0)
    8682          18 :             ereport(ERROR,
    8683             :                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    8684             :                      errmsg("cannot alter statistics on non-expression column \"%s\" of index \"%s\"",
    8685             :                             NameStr(attrtuple->attname), RelationGetRelationName(rel)),
    8686             :                      errhint("Alter statistics on table column instead.")));
    8687             :     }
    8688             : 
    8689             :     /* Build new tuple. */
    8690         116 :     memset(repl_null, false, sizeof(repl_null));
    8691         116 :     memset(repl_repl, false, sizeof(repl_repl));
    8692         116 :     if (!newtarget_default)
    8693          72 :         repl_val[Anum_pg_attribute_attstattarget - 1] = newtarget;
    8694             :     else
    8695          44 :         repl_null[Anum_pg_attribute_attstattarget - 1] = true;
    8696         116 :     repl_repl[Anum_pg_attribute_attstattarget - 1] = true;
    8697         116 :     newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrelation),
    8698             :                                  repl_val, repl_null, repl_repl);
    8699         116 :     CatalogTupleUpdate(attrelation, &tuple->t_self, newtuple);
    8700             : 
    8701         116 :     InvokeObjectPostAlterHook(RelationRelationId,
    8702             :                               RelationGetRelid(rel),
    8703             :                               attrtuple->attnum);
    8704         116 :     ObjectAddressSubSet(address, RelationRelationId,
    8705             :                         RelationGetRelid(rel), attnum);
    8706             : 
    8707         116 :     heap_freetuple(newtuple);
    8708             : 
    8709         116 :     ReleaseSysCache(tuple);
    8710             : 
    8711         116 :     table_close(attrelation, RowExclusiveLock);
    8712             : 
    8713         116 :     return address;
    8714             : }
    8715             : 
    8716             : /*
    8717             :  * Return value is the address of the modified column
    8718             :  */
    8719             : static ObjectAddress
    8720          32 : ATExecSetOptions(Relation rel, const char *colName, Node *options,
    8721             :                  bool isReset, LOCKMODE lockmode)
    8722             : {
    8723             :     Relation    attrelation;
    8724             :     HeapTuple   tuple,
    8725             :                 newtuple;
    8726             :     Form_pg_attribute attrtuple;
    8727             :     AttrNumber  attnum;
    8728             :     Datum       datum,
    8729             :                 newOptions;
    8730             :     bool        isnull;
    8731             :     ObjectAddress address;
    8732             :     Datum       repl_val[Natts_pg_attribute];
    8733             :     bool        repl_null[Natts_pg_attribute];
    8734             :     bool        repl_repl[Natts_pg_attribute];
    8735             : 
    8736          32 :     attrelation = table_open(AttributeRelationId, RowExclusiveLock);
    8737             : 
    8738          32 :     tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
    8739             : 
    8740          32 :     if (!HeapTupleIsValid(tuple))
    8741           0 :         ereport(ERROR,
    8742             :                 (errcode(ERRCODE_UNDEFINED_COLUMN),
    8743             :                  errmsg("column \"%s\" of relation \"%s\" does not exist",
    8744             :                         colName, RelationGetRelationName(rel))));
    8745          32 :     attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
    8746             : 
    8747          32 :     attnum = attrtuple->attnum;
    8748          32 :     if (attnum <= 0)
    8749           0 :         ereport(ERROR,
    8750             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    8751             :                  errmsg("cannot alter system column \"%s\"",
    8752             :                         colName)));
    8753             : 
    8754             :     /* Generate new proposed attoptions (text array) */
    8755          32 :     datum = SysCacheGetAttr(ATTNAME, tuple, Anum_pg_attribute_attoptions,
    8756             :                             &isnull);
    8757          32 :     newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
    8758             :                                      castNode(List, options), NULL, NULL,
    8759             :                                      false, isReset);
    8760             :     /* Validate new options */
    8761          32 :     (void) attribute_reloptions(newOptions, true);
    8762             : 
    8763             :     /* Build new tuple. */
    8764          32 :     memset(repl_null, false, sizeof(repl_null));
    8765          32 :     memset(repl_repl, false, sizeof(repl_repl));
    8766          32 :     if (newOptions != (Datum) 0)
    8767          32 :         repl_val[Anum_pg_attribute_attoptions - 1] = newOptions;
    8768             :     else
    8769           0 :         repl_null[Anum_pg_attribute_attoptions - 1] = true;
    8770          32 :     repl_repl[Anum_pg_attribute_attoptions - 1] = true;
    8771          32 :     newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrelation),
    8772             :                                  repl_val, repl_null, repl_repl);
    8773             : 
    8774             :     /* Update system catalog. */
    8775          32 :     CatalogTupleUpdate(attrelation, &newtuple->t_self, newtuple);
    8776             : 
    8777          32 :     InvokeObjectPostAlterHook(RelationRelationId,
    8778             :                               RelationGetRelid(rel),
    8779             :                               attrtuple->attnum);
    8780          32 :     ObjectAddressSubSet(address, RelationRelationId,
    8781             :                         RelationGetRelid(rel), attnum);
    8782             : 
    8783          32 :     heap_freetuple(newtuple);
    8784             : 
    8785          32 :     ReleaseSysCache(tuple);
    8786             : 
    8787          32 :     table_close(attrelation, RowExclusiveLock);
    8788             : 
    8789          32 :     return address;
    8790             : }
    8791             : 
    8792             : /*
    8793             :  * Helper function for ATExecSetStorage and ATExecSetCompression
    8794             :  *
    8795             :  * Set the attstorage and/or attcompression fields for index columns
    8796             :  * associated with the specified table column.
    8797             :  */
    8798             : static void
    8799         284 : SetIndexStorageProperties(Relation rel, Relation attrelation,
    8800             :                           AttrNumber attnum,
    8801             :                           bool setstorage, char newstorage,
    8802             :                           bool setcompression, char newcompression,
    8803             :                           LOCKMODE lockmode)
    8804             : {
    8805             :     ListCell   *lc;
    8806             : 
    8807         356 :     foreach(lc, RelationGetIndexList(rel))
    8808             :     {
    8809          72 :         Oid         indexoid = lfirst_oid(lc);
    8810             :         Relation    indrel;
    8811          72 :         AttrNumber  indattnum = 0;
    8812             :         HeapTuple   tuple;
    8813             : 
    8814          72 :         indrel = index_open(indexoid, lockmode);
    8815             : 
    8816         120 :         for (int i = 0; i < indrel->rd_index->indnatts; i++)
    8817             :         {
    8818          78 :             if (indrel->rd_index->indkey.values[i] == attnum)
    8819             :             {
    8820          30 :                 indattnum = i + 1;
    8821          30 :                 break;
    8822             :             }
    8823             :         }
    8824             : 
    8825          72 :         if (indattnum == 0)
    8826             :         {
    8827          42 :             index_close(indrel, lockmode);
    8828          42 :             continue;
    8829             :         }
    8830             : 
    8831          30 :         tuple = SearchSysCacheCopyAttNum(RelationGetRelid(indrel), indattnum);
    8832             : 
    8833          30 :         if (HeapTupleIsValid(tuple))
    8834             :         {
    8835          30 :             Form_pg_attribute attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
    8836             : 
    8837          30 :             if (setstorage)
    8838          24 :                 attrtuple->attstorage = newstorage;
    8839             : 
    8840          30 :             if (setcompression)
    8841           6 :                 attrtuple->attcompression = newcompression;
    8842             : 
    8843          30 :             CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
    8844             : 
    8845          30 :             InvokeObjectPostAlterHook(RelationRelationId,
    8846             :                                       RelationGetRelid(rel),
    8847             :                                       attrtuple->attnum);
    8848             : 
    8849          30 :             heap_freetuple(tuple);
    8850             :         }
    8851             : 
    8852          30 :         index_close(indrel, lockmode);
    8853             :     }
    8854         284 : }
    8855             : 
    8856             : /*
    8857             :  * ALTER TABLE ALTER COLUMN SET STORAGE
    8858             :  *
    8859             :  * Return value is the address of the modified column
    8860             :  */
    8861             : static ObjectAddress
    8862         234 : ATExecSetStorage(Relation rel, const char *colName, Node *newValue, LOCKMODE lockmode)
    8863             : {
    8864             :     Relation    attrelation;
    8865             :     HeapTuple   tuple;
    8866             :     Form_pg_attribute attrtuple;
    8867             :     AttrNumber  attnum;
    8868             :     ObjectAddress address;
    8869             : 
    8870         234 :     attrelation = table_open(AttributeRelationId, RowExclusiveLock);
    8871             : 
    8872         234 :     tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
    8873             : 
    8874         234 :     if (!HeapTupleIsValid(tuple))
    8875          12 :         ereport(ERROR,
    8876             :                 (errcode(ERRCODE_UNDEFINED_COLUMN),
    8877             :                  errmsg("column \"%s\" of relation \"%s\" does not exist",
    8878             :                         colName, RelationGetRelationName(rel))));
    8879         222 :     attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
    8880             : 
    8881         222 :     attnum = attrtuple->attnum;
    8882         222 :     if (attnum <= 0)
    8883           0 :         ereport(ERROR,
    8884             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    8885             :                  errmsg("cannot alter system column \"%s\"",
    8886             :                         colName)));
    8887             : 
    8888         222 :     attrtuple->attstorage = GetAttributeStorage(attrtuple->atttypid, strVal(newValue));
    8889             : 
    8890         222 :     CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
    8891             : 
    8892         222 :     InvokeObjectPostAlterHook(RelationRelationId,
    8893             :                               RelationGetRelid(rel),
    8894             :                               attrtuple->attnum);
    8895             : 
    8896             :     /*
    8897             :      * Apply the change to indexes as well (only for simple index columns,
    8898             :      * matching behavior of index.c ConstructTupleDescriptor()).
    8899             :      */
    8900         222 :     SetIndexStorageProperties(rel, attrelation, attnum,
    8901         222 :                               true, attrtuple->attstorage,
    8902             :                               false, 0,
    8903             :                               lockmode);
    8904             : 
    8905         222 :     heap_freetuple(tuple);
    8906             : 
    8907         222 :     table_close(attrelation, RowExclusiveLock);
    8908             : 
    8909         222 :     ObjectAddressSubSet(address, RelationRelationId,
    8910             :                         RelationGetRelid(rel), attnum);
    8911         222 :     return address;
    8912             : }
    8913             : 
    8914             : 
    8915             : /*
    8916             :  * ALTER TABLE DROP COLUMN
    8917             :  *
    8918             :  * DROP COLUMN cannot use the normal ALTER TABLE recursion mechanism,
    8919             :  * because we have to decide at runtime whether to recurse or not depending
    8920             :  * on whether attinhcount goes to zero or not.  (We can't check this in a
    8921             :  * static pre-pass because it won't handle multiple inheritance situations
    8922             :  * correctly.)
    8923             :  */
    8924             : static void
    8925        1598 : ATPrepDropColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
    8926             :                  AlterTableCmd *cmd, LOCKMODE lockmode,
    8927             :                  AlterTableUtilityContext *context)
    8928             : {
    8929        1598 :     if (rel->rd_rel->reloftype && !recursing)
    8930           6 :         ereport(ERROR,
    8931             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    8932             :                  errmsg("cannot drop column from typed table")));
    8933             : 
    8934        1592 :     if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
    8935          82 :         ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
    8936             : 
    8937        1586 :     if (recurse)
    8938        1318 :         cmd->recurse = true;
    8939        1586 : }
    8940             : 
    8941             : /*
    8942             :  * Drops column 'colName' from relation 'rel' and returns the address of the
    8943             :  * dropped column.  The column is also dropped (or marked as no longer
    8944             :  * inherited from relation) from the relation's inheritance children, if any.
    8945             :  *
    8946             :  * In the recursive invocations for inheritance child relations, instead of
    8947             :  * dropping the column directly (if to be dropped at all), its object address
    8948             :  * is added to 'addrs', which must be non-NULL in such invocations.  All
    8949             :  * columns are dropped at the same time after all the children have been
    8950             :  * checked recursively.
    8951             :  */
    8952             : static ObjectAddress
    8953        2142 : ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
    8954             :                  DropBehavior behavior,
    8955             :                  bool recurse, bool recursing,
    8956             :                  bool missing_ok, LOCKMODE lockmode,
    8957             :                  ObjectAddresses *addrs)
    8958             : {
    8959             :     HeapTuple   tuple;
    8960             :     Form_pg_attribute targetatt;
    8961             :     AttrNumber  attnum;
    8962             :     List       *children;
    8963             :     ObjectAddress object;
    8964             :     bool        is_expr;
    8965             : 
    8966             :     /* At top level, permission check was done in ATPrepCmd, else do it */
    8967        2142 :     if (recursing)
    8968         556 :         ATSimplePermissions(AT_DropColumn, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
    8969             : 
    8970             :     /* Initialize addrs on the first invocation */
    8971             :     Assert(!recursing || addrs != NULL);
    8972             : 
    8973             :     /* since this function recurses, it could be driven to stack overflow */
    8974        2142 :     check_stack_depth();
    8975             : 
    8976        2142 :     if (!recursing)
    8977        1586 :         addrs = new_object_addresses();
    8978             : 
    8979             :     /*
    8980             :      * get the number of the attribute
    8981             :      */
    8982        2142 :     tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
    8983        2142 :     if (!HeapTupleIsValid(tuple))
    8984             :     {
    8985          54 :         if (!missing_ok)
    8986             :         {
    8987          36 :             ereport(ERROR,
    8988             :                     (errcode(ERRCODE_UNDEFINED_COLUMN),
    8989             :                      errmsg("column \"%s\" of relation \"%s\" does not exist",
    8990             :                             colName, RelationGetRelationName(rel))));
    8991             :         }
    8992             :         else
    8993             :         {
    8994          18 :             ereport(NOTICE,
    8995             :                     (errmsg("column \"%s\" of relation \"%s\" does not exist, skipping",
    8996             :                             colName, RelationGetRelationName(rel))));
    8997          18 :             return InvalidObjectAddress;
    8998             :         }
    8999             :     }
    9000        2088 :     targetatt = (Form_pg_attribute) GETSTRUCT(tuple);
    9001             : 
    9002        2088 :     attnum = targetatt->attnum;
    9003             : 
    9004             :     /* Can't drop a system attribute */
    9005        2088 :     if (attnum <= 0)
    9006           6 :         ereport(ERROR,
    9007             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    9008             :                  errmsg("cannot drop system column \"%s\"",
    9009             :                         colName)));
    9010             : 
    9011             :     /*
    9012             :      * Don't drop inherited columns, unless recursing (presumably from a drop
    9013             :      * of the parent column)
    9014             :      */
    9015        2082 :     if (targetatt->attinhcount > 0 && !recursing)
    9016          48 :         ereport(ERROR,
    9017             :                 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    9018             :                  errmsg("cannot drop inherited column \"%s\"",
    9019             :                         colName)));
    9020             : 
    9021             :     /*
    9022             :      * Don't drop columns used in the partition key, either.  (If we let this
    9023             :      * go through, the key column's dependencies would cause a cascaded drop
    9024             :      * of the whole table, which is surely not what the user expected.)
    9025             :      */
    9026        2034 :     if (has_partition_attrs(rel,
    9027             :                             bms_make_singleton(attnum - FirstLowInvalidHeapAttributeNumber),
    9028             :                             &is_expr))
    9029          30 :         ereport(ERROR,
    9030             :                 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    9031             :                  errmsg("cannot drop column \"%s\" because it is part of the partition key of relation \"%s\"",
    9032             :                         colName, RelationGetRelationName(rel))));
    9033             : 
    9034        2004 :     ReleaseSysCache(tuple);
    9035             : 
    9036             :     /*
    9037             :      * Propagate to children as appropriate.  Unlike most other ALTER
    9038             :      * routines, we have to do this one level of recursion at a time; we can't
    9039             :      * use find_all_inheritors to do it in one pass.
    9040             :      */
    9041             :     children =
    9042        2004 :         find_inheritance_children(RelationGetRelid(rel), lockmode);
    9043             : 
    9044        2004 :     if (children)
    9045             :     {
    9046             :         Relation    attr_rel;
    9047             :         ListCell   *child;
    9048             : 
    9049             :         /*
    9050             :          * In case of a partitioned table, the column must be dropped from the
    9051             :          * partitions as well.
    9052             :          */
    9053         302 :         if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && !recurse)
    9054           6 :             ereport(ERROR,
    9055             :                     (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    9056             :                      errmsg("cannot drop column from only the partitioned table when partitions exist"),
    9057             :                      errhint("Do not specify the ONLY keyword.")));
    9058             : 
    9059         296 :         attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
    9060         882 :         foreach(child, children)
    9061             :         {
    9062         592 :             Oid         childrelid = lfirst_oid(child);
    9063             :             Relation    childrel;
    9064             :             Form_pg_attribute childatt;
    9065             : 
    9066             :             /* find_inheritance_children already got lock */
    9067         592 :             childrel = table_open(childrelid, NoLock);
    9068         592 :             CheckAlterTableIsSafe(childrel);
    9069             : 
    9070         592 :             tuple = SearchSysCacheCopyAttName(childrelid, colName);
    9071         592 :             if (!HeapTupleIsValid(tuple))   /* shouldn't happen */
    9072           0 :                 elog(ERROR, "cache lookup failed for attribute \"%s\" of relation %u",
    9073             :                      colName, childrelid);
    9074         592 :             childatt = (Form_pg_attribute) GETSTRUCT(tuple);
    9075             : 
    9076         592 :             if (childatt->attinhcount <= 0) /* shouldn't happen */
    9077           0 :                 elog(ERROR, "relation %u has non-inherited attribute \"%s\"",
    9078             :                      childrelid, colName);
    9079             : 
    9080         592 :             if (recurse)
    9081             :             {
    9082             :                 /*
    9083             :                  * If the child column has other definition sources, just
    9084             :                  * decrement its inheritance count; if not, recurse to delete
    9085             :                  * it.
    9086             :                  */
    9087         568 :                 if (childatt->attinhcount == 1 && !childatt->attislocal)
    9088             :                 {
    9089             :                     /* Time to delete this child column, too */
    9090         556 :                     ATExecDropColumn(wqueue, childrel, colName,
    9091             :                                      behavior, true, true,
    9092             :                                      false, lockmode, addrs);
    9093             :                 }
    9094             :                 else
    9095             :                 {
    9096             :                     /* Child column must survive my deletion */
    9097          12 :                     childatt->attinhcount--;
    9098             : 
    9099          12 :                     CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
    9100             : 
    9101             :                     /* Make update visible */
    9102          12 :                     CommandCounterIncrement();
    9103             :                 }
    9104             :             }
    9105             :             else
    9106             :             {
    9107             :                 /*
    9108             :                  * If we were told to drop ONLY in this table (no recursion),
    9109             :                  * we need to mark the inheritors' attributes as locally
    9110             :                  * defined rather than inherited.
    9111             :                  */
    9112          24 :                 childatt->attinhcount--;
    9113          24 :                 childatt->attislocal = true;
    9114             : 
    9115          24 :                 CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
    9116             : 
    9117             :                 /* Make update visible */
    9118          24 :                 CommandCounterIncrement();
    9119             :             }
    9120             : 
    9121         586 :             heap_freetuple(tuple);
    9122             : 
    9123         586 :             table_close(childrel, NoLock);
    9124             :         }
    9125         290 :         table_close(attr_rel, RowExclusiveLock);
    9126             :     }
    9127             : 
    9128             :     /* Add object to delete */
    9129        1992 :     object.classId = RelationRelationId;
    9130        1992 :     object.objectId = RelationGetRelid(rel);
    9131        1992 :     object.objectSubId = attnum;
    9132        1992 :     add_exact_object_address(&object, addrs);
    9133             : 
    9134        1992 :     if (!recursing)
    9135             :     {
    9136             :         /* Recursion has ended, drop everything that was collected */
    9137        1442 :         performMultipleDeletions(addrs, behavior, 0);
    9138        1400 :         free_object_addresses(addrs);
    9139             :     }
    9140             : 
    9141        1950 :     return object;
    9142             : }
    9143             : 
    9144             : /*
    9145             :  * ALTER TABLE ADD INDEX
    9146             :  *
    9147             :  * There is no such command in the grammar, but parse_utilcmd.c converts
    9148             :  * UNIQUE and PRIMARY KEY constraints into AT_AddIndex subcommands.  This lets
    9149             :  * us schedule creation of the index at the appropriate time during ALTER.
    9150             :  *
    9151             :  * Return value is the address of the new index.
    9152             :  */
    9153             : static ObjectAddress
    9154        1350 : ATExecAddIndex(AlteredTableInfo *tab, Relation rel,
    9155             :                IndexStmt *stmt, bool is_rebuild, LOCKMODE lockmode)
    9156             : {
    9157             :     bool        check_rights;
    9158             :     bool        skip_build;
    9159             :     bool        quiet;
    9160             :     ObjectAddress address;
    9161             : 
    9162             :     Assert(IsA(stmt, IndexStmt));
    9163             :     Assert(!stmt->concurrent);
    9164             : 
    9165             :     /* The IndexStmt has already been through transformIndexStmt */
    9166             :     Assert(stmt->transformed);
    9167             : 
    9168             :     /* suppress schema rights check when rebuilding existing index */
    9169        1350 :     check_rights = !is_rebuild;
    9170             :     /* skip index build if phase 3 will do it or we're reusing an old one */
    9171        1350 :     skip_build = tab->rewrite > 0 || RelFileNumberIsValid(stmt->oldNumber);
    9172             :     /* suppress notices when rebuilding existing index */
    9173        1350 :     quiet = is_rebuild;
    9174             : 
    9175        1350 :     address = DefineIndex(RelationGetRelid(rel),
    9176             :                           stmt,
    9177             :                           InvalidOid,   /* no predefined OID */
    9178             :                           InvalidOid,   /* no parent index */
    9179             :                           InvalidOid,   /* no parent constraint */
    9180             :                           -1,   /* total_parts unknown */
    9181             :                           true, /* is_alter_table */
    9182             :                           check_rights,
    9183             :                           false,    /* check_not_in_use - we did it already */
    9184             :                           skip_build,
    9185             :                           quiet);
    9186             : 
    9187             :     /*
    9188             :      * If TryReuseIndex() stashed a relfilenumber for us, we used it for the
    9189             :      * new index instead of building from scratch.  Restore associated fields.
    9190             :      * This may store InvalidSubTransactionId in both fields, in which case
    9191             :      * relcache.c will assume it can rebuild the relcache entry.  Hence, do
    9192             :      * this after the CCI that made catalog rows visible to any rebuild.  The
    9193             :      * DROP of the old edition of this index will have scheduled the storage
    9194             :      * for deletion at commit, so cancel that pending deletion.
    9195             :      */
    9196        1228 :     if (RelFileNumberIsValid(stmt->oldNumber))
    9197             :     {
    9198          72 :         Relation    irel = index_open(address.objectId, NoLock);
    9199             : 
    9200          72 :         irel->rd_createSubid = stmt->oldCreateSubid;
    9201          72 :         irel->rd_firstRelfilelocatorSubid = stmt->oldFirstRelfilelocatorSubid;
    9202          72 :         RelationPreserveStorage(irel->rd_locator, true);
    9203          72 :         index_close(irel, NoLock);
    9204             :     }
    9205             : 
    9206        1228 :     return address;
    9207             : }
    9208             : 
    9209             : /*
    9210             :  * ALTER TABLE ADD STATISTICS
    9211             :  *
    9212             :  * This is no such command in the grammar, but we use this internally to add
    9213             :  * AT_ReAddStatistics subcommands to rebuild extended statistics after a table
    9214             :  * column type change.
    9215             :  */
    9216             : static ObjectAddress
    9217          14 : ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
    9218             :                     CreateStatsStmt *stmt, bool is_rebuild, LOCKMODE lockmode)
    9219             : {
    9220             :     ObjectAddress address;
    9221             : 
    9222             :     Assert(IsA(stmt, CreateStatsStmt));
    9223             : 
    9224             :     /* The CreateStatsStmt has already been through transformStatsStmt */
    9225             :     Assert(stmt->transformed);
    9226             : 
    9227          14 :     address = CreateStatistics(stmt);
    9228             : 
    9229          14 :     return address;
    9230             : }
    9231             : 
    9232             : /*
    9233             :  * ALTER TABLE ADD CONSTRAINT USING INDEX
    9234             :  *
    9235             :  * Returns the address of the new constraint.
    9236             :  */
    9237             : static ObjectAddress
    9238        8422 : ATExecAddIndexConstraint(AlteredTableInfo *tab, Relation rel,
    9239             :                          IndexStmt *stmt, LOCKMODE lockmode)
    9240             : {
    9241        8422 :     Oid         index_oid = stmt->indexOid;
    9242             :     Relation    indexRel;
    9243             :     char       *indexName;
    9244             :     IndexInfo  *indexInfo;
    9245             :     char       *constraintName;
    9246             :     char        constraintType;
    9247             :     ObjectAddress address;
    9248             :     bits16      flags;
    9249             : 
    9250             :     Assert(IsA(stmt, IndexStmt));
    9251             :     Assert(OidIsValid(index_oid));
    9252             :     Assert(stmt->isconstraint);
    9253             : 
    9254             :     /*
    9255             :      * Doing this on partitioned tables is not a simple feature to implement,
    9256             :      * so let's punt for now.
    9257             :      */
    9258        8422 :     if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
    9259           6 :         ereport(ERROR,
    9260             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    9261             :                  errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX is not supported on partitioned tables")));
    9262             : 
    9263        8416 :     indexRel = index_open(index_oid, AccessShareLock);
    9264             : 
    9265        8416 :     indexName = pstrdup(RelationGetRelationName(indexRel));
    9266             : 
    9267        8416 :     indexInfo = BuildIndexInfo(indexRel);
    9268             : 
    9269             :     /* this should have been checked at parse time */
    9270        8416 :     if (!indexInfo->ii_Unique)
    9271           0 :         elog(ERROR, "index \"%s\" is not unique", indexName);
    9272             : 
    9273             :     /*
    9274             :      * Determine name to assign to constraint.  We require a constraint to
    9275             :      * have the same name as the underlying index; therefore, use the index's
    9276             :      * existing name as the default constraint name, and if the user
    9277             :      * explicitly gives some other name for the constraint, rename the index
    9278             :      * to match.
    9279             :      */
    9280        8416 :     constraintName = stmt->idxname;
    9281        8416 :     if (constraintName == NULL)
    9282        8402 :         constraintName = indexName;
    9283          14 :     else if (strcmp(constraintName, indexName) != 0)
    9284             :     {
    9285           8 :         ereport(NOTICE,
    9286             :                 (errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX will rename index \"%s\" to \"%s\"",
    9287             :                         indexName, constraintName)));
    9288           8 :         RenameRelationInternal(index_oid, constraintName, false, true);
    9289             :     }
    9290             : 
    9291             :     /* Extra checks needed if making primary key */
    9292        8416 :     if (stmt->primary)
    9293        4760 :         index_check_primary_key(rel, indexInfo, true, stmt);
    9294             : 
    9295             :     /* Note we currently don't support EXCLUSION constraints here */
    9296        8410 :     if (stmt->primary)
    9297        4754 :         constraintType = CONSTRAINT_PRIMARY;
    9298             :     else
    9299        3656 :         constraintType = CONSTRAINT_UNIQUE;
    9300             : 
    9301             :     /* Create the catalog entries for the constraint */
    9302        8410 :     flags = INDEX_CONSTR_CREATE_UPDATE_INDEX |
    9303             :         INDEX_CONSTR_CREATE_REMOVE_OLD_DEPS |
    9304       16820 :         (stmt->initdeferred ? INDEX_CONSTR_CREATE_INIT_DEFERRED : 0) |
    9305        8410 :         (stmt->deferrable ? INDEX_CONSTR_CREATE_DEFERRABLE : 0) |
    9306        8410 :         (stmt->primary ? INDEX_CONSTR_CREATE_MARK_AS_PRIMARY : 0);
    9307             : 
    9308        8410 :     address = index_constraint_create(rel,
    9309             :                                       index_oid,
    9310             :                                       InvalidOid,
    9311             :                                       indexInfo,
    9312             :                                       constraintName,
    9313             :                                       constraintType,
    9314             :                                       flags,
    9315             :                                       allowSystemTableMods,
    9316             :                                       false);   /* is_internal */
    9317             : 
    9318        8410 :     index_close(indexRel, NoLock);
    9319             : 
    9320        8410 :     return address;
    9321             : }
    9322             : 
    9323             : /*
    9324             :  * ALTER TABLE ADD CONSTRAINT
    9325             :  *
    9326             :  * Return value is the address of the new constraint; if no constraint was
    9327             :  * added, InvalidObjectAddress is returned.
    9328             :  */
    9329             : static ObjectAddress
    9330        2898 : ATExecAddConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
    9331             :                     Constraint *newConstraint, bool recurse, bool is_readd,
    9332             :                     LOCKMODE lockmode)
    9333             : {
    9334        2898 :     ObjectAddress address = InvalidObjectAddress;
    9335             : 
    9336             :     Assert(IsA(newConstraint, Constraint));
    9337             : 
    9338             :     /*
    9339             :      * Currently, we only expect to see CONSTR_CHECK and CONSTR_FOREIGN nodes
    9340             :      * arriving here (see the preprocessing done in parse_utilcmd.c).  Use a
    9341             :      * switch anyway to make it easier to add more code later.
    9342             :      */
    9343        2898 :     switch (newConstraint->contype)
    9344             :     {
    9345         780 :         case CONSTR_CHECK:
    9346             :             address =
    9347         780 :                 ATAddCheckConstraint(wqueue, tab, rel,
    9348             :                                      newConstraint, recurse, false, is_readd,
    9349             :                                      lockmode);
    9350         714 :             break;
    9351             : 
    9352        2118 :         case CONSTR_FOREIGN:
    9353             : 
    9354             :             /*
    9355             :              * Assign or validate constraint name
    9356             :              */
    9357        2118 :             if (newConstraint->conname)
    9358             :             {
    9359         796 :                 if (ConstraintNameIsUsed(CONSTRAINT_RELATION,
    9360             :                                          RelationGetRelid(rel),
    9361         796 :                                          newConstraint->conname))
    9362           0 :                     ereport(ERROR,
    9363             :                             (errcode(ERRCODE_DUPLICATE_OBJECT),
    9364             :                              errmsg("constraint \"%s\" for relation \"%s\" already exists",
    9365             :                                     newConstraint->conname,
    9366             :                                     RelationGetRelationName(rel))));
    9367             :             }
    9368             :             else
    9369        1322 :                 newConstraint->conname =
    9370        1322 :                     ChooseConstraintName(RelationGetRelationName(rel),
    9371        1322 :                                          ChooseForeignKeyConstraintNameAddition(newConstraint->fk_attrs),
    9372             :                                          "fkey",
    9373        1322 :                                          RelationGetNamespace(rel),
    9374             :                                          NIL);
    9375             : 
    9376        2118 :             address = ATAddForeignKeyConstraint(wqueue, tab, rel,
    9377             :                                                 newConstraint,
    9378             :                                                 recurse, false,
    9379             :                                                 lockmode);
    9380        1798 :             break;
    9381             : 
    9382           0 :         default:
    9383           0 :             elog(ERROR, "unrecognized constraint type: %d",
    9384             :                  (int) newConstraint->contype);
    9385             :     }
    9386             : 
    9387        2512 :     return address;
    9388             : }
    9389             : 
    9390             : /*
    9391             :  * Generate the column-name portion of the constraint name for a new foreign
    9392             :  * key given the list of column names that reference the referenced
    9393             :  * table.  This will be passed to ChooseConstraintName along with the parent
    9394             :  * table name and the "fkey" suffix.
    9395             :  *
    9396             :  * We know that less than NAMEDATALEN characters will actually be used, so we
    9397             :  * can truncate the result once we've generated that many.
    9398             :  *
    9399             :  * XXX see also ChooseExtendedStatisticNameAddition and
    9400             :  * ChooseIndexNameAddition.
    9401             :  */
    9402             : static char *
    9403        2024 : ChooseForeignKeyConstraintNameAddition(List *colnames)
    9404             : {
    9405             :     char        buf[NAMEDATALEN * 2];
    9406        2024 :     int         buflen = 0;
    9407             :     ListCell   *lc;
    9408             : 
    9409        2024 :     buf[0] = '\0';
    9410        4446 :     foreach(lc, colnames)
    9411             :     {
    9412        2422 :         const char *name = strVal(lfirst(lc));
    9413             : 
    9414        2422 :         if (buflen > 0)
    9415         398 :             buf[buflen++] = '_';    /* insert _ between names */
    9416             : 
    9417             :         /*
    9418             :          * At this point we have buflen <= NAMEDATALEN.  name should be less
    9419             :          * than NAMEDATALEN already, but use strlcpy for paranoia.
    9420             :          */
    9421        2422 :         strlcpy(buf + buflen, name, NAMEDATALEN);
    9422        2422 :         buflen += strlen(buf + buflen);
    9423        2422 :         if (buflen >= NAMEDATALEN)
    9424           0 :             break;
    9425             :     }
    9426        2024 :     return pstrdup(buf);
    9427             : }
    9428             : 
    9429             : /*
    9430             :  * Add a check constraint to a single table and its children.  Returns the
    9431             :  * address of the constraint added to the parent relation, if one gets added,
    9432             :  * or InvalidObjectAddress otherwise.
    9433             :  *
    9434             :  * Subroutine for ATExecAddConstraint.
    9435             :  *
    9436             :  * We must recurse to child tables during execution, rather than using
    9437             :  * ALTER TABLE's normal prep-time recursion.  The reason is that all the
    9438             :  * constraints *must* be given the same name, else they won't be seen as
    9439             :  * related later.  If the user didn't explicitly specify a name, then
    9440             :  * AddRelationNewConstraints would normally assign different names to the
    9441             :  * child constraints.  To fix that, we must capture the name assigned at
    9442             :  * the parent table and pass that down.
    9443             :  */
    9444             : static ObjectAddress
    9445        1314 : ATAddCheckConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
    9446             :                      Constraint *constr, bool recurse, bool recursing,
    9447             :                      bool is_readd, LOCKMODE lockmode)
    9448             : {
    9449             :     List       *newcons;
    9450             :     ListCell   *lcon;
    9451             :     List       *children;
    9452             :     ListCell   *child;
    9453        1314 :     ObjectAddress address = InvalidObjectAddress;
    9454             : 
    9455             :     /* At top level, permission check was done in ATPrepCmd, else do it */
    9456        1314 :     if (recursing)
    9457         400 :         ATSimplePermissions(AT_AddConstraint, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
    9458             : 
    9459             :     /*
    9460             :      * Call AddRelationNewConstraints to do the work, making sure it works on
    9461             :      * a copy of the Constraint so transformExpr can't modify the original. It
    9462             :      * returns a list of cooked constraints.
    9463             :      *
    9464             :      * If the constraint ends up getting merged with a pre-existing one, it's
    9465             :      * omitted from the returned list, which is what we want: we do not need
    9466             :      * to do any validation work.  That can only happen at child tables,
    9467             :      * though, since we disallow merging at the top level.
    9468             :      */
    9469        1314 :     newcons = AddRelationNewConstraints(rel, NIL,
    9470        1314 :                                         list_make1(copyObject(constr)),
    9471        1314 :                                         recursing || is_readd,  /* allow_merge */
    9472        1314 :                                         !recursing, /* is_local */
    9473             :                                         is_readd,   /* is_internal */
    9474        1314 :                                         NULL);  /* queryString not available
    9475             :                                                  * here */
    9476             : 
    9477             :     /* we don't expect more than one constraint here */
    9478             :     Assert(list_length(newcons) <= 1);
    9479             : 
    9480             :     /* Add each to-be-validated constraint to Phase 3's queue */
    9481        2450 :     foreach(lcon, newcons)
    9482             :     {
    9483        1196 :         CookedConstraint *ccon = (CookedConstraint *) lfirst(lcon);
    9484             : 
    9485        1196 :         if (!ccon->skip_validation)
    9486             :         {
    9487             :             NewConstraint *newcon;
    9488             : 
    9489         802 :             newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
    9490         802 :             newcon->name = ccon->name;
    9491         802 :             newcon->contype = ccon->contype;
    9492         802 :             newcon->qual = ccon->expr;
    9493             : 
    9494         802 :             tab->constraints = lappend(tab->constraints, newcon);
    9495             :         }
    9496             : 
    9497             :         /* Save the actually assigned name if it was defaulted */
    9498        1196 :         if (constr->conname == NULL)
    9499         254 :             constr->conname = ccon->name;
    9500             : 
    9501        1196 :         ObjectAddressSet(address, ConstraintRelationId, ccon->conoid);
    9502             :     }
    9503             : 
    9504             :     /* At this point we must have a locked-down name to use */
    9505             :     Assert(constr->conname != NULL);
    9506             : 
    9507             :     /* Advance command counter in case same table is visited multiple times */
    9508        1254 :     CommandCounterIncrement();
    9509             : 
    9510             :     /*
    9511             :      * If the constraint got merged with an existing constraint, we're done.
    9512             :      * We mustn't recurse to child tables in this case, because they've
    9513             :      * already got the constraint, and visiting them again would lead to an
    9514             :      * incorrect value for coninhcount.
    9515             :      */
    9516        1254 :     if (newcons == NIL)
    9517          58 :         return address;
    9518             : 
    9519             :     /*
    9520             :      * If adding a NO INHERIT constraint, no need to find our children.
    9521             :      */
    9522        1196 :     if (constr->is_no_inherit)
    9523          54 :         return address;
    9524             : 
    9525             :     /*
    9526             :      * Propagate to children as appropriate.  Unlike most other ALTER
    9527             :      * routines, we have to do this one level of recursion at a time; we can't
    9528             :      * use find_all_inheritors to do it in one pass.
    9529             :      */
    9530             :     children =
    9531        1142 :         find_inheritance_children(RelationGetRelid(rel), lockmode);
    9532             : 
    9533             :     /*
    9534             :      * Check if ONLY was specified with ALTER TABLE.  If so, allow the
    9535             :      * constraint creation only if there are no children currently.  Error out
    9536             :      * otherwise.
    9537             :      */
    9538        1142 :     if (!recurse && children != NIL)
    9539           6 :         ereport(ERROR,
    9540             :                 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    9541             :                  errmsg("constraint must be added to child tables too")));
    9542             : 
    9543        1530 :     foreach(child, children)
    9544             :     {
    9545         400 :         Oid         childrelid = lfirst_oid(child);
    9546             :         Relation    childrel;
    9547             :         AlteredTableInfo *childtab;
    9548             : 
    9549             :         /* find_inheritance_children already got lock */
    9550         400 :         childrel = table_open(childrelid, NoLock);
    9551         400 :         CheckAlterTableIsSafe(childrel);
    9552             : 
    9553             :         /* Find or create work queue entry for this table */
    9554         400 :         childtab = ATGetQueueEntry(wqueue, childrel);
    9555             : 
    9556             :         /* Recurse to child */
    9557         400 :         ATAddCheckConstraint(wqueue, childtab, childrel,
    9558             :                              constr, recurse, true, is_readd, lockmode);
    9559             : 
    9560         394 :         table_close(childrel, NoLock);
    9561             :     }
    9562             : 
    9563        1130 :     return address;
    9564             : }
    9565             : 
    9566             : /*
    9567             :  * Add a foreign-key constraint to a single table; return the new constraint's
    9568             :  * address.
    9569             :  *
    9570             :  * Subroutine for ATExecAddConstraint.  Must already hold exclusive
    9571             :  * lock on the rel, and have done appropriate validity checks for it.
    9572             :  * We do permissions checks here, however.
    9573             :  *
    9574             :  * When the referenced or referencing tables (or both) are partitioned,
    9575             :  * multiple pg_constraint rows are required -- one for each partitioned table
    9576             :  * and each partition on each side (fortunately, not one for every combination
    9577             :  * thereof).  We also need action triggers on each leaf partition on the
    9578             :  * referenced side, and check triggers on each leaf partition on the
    9579             :  * referencing side.
    9580             :  */
    9581             : static ObjectAddress
    9582        2118 : ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
    9583             :                           Constraint *fkconstraint,
    9584             :                           bool recurse, bool recursing, LOCKMODE lockmode)
    9585             : {
    9586             :     Relation    pkrel;
    9587        2118 :     int16       pkattnum[INDEX_MAX_KEYS] = {0};
    9588        2118 :     int16       fkattnum[INDEX_MAX_KEYS] = {0};
    9589        2118 :     Oid         pktypoid[INDEX_MAX_KEYS] = {0};
    9590        2118 :     Oid         fktypoid[INDEX_MAX_KEYS] = {0};
    9591        2118 :     Oid         opclasses[INDEX_MAX_KEYS] = {0};
    9592        2118 :     Oid         pfeqoperators[INDEX_MAX_KEYS] = {0};
    9593        2118 :     Oid         ppeqoperators[INDEX_MAX_KEYS] = {0};
    9594        2118 :     Oid         ffeqoperators[INDEX_MAX_KEYS] = {0};
    9595        2118 :     int16       fkdelsetcols[INDEX_MAX_KEYS] = {0};
    9596             :     int         i;
    9597             :     int         numfks,
    9598             :                 numpks,
    9599             :                 numfkdelsetcols;
    9600             :     Oid         indexOid;
    9601             :     bool        old_check_ok;
    9602             :     ObjectAddress address;
    9603        2118 :     ListCell   *old_pfeqop_item = list_head(fkconstraint->old_conpfeqop);
    9604             : 
    9605             :     /*
    9606             :      * Grab ShareRowExclusiveLock on the pk table, so that someone doesn't
    9607             :      * delete rows out from under us.
    9608             :      */
    9609        2118 :     if (OidIsValid(fkconstraint->old_pktable_oid))
    9610          72 :         pkrel = table_open(fkconstraint->old_pktable_oid, ShareRowExclusiveLock);
    9611             :     else
    9612        2046 :         pkrel = table_openrv(fkconstraint->pktable, ShareRowExclusiveLock);
    9613             : 
    9614             :     /*
    9615             :      * Validity checks (permission checks wait till we have the column
    9616             :      * numbers)
    9617             :      */
    9618        2118 :     if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
    9619             :     {
    9620         300 :         if (!recurse)
    9621           6 :             ereport(ERROR,
    9622             :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    9623             :                      errmsg("cannot use ONLY for foreign key on partitioned table \"%s\" referencing relation \"%s\"",
    9624             :                             RelationGetRelationName(rel),
    9625             :                             RelationGetRelationName(pkrel))));
    9626         294 :         if (fkconstraint->skip_validation && !fkconstraint->initially_valid)
    9627           6 :             ereport(ERROR,
    9628             :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    9629             :                      errmsg("cannot add NOT VALID foreign key on partitioned table \"%s\" referencing relation \"%s\"",
    9630             :                             RelationGetRelationName(rel),
    9631             :                             RelationGetRelationName(pkrel)),
    9632             :                      errdetail("This feature is not yet supported on partitioned tables.")));
    9633             :     }
    9634             : 
    9635        2106 :     if (pkrel->rd_rel->relkind != RELKIND_RELATION &&
    9636         266 :         pkrel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
    9637           0 :         ereport(ERROR,
    9638             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    9639             :                  errmsg("referenced relation \"%s\" is not a table",
    9640             :                         RelationGetRelationName(pkrel))));
    9641             : 
    9642        2106 :     if (!allowSystemTableMods && IsSystemRelation(pkrel))
    9643           2 :         ereport(ERROR,
    9644             :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
    9645             :                  errmsg("permission denied: \"%s\" is a system catalog",
    9646             :                         RelationGetRelationName(pkrel))));
    9647             : 
    9648             :     /*
    9649             :      * References from permanent or unlogged tables to temp tables, and from
    9650             :      * permanent tables to unlogged tables, are disallowed because the
    9651             :      * referenced data can vanish out from under us.  References from temp
    9652             :      * tables to any other table type are also disallowed, because other
    9653             :      * backends might need to run the RI triggers on the perm table, but they
    9654             :      * can't reliably see tuples in the local buffers of other backends.
    9655             :      */
    9656        2104 :     switch (rel->rd_rel->relpersistence)
    9657             :     {
    9658        1794 :         case RELPERSISTENCE_PERMANENT:
    9659        1794 :             if (!RelationIsPermanent(pkrel))
    9660           0 :                 ereport(ERROR,
    9661             :                         (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    9662             :                          errmsg("constraints on permanent tables may reference only permanent tables")));
    9663        1794 :             break;
    9664          32 :         case RELPERSISTENCE_UNLOGGED:
    9665          32 :             if (!RelationIsPermanent(pkrel)
    9666          32 :                 && pkrel->rd_rel->relpersistence != RELPERSISTENCE_UNLOGGED)
    9667           0 :                 ereport(ERROR,
    9668             :                         (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    9669             :                          errmsg("constraints on unlogged tables may reference only permanent or unlogged tables")));
    9670          32 :             break;
    9671         278 :         case RELPERSISTENCE_TEMP:
    9672         278 :             if (pkrel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
    9673           0 :                 ereport(ERROR,
    9674             :                         (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    9675             :                          errmsg("constraints on temporary tables may reference only temporary tables")));
    9676         278 :             if (!pkrel->rd_islocaltemp || !rel->rd_islocaltemp)
    9677           0 :                 ereport(ERROR,
    9678             :                         (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    9679             :                          errmsg("constraints on temporary tables must involve temporary tables of this session")));
    9680         278 :             break;
    9681             :     }
    9682             : 
    9683             :     /*
    9684             :      * Look up the referencing attributes to make sure they exist, and record
    9685             :      * their attnums and type OIDs.
    9686             :      */
    9687        2104 :     numfks = transformColumnNameList(RelationGetRelid(rel),
    9688             :                                      fkconstraint->fk_attrs,
    9689             :                                      fkattnum, fktypoid);
    9690             : 
    9691        2074 :     numfkdelsetcols = transformColumnNameList(RelationGetRelid(rel),
    9692             :                                               fkconstraint->fk_del_set_cols,
    9693             :                                               fkdelsetcols, NULL);
    9694        2068 :     validateFkOnDeleteSetColumns(numfks, fkattnum,
    9695             :                                  numfkdelsetcols, fkdelsetcols,
    9696             :                                  fkconstraint->fk_del_set_cols);
    9697             : 
    9698             :     /*
    9699             :      * If the attribute list for the referenced table was omitted, lookup the
    9700             :      * definition of the primary key and use it.  Otherwise, validate the
    9701             :      * supplied attribute list.  In either case, discover the index OID and
    9702             :      * index opclasses, and the attnums and type OIDs of the attributes.
    9703             :      */
    9704        2062 :     if (fkconstraint->pk_attrs == NIL)
    9705             :     {
    9706         944 :         numpks = transformFkeyGetPrimaryKey(pkrel, &indexOid,
    9707             :                                             &fkconstraint->pk_attrs,
    9708             :                                             pkattnum, pktypoid,
    9709             :                                             opclasses);
    9710             :     }
    9711             :     else
    9712             :     {
    9713        1118 :         numpks = transformColumnNameList(RelationGetRelid(pkrel),
    9714             :                                          fkconstraint->pk_attrs,
    9715             :                                          pkattnum, pktypoid);
    9716             :         /* Look for an index matching the column list */
    9717        1088 :         indexOid = transformFkeyCheckAttrs(pkrel, numpks, pkattnum,
    9718             :                                            opclasses);
    9719             :     }
    9720             : 
    9721             :     /*
    9722             :      * Now we can check permissions.
    9723             :      */
    9724        2020 :     checkFkeyPermissions(pkrel, pkattnum, numpks);
    9725             : 
    9726             :     /*
    9727             :      * Check some things for generated columns.
    9728             :      */
    9729        4544 :     for (i = 0; i < numfks; i++)
    9730             :     {
    9731        2536 :         char        attgenerated = TupleDescAttr(RelationGetDescr(rel), fkattnum[i] - 1)->attgenerated;
    9732             : 
    9733        2536 :         if (attgenerated)
    9734             :         {
    9735             :             /*
    9736             :              * Check restrictions on UPDATE/DELETE actions, per SQL standard
    9737             :              */
    9738          30 :             if (fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETNULL ||
    9739          30 :                 fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETDEFAULT ||
    9740          30 :                 fkconstraint->fk_upd_action == FKCONSTR_ACTION_CASCADE)
    9741           6 :                 ereport(ERROR,
    9742             :                         (errcode(ERRCODE_SYNTAX_ERROR),
    9743             :                          errmsg("invalid %s action for foreign key constraint containing generated column",
    9744             :                                 "ON UPDATE")));
    9745          24 :             if (fkconstraint->fk_del_action == FKCONSTR_ACTION_SETNULL ||
    9746          18 :                 fkconstraint->fk_del_action == FKCONSTR_ACTION_SETDEFAULT)
    9747           6 :                 ereport(ERROR,
    9748             :                         (errcode(ERRCODE_SYNTAX_ERROR),
    9749             :                          errmsg("invalid %s action for foreign key constraint containing generated column",
    9750             :                                 "ON DELETE")));
    9751             :         }
    9752             :     }
    9753             : 
    9754             :     /*
    9755             :      * Look up the equality operators to use in the constraint.
    9756             :      *
    9757             :      * Note that we have to be careful about the difference between the actual
    9758             :      * PK column type and the opclass' declared input type, which might be
    9759             :      * only binary-compatible with it.  The declared opcintype is the right
    9760             :      * thing to probe pg_amop with.
    9761             :      */
    9762        2008 :     if (numfks != numpks)
    9763           0 :         ereport(ERROR,
    9764             :                 (errcode(ERRCODE_INVALID_FOREIGN_KEY),
    9765             :                  errmsg("number of referencing and referenced columns for foreign key disagree")));
    9766             : 
    9767             :     /*
    9768             :      * On the strength of a previous constraint, we might avoid scanning
    9769             :      * tables to validate this one.  See below.
    9770             :      */
    9771        2008 :     old_check_ok = (fkconstraint->old_conpfeqop != NIL);
    9772             :     Assert(!old_check_ok || numfks == list_length(fkconstraint->old_conpfeqop));
    9773             : 
    9774        4178 :     for (i = 0; i < numpks; i++)
    9775             :     {
    9776        2380 :         Oid         pktype = pktypoid[i];
    9777        2380 :         Oid         fktype = fktypoid[i];
    9778             :         Oid         fktyped;
    9779             :         HeapTuple   cla_ht;
    9780             :         Form_pg_opclass cla_tup;
    9781             :         Oid         amid;
    9782             :         Oid         opfamily;
    9783             :         Oid         opcintype;
    9784             :         Oid         pfeqop;
    9785             :         Oid         ppeqop;
    9786             :         Oid         ffeqop;
    9787             :         int16       eqstrategy;
    9788             :         Oid         pfeqop_right;
    9789             : 
    9790             :         /* We need several fields out of the pg_opclass entry */
    9791        2380 :         cla_ht = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclasses[i]));
    9792        2380 :         if (!HeapTupleIsValid(cla_ht))
    9793           0 :             elog(ERROR, "cache lookup failed for opclass %u", opclasses[i]);
    9794        2380 :         cla_tup = (Form_pg_opclass) GETSTRUCT(cla_ht);
    9795        2380 :         amid = cla_tup->opcmethod;
    9796        2380 :         opfamily = cla_tup->opcfamily;
    9797        2380 :         opcintype = cla_tup->opcintype;
    9798        2380 :         ReleaseSysCache(cla_ht);
    9799             : 
    9800             :         /*
    9801             :          * Check it's a btree; currently this can never fail since no other
    9802             :          * index AMs support unique indexes.  If we ever did have other types
    9803             :          * of unique indexes, we'd need a way to determine which operator
    9804             :          * strategy number is equality.  (Is it reasonable to insist that
    9805             :          * every such index AM use btree's number for equality?)
    9806             :          */
    9807        2380 :         if (amid != BTREE_AM_OID)
    9808           0 :             elog(ERROR, "only b-tree indexes are supported for foreign keys");
    9809        2380 :         eqstrategy = BTEqualStrategyNumber;
    9810             : 
    9811             :         /*
    9812             :          * There had better be a primary equality operator for the index.
    9813             :          * We'll use it for PK = PK comparisons.
    9814             :          */
    9815        2380 :         ppeqop = get_opfamily_member(opfamily, opcintype, opcintype,
    9816             :                                      eqstrategy);
    9817             : 
    9818        2380 :         if (!OidIsValid(ppeqop))
    9819           0 :             elog(ERROR, "missing operator %d(%u,%u) in opfamily %u",
    9820             :                  eqstrategy, opcintype, opcintype, opfamily);
    9821             : 
    9822             :         /*
    9823             :          * Are there equality operators that take exactly the FK type? Assume
    9824             :          * we should look through any domain here.
    9825             :          */
    9826        2380 :         fktyped = getBaseType(fktype);
    9827             : 
    9828        2380 :         pfeqop = get_opfamily_member(opfamily, opcintype, fktyped,
    9829             :                                      eqstrategy);
    9830        2380 :         if (OidIsValid(pfeqop))
    9831             :         {
    9832        2122 :             pfeqop_right = fktyped;
    9833        2122 :             ffeqop = get_opfamily_member(opfamily, fktyped, fktyped,
    9834             :                                          eqstrategy);
    9835             :         }
    9836             :         else
    9837             :         {
    9838             :             /* keep compiler quiet */
    9839         258 :             pfeqop_right = InvalidOid;
    9840         258 :             ffeqop = InvalidOid;
    9841             :         }
    9842             : 
    9843        2380 :         if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
    9844             :         {
    9845             :             /*
    9846             :              * Otherwise, look for an implicit cast from the FK type to the
    9847             :              * opcintype, and if found, use the primary equality operator.
    9848             :              * This is a bit tricky because opcintype might be a polymorphic
    9849             :              * type such as ANYARRAY or ANYENUM; so what we have to test is
    9850             :              * whether the two actual column types can be concurrently cast to
    9851             :              * that type.  (Otherwise, we'd fail to reject combinations such
    9852             :              * as int[] and point[].)
    9853             :              */
    9854             :             Oid         input_typeids[2];
    9855             :             Oid         target_typeids[2];
    9856             : 
    9857         258 :             input_typeids[0] = pktype;
    9858         258 :             input_typeids[1] = fktype;
    9859         258 :             target_typeids[0] = opcintype;
    9860         258 :             target_typeids[1] = opcintype;
    9861         258 :             if (can_coerce_type(2, input_typeids, target_typeids,
    9862             :                                 COERCION_IMPLICIT))
    9863             :             {
    9864          48 :                 pfeqop = ffeqop = ppeqop;
    9865          48 :                 pfeqop_right = opcintype;
    9866             :             }
    9867             :         }
    9868             : 
    9869        2380 :         if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
    9870         210 :             ereport(ERROR,
    9871             :                     (errcode(ERRCODE_DATATYPE_MISMATCH),
    9872             :                      errmsg("foreign key constraint \"%s\" cannot be implemented",
    9873             :                             fkconstraint->conname),
    9874             :                      errdetail("Key columns \"%s\" and \"%s\" "
    9875             :                                "are of incompatible types: %s and %s.",
    9876             :                                strVal(list_nth(fkconstraint->fk_attrs, i)),
    9877             :                                strVal(list_nth(fkconstraint->pk_attrs, i)),
    9878             :                                format_type_be(fktype),
    9879             :                                format_type_be(pktype))));
    9880             : 
    9881        2170 :         if (old_check_ok)
    9882             :         {
    9883             :             /*
    9884             :              * When a pfeqop changes, revalidate the constraint.  We could
    9885             :              * permit intra-opfamily changes, but that adds subtle complexity
    9886             :              * without any concrete benefit for core types.  We need not
    9887             :              * assess ppeqop or ffeqop, which RI_Initial_Check() does not use.
    9888             :              */
    9889           6 :             old_check_ok = (pfeqop == lfirst_oid(old_pfeqop_item));
    9890           6 :             old_pfeqop_item = lnext(fkconstraint->old_conpfeqop,
    9891             :                                     old_pfeqop_item);
    9892             :         }
    9893        2170 :         if (old_check_ok)
    9894             :         {
    9895             :             Oid         old_fktype;
    9896             :             Oid         new_fktype;
    9897             :             CoercionPathType old_pathtype;
    9898             :             CoercionPathType new_pathtype;
    9899             :             Oid         old_castfunc;
    9900             :             Oid         new_castfunc;
    9901           6 :             Form_pg_attribute attr = TupleDescAttr(tab->oldDesc,
    9902             :                                                    fkattnum[i] - 1);
    9903             : 
    9904             :             /*
    9905             :              * Identify coercion pathways from each of the old and new FK-side
    9906             :              * column types to the right (foreign) operand type of the pfeqop.
    9907             :              * We may assume that pg_constraint.conkey is not changing.
    9908             :              */
    9909           6 :             old_fktype = attr->atttypid;
    9910           6 :             new_fktype = fktype;
    9911           6 :             old_pathtype = findFkeyCast(pfeqop_right, old_fktype,
    9912             :                                         &old_castfunc);
    9913           6 :             new_pathtype = findFkeyCast(pfeqop_right, new_fktype,
    9914             :                                         &new_castfunc);
    9915             : 
    9916             :             /*
    9917             :              * Upon a change to the cast from the FK column to its pfeqop
    9918             :              * operand, revalidate the constraint.  For this evaluation, a
    9919             :              * binary coercion cast is equivalent to no cast at all.  While
    9920             :              * type implementors should design implicit casts with an eye
    9921             :              * toward consistency of operations like equality, we cannot
    9922             :              * assume here that they have done so.
    9923             :              *
    9924             :              * A function with a polymorphic argument could change behavior
    9925             :              * arbitrarily in response to get_fn_expr_argtype().  Therefore,
    9926             :              * when the cast destination is polymorphic, we only avoid
    9927             :              * revalidation if the input type has not changed at all.  Given
    9928             :              * just the core data types and operator classes, this requirement
    9929             :              * prevents no would-be optimizations.
    9930             :              *
    9931             :              * If the cast converts from a base type to a domain thereon, then
    9932             :              * that domain type must be the opcintype of the unique index.
    9933             :              * Necessarily, the primary key column must then be of the domain
    9934             :              * type.  Since the constraint was previously valid, all values on
    9935             :              * the foreign side necessarily exist on the primary side and in
    9936             :              * turn conform to the domain.  Consequently, we need not treat
    9937             :              * domains specially here.
    9938             :              *
    9939             :              * Since we require that all collations share the same notion of
    9940             :              * equality (which they do, because texteq reduces to bitwise
    9941             :              * equality), we don't compare collation here.
    9942             :              *
    9943             :              * We need not directly consider the PK type.  It's necessarily
    9944             :              * binary coercible to the opcintype of the unique index column,
    9945             :              * and ri_triggers.c will only deal with PK datums in terms of
    9946             :              * that opcintype.  Changing the opcintype also changes pfeqop.
    9947             :              */
    9948           6 :             old_check_ok = (new_pathtype == old_pathtype &&
    9949          12 :                             new_castfunc == old_castfunc &&
    9950           6 :                             (!IsPolymorphicType(pfeqop_right) ||
    9951             :                              new_fktype == old_fktype));
    9952             :         }
    9953             : 
    9954        2170 :         pfeqoperators[i] = pfeqop;
    9955        2170 :         ppeqoperators[i] = ppeqop;
    9956        2170 :         ffeqoperators[i] = ffeqop;
    9957             :     }
    9958             : 
    9959             :     /*
    9960             :      * Create all the constraint and trigger objects, recursing to partitions
    9961             :      * as necessary.  First handle the referenced side.
    9962             :      */
    9963        1798 :     address = addFkRecurseReferenced(wqueue, fkconstraint, rel, pkrel,
    9964             :                                      indexOid,
    9965             :                                      InvalidOid,    /* no parent constraint */
    9966             :                                      numfks,
    9967             :                                      pkattnum,
    9968             :                                      fkattnum,
    9969             :                                      pfeqoperators,
    9970             :                                      ppeqoperators,
    9971             :                                      ffeqoperators,
    9972             :                                      numfkdelsetcols,
    9973             :                                      fkdelsetcols,
    9974             :                                      old_check_ok,
    9975             :                                      InvalidOid, InvalidOid);
    9976             : 
    9977             :     /* Now handle the referencing side. */
    9978        1798 :     addFkRecurseReferencing(wqueue, fkconstraint, rel, pkrel,
    9979             :                             indexOid,
    9980             :                             address.objectId,
    9981             :                             numfks,
    9982             :                             pkattnum,
    9983             :                             fkattnum,
    9984             :                             pfeqoperators,
    9985             :                             ppeqoperators,
    9986             :                             ffeqoperators,
    9987             :                             numfkdelsetcols,
    9988             :                             fkdelsetcols,
    9989             :                             old_check_ok,
    9990             :                             lockmode,
    9991             :                             InvalidOid, InvalidOid);
    9992             : 
    9993             :     /*
    9994             :      * Done.  Close pk table, but keep lock until we've committed.
    9995             :      */
    9996        1798 :     table_close(pkrel, NoLock);
    9997             : 
    9998        1798 :     return address;
    9999             : }
   10000             : 
   10001             : /*
   10002             :  * validateFkOnDeleteSetColumns
   10003             :  *      Verifies that columns used in ON DELETE SET NULL/DEFAULT (...)
   10004             :  *      column lists are valid.
   10005             :  */
   10006             : void
   10007        2068 : validateFkOnDeleteSetColumns(int numfks, const int16 *fkattnums,
   10008             :                              int numfksetcols, const int16 *fksetcolsattnums,
   10009             :                              List *fksetcols)
   10010             : {
   10011        2092 :     for (int i = 0; i < numfksetcols; i++)
   10012             :     {
   10013          30 :         int16       setcol_attnum = fksetcolsattnums[i];
   10014          30 :         bool        seen = false;
   10015             : 
   10016          54 :         for (int j = 0; j < numfks; j++)
   10017             :         {
   10018          48 :             if (fkattnums[j] == setcol_attnum)
   10019             :             {
   10020          24 :                 seen = true;
   10021          24 :                 break;
   10022             :             }
   10023             :         }
   10024             : 
   10025          30 :         if (!seen)
   10026             :         {
   10027           6 :             char       *col = strVal(list_nth(fksetcols, i));
   10028             : 
   10029           6 :             ereport(ERROR,
   10030             :                     (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
   10031             :                      errmsg("column \"%s\" referenced in ON DELETE SET action must be part of foreign key", col)));
   10032             :         }
   10033             :     }
   10034        2062 : }
   10035             : 
   10036             : /*
   10037             :  * addFkRecurseReferenced
   10038             :  *      subroutine for ATAddForeignKeyConstraint; recurses on the referenced
   10039             :  *      side of the constraint
   10040             :  *
   10041             :  * Create pg_constraint rows for the referenced side of the constraint,
   10042             :  * referencing the parent of the referencing side; also create action triggers
   10043             :  * on leaf partitions.  If the table is partitioned, recurse to handle each
   10044             :  * partition.
   10045             :  *
   10046             :  * wqueue is the ALTER TABLE work queue; can be NULL when not running as part
   10047             :  * of an ALTER TABLE sequence.
   10048             :  * fkconstraint is the constraint being added.
   10049             :  * rel is the root referencing relation.
   10050             :  * pkrel is the referenced relation; might be a partition, if recursing.
   10051             :  * indexOid is the OID of the index (on pkrel) implementing this constraint.
   10052             :  * parentConstr is the OID of a parent constraint; InvalidOid if this is a
   10053             :  * top-level constraint.
   10054             :  * numfks is the number of columns in the foreign key
   10055             :  * pkattnum is the attnum array of referenced attributes.
   10056             :  * fkattnum is the attnum array of referencing attributes.
   10057             :  * numfkdelsetcols is the number of columns in the ON DELETE SET NULL/DEFAULT
   10058             :  *      (...) clause
   10059             :  * fkdelsetcols is the attnum array of the columns in the ON DELETE SET
   10060             :  *      NULL/DEFAULT clause
   10061             :  * pf/pp/ffeqoperators are OID array of operators between columns.
   10062             :  * old_check_ok signals that this constraint replaces an existing one that
   10063             :  * was already validated (thus this one doesn't need validation).
   10064             :  * parentDelTrigger and parentUpdTrigger, when being recursively called on
   10065             :  * a partition, are the OIDs of the parent action triggers for DELETE and
   10066             :  * UPDATE respectively.
   10067             :  */
   10068             : static ObjectAddress
   10069        2494 : addFkRecurseReferenced(List **wqueue, Constraint *fkconstraint, Relation rel,
   10070             :                        Relation pkrel, Oid indexOid, Oid parentConstr,
   10071             :                        int numfks,
   10072             :                        int16 *pkattnum, int16 *fkattnum, Oid *pfeqoperators,
   10073             :                        Oid *ppeqoperators, Oid *ffeqoperators,
   10074             :                        int numfkdelsetcols, int16 *fkdelsetcols,
   10075             :                        bool old_check_ok,
   10076             :                        Oid parentDelTrigger, Oid parentUpdTrigger)
   10077             : {
   10078             :     ObjectAddress address;
   10079             :     Oid         constrOid;
   10080             :     char       *conname;
   10081             :     bool        conislocal;
   10082             :     int         coninhcount;
   10083             :     bool        connoinherit;
   10084             :     Oid         deleteTriggerOid,
   10085             :                 updateTriggerOid;
   10086             : 
   10087             :     /*
   10088             :      * Verify relkind for each referenced partition.  At the top level, this
   10089             :      * is redundant with a previous check, but we need it when recursing.
   10090             :      */
   10091        2494 :     if (pkrel->rd_rel->relkind != RELKIND_RELATION &&
   10092         366 :         pkrel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
   10093           0 :         ereport(ERROR,
   10094             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   10095             :                  errmsg("referenced relation \"%s\" is not a table",
   10096             :                         RelationGetRelationName(pkrel))));
   10097             : 
   10098             :     /*
   10099             :      * Caller supplies us with a constraint name; however, it may be used in
   10100             :      * this partition, so come up with a different one in that case.
   10101             :      */
   10102        2494 :     if (ConstraintNameIsUsed(CONSTRAINT_RELATION,
   10103             :                              RelationGetRelid(rel),
   10104        2494 :                              fkconstraint->conname))
   10105         696 :         conname = ChooseConstraintName(RelationGetRelationName(rel),
   10106         696 :                                        ChooseForeignKeyConstraintNameAddition(fkconstraint->fk_attrs),
   10107             :                                        "fkey",
   10108         696 :                                        RelationGetNamespace(rel), NIL);
   10109             :     else
   10110        1798 :         conname = fkconstraint->conname;
   10111             : 
   10112        2494 :     if (OidIsValid(parentConstr))
   10113             :     {
   10114         696 :         conislocal = false;
   10115         696 :         coninhcount = 1;
   10116         696 :         connoinherit = false;
   10117             :     }
   10118             :     else
   10119             :     {
   10120        1798 :         conislocal = true;
   10121        1798 :         coninhcount = 0;
   10122             : 
   10123             :         /*
   10124             :          * always inherit for partitioned tables, never for legacy inheritance
   10125             :          */
   10126        1798 :         connoinherit = rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE;
   10127             :     }
   10128             : 
   10129             :     /*
   10130             :      * Record the FK constraint in pg_constraint.
   10131             :      */
   10132        2494 :     constrOid = CreateConstraintEntry(conname,
   10133        2494 :                                       RelationGetNamespace(rel),
   10134             :                                       CONSTRAINT_FOREIGN,
   10135        2494 :                                       fkconstraint->deferrable,
   10136        2494 :                                       fkconstraint->initdeferred,
   10137        2494 :                                       fkconstraint->initially_valid,
   10138             :                                       parentConstr,
   10139             :                                       RelationGetRelid(rel),
   10140             :                                       fkattnum,
   10141             :                                       numfks,
   10142             :                                       numfks,
   10143             :                                       InvalidOid,   /* not a domain constraint */
   10144             :                                       indexOid,
   10145             :                                       RelationGetRelid(pkrel),
   10146             :                                       pkattnum,
   10147             :                                       pfeqoperators,
   10148             :                                       ppeqoperators,
   10149             :                                       ffeqoperators,
   10150             :                                       numfks,
   10151        2494 :                                       fkconstraint->fk_upd_action,
   10152        2494 :                                       fkconstraint->fk_del_action,
   10153             :                                       fkdelsetcols,
   10154             :                                       numfkdelsetcols,
   10155        2494 :                                       fkconstraint->fk_matchtype,
   10156             :                                       NULL, /* no exclusion constraint */
   10157             :                                       NULL, /* no check constraint */
   10158             :                                       NULL,
   10159             :                                       conislocal,   /* islocal */
   10160             :                                       coninhcount,  /* inhcount */
   10161             :                                       connoinherit, /* conNoInherit */
   10162             :                                       false);   /* is_internal */
   10163             : 
   10164        2494 :     ObjectAddressSet(address, ConstraintRelationId, constrOid);
   10165             : 
   10166             :     /*
   10167             :      * Mark the child constraint as part of the parent constraint; it must not
   10168             :      * be dropped on its own.  (This constraint is deleted when the partition
   10169             :      * is detached, but a special check needs to occur that the partition
   10170             :      * contains no referenced values.)
   10171             :      */
   10172        2494 :     if (OidIsValid(parentConstr))
   10173             :     {
   10174             :         ObjectAddress referenced;
   10175             : 
   10176         696 :         ObjectAddressSet(referenced, ConstraintRelationId, parentConstr);
   10177         696 :         recordDependencyOn(&address, &referenced, DEPENDENCY_INTERNAL);
   10178             :     }
   10179             : 
   10180             :     /* make new constraint visible, in case we add more */
   10181        2494 :     CommandCounterIncrement();
   10182             : 
   10183             :     /*
   10184             :      * Create the action triggers that enforce the constraint.
   10185             :      */
   10186        2494 :     createForeignKeyActionTriggers(rel, RelationGetRelid(pkrel),
   10187             :                                    fkconstraint,
   10188             :                                    constrOid, indexOid,
   10189             :                                    parentDelTrigger, parentUpdTrigger,
   10190             :                                    &deleteTriggerOid, &updateTriggerOid);
   10191             : 
   10192             :     /*
   10193             :      * If the referenced table is partitioned, recurse on ourselves to handle
   10194             :      * each partition.  We need one pg_constraint row created for each
   10195             :      * partition in addition to the pg_constraint row for the parent table.
   10196             :      */
   10197        2494 :     if (pkrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
   10198             :     {
   10199         366 :         PartitionDesc pd = RelationGetPartitionDesc(pkrel, true);
   10200             : 
   10201         912 :         for (int i = 0; i < pd->nparts; i++)
   10202             :         {
   10203             :             Relation    partRel;
   10204             :             AttrMap    *map;
   10205             :             AttrNumber *mapped_pkattnum;
   10206             :             Oid         partIndexId;
   10207             : 
   10208         546 :             partRel = table_open(pd->oids[i], ShareRowExclusiveLock);
   10209             : 
   10210             :             /*
   10211             :              * Map the attribute numbers in the referenced side of the FK
   10212             :              * definition to match the partition's column layout.
   10213             :              */
   10214         546 :             map = build_attrmap_by_name_if_req(RelationGetDescr(partRel),
   10215             :                                                RelationGetDescr(pkrel),
   10216             :                                                false);
   10217         546 :             if (map)
   10218             :             {
   10219          46 :                 mapped_pkattnum = palloc(sizeof(AttrNumber) * numfks);
   10220          92 :                 for (int j = 0; j < numfks; j++)
   10221          46 :                     mapped_pkattnum[j] = map->attnums[pkattnum[j] - 1];
   10222             :             }
   10223             :             else
   10224         500 :                 mapped_pkattnum = pkattnum;
   10225             : 
   10226             :             /* do the deed */
   10227         546 :             partIndexId = index_get_partition(partRel, indexOid);
   10228         546 :             if (!OidIsValid(partIndexId))
   10229           0 :                 elog(ERROR, "index for %u not found in partition %s",
   10230             :                      indexOid, RelationGetRelationName(partRel));
   10231         546 :             addFkRecurseReferenced(wqueue, fkconstraint, rel, partRel,
   10232             :                                    partIndexId, constrOid, numfks,
   10233             :                                    mapped_pkattnum, fkattnum,
   10234             :                                    pfeqoperators, ppeqoperators, ffeqoperators,
   10235             :                                    numfkdelsetcols, fkdelsetcols,
   10236             :                                    old_check_ok,
   10237             :                                    deleteTriggerOid, updateTriggerOid);
   10238             : 
   10239             :             /* Done -- clean up (but keep the lock) */
   10240         546 :             table_close(partRel, NoLock);
   10241         546 :             if (map)
   10242             :             {
   10243          46 :                 pfree(mapped_pkattnum);
   10244          46 :                 free_attrmap(map);
   10245             :             }
   10246             :         }
   10247             :     }
   10248             : 
   10249        2494 :     return address;
   10250             : }
   10251             : 
   10252             : /*
   10253             :  * addFkRecurseReferencing
   10254             :  *      subroutine for ATAddForeignKeyConstraint and CloneFkReferencing
   10255             :  *
   10256             :  * If the referencing relation is a plain relation, create the necessary check
   10257             :  * triggers that implement the constraint, and set up for Phase 3 constraint
   10258             :  * verification.  If the referencing relation is a partitioned table, then
   10259             :  * we create a pg_constraint row for it and recurse on this routine for each
   10260             :  * partition.
   10261             :  *
   10262             :  * We assume that the referenced relation is locked against concurrent
   10263             :  * deletions.  If it's a partitioned relation, every partition must be so
   10264             :  * locked.
   10265             :  *
   10266             :  * wqueue is the ALTER TABLE work queue; can be NULL when not running as part
   10267             :  * of an ALTER TABLE sequence.
   10268             :  * fkconstraint is the constraint being added.
   10269             :  * rel is the referencing relation; might be a partition, if recursing.
   10270             :  * pkrel is the root referenced relation.
   10271             :  * indexOid is the OID of the index (on pkrel) implementing this constraint.
   10272             :  * parentConstr is the OID of the parent constraint (there is always one).
   10273             :  * numfks is the number of columns in the foreign key
   10274             :  * pkattnum is the attnum array of referenced attributes.
   10275             :  * fkattnum is the attnum array of referencing attributes.
   10276             :  * pf/pp/ffeqoperators are OID array of operators between columns.
   10277             :  * numfkdelsetcols is the number of columns in the ON DELETE SET NULL/DEFAULT
   10278             :  *      (...) clause
   10279             :  * fkdelsetcols is the attnum array of the columns in the ON DELETE SET
   10280             :  *      NULL/DEFAULT clause
   10281             :  * old_check_ok signals that this constraint replaces an existing one that
   10282             :  *      was already validated (thus this one doesn't need validation).
   10283             :  * lockmode is the lockmode to acquire on partitions when recursing.
   10284             :  * parentInsTrigger and parentUpdTrigger, when being recursively called on
   10285             :  * a partition, are the OIDs of the parent check triggers for INSERT and
   10286             :  * UPDATE respectively.
   10287             :  */
   10288             : static void
   10289        2544 : addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint, Relation rel,
   10290             :                         Relation pkrel, Oid indexOid, Oid parentConstr,
   10291             :                         int numfks, int16 *pkattnum, int16 *fkattnum,
   10292             :                         Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators,
   10293             :                         int numfkdelsetcols, int16 *fkdelsetcols,
   10294             :                         bool old_check_ok, LOCKMODE lockmode,
   10295             :                         Oid parentInsTrigger, Oid parentUpdTrigger)
   10296             : {
   10297             :     Oid         insertTriggerOid,
   10298             :                 updateTriggerOid;
   10299             : 
   10300             :     Assert(OidIsValid(parentConstr));
   10301             : 
   10302        2544 :     if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
   10303           0 :         ereport(ERROR,
   10304             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   10305             :                  errmsg("foreign key constraints are not supported on foreign tables")));
   10306             : 
   10307             :     /*
   10308             :      * Add the check triggers to it and, if necessary, schedule it to be
   10309             :      * checked in Phase 3.
   10310             :      *
   10311             :      * If the relation is partitioned, drill down to do it to its partitions.
   10312             :      */
   10313        2544 :     createForeignKeyCheckTriggers(RelationGetRelid(rel),
   10314             :                                   RelationGetRelid(pkrel),
   10315             :                                   fkconstraint,
   10316             :                                   parentConstr,
   10317             :                                   indexOid,
   10318             :                                   parentInsTrigger, parentUpdTrigger,
   10319             :                                   &insertTriggerOid, &updateTriggerOid);
   10320             : 
   10321        2544 :     if (rel->rd_rel->relkind == RELKIND_RELATION)
   10322             :     {
   10323             :         /*
   10324             :          * Tell Phase 3 to check that the constraint is satisfied by existing
   10325             :          * rows. We can skip this during table creation, when requested
   10326             :          * explicitly by specifying NOT VALID in an ADD FOREIGN KEY command,
   10327             :          * and when we're recreating a constraint following a SET DATA TYPE
   10328             :          * operation that did not impugn its validity.
   10329             :          */
   10330        2144 :         if (wqueue && !old_check_ok && !fkconstraint->skip_validation)
   10331             :         {
   10332             :             NewConstraint *newcon;
   10333             :             AlteredTableInfo *tab;
   10334             : 
   10335         652 :             tab = ATGetQueueEntry(wqueue, rel);
   10336             : 
   10337         652 :             newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
   10338         652 :             newcon->name = get_constraint_name(parentConstr);
   10339         652 :             newcon->contype = CONSTR_FOREIGN;
   10340         652 :             newcon->refrelid = RelationGetRelid(pkrel);
   10341         652 :             newcon->refindid = indexOid;
   10342         652 :             newcon->conid = parentConstr;
   10343         652 :             newcon->qual = (Node *) fkconstraint;
   10344             : 
   10345         652 :             tab->constraints = lappend(tab->constraints, newcon);
   10346             :         }
   10347             :     }
   10348         400 :     else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
   10349             :     {
   10350         400 :         PartitionDesc pd = RelationGetPartitionDesc(rel, true);
   10351             :         Relation    trigrel;
   10352             : 
   10353             :         /*
   10354             :          * Triggers of the foreign keys will be manipulated a bunch of times
   10355             :          * in the loop below.  To avoid repeatedly opening/closing the trigger
   10356             :          * catalog relation, we open it here and pass it to the subroutines
   10357             :          * called below.
   10358             :          */
   10359         400 :         trigrel = table_open(TriggerRelationId, RowExclusiveLock);
   10360             : 
   10361             :         /*
   10362             :          * Recurse to take appropriate action on each partition; either we
   10363             :          * find an existing constraint to reparent to ours, or we create a new
   10364             :          * one.
   10365             :          */
   10366         764 :         for (int i = 0; i < pd->nparts; i++)
   10367             :         {
   10368         364 :             Oid         partitionId = pd->oids[i];
   10369         364 :             Relation    partition = table_open(partitionId, lockmode);
   10370             :             List       *partFKs;
   10371             :             AttrMap    *attmap;
   10372             :             AttrNumber  mapped_fkattnum[INDEX_MAX_KEYS];
   10373             :             bool        attached;
   10374             :             char       *conname;
   10375             :             Oid         constrOid;
   10376             :             ObjectAddress address,
   10377             :                         referenced;
   10378             :             ListCell   *cell;
   10379             : 
   10380         364 :             CheckAlterTableIsSafe(partition);
   10381             : 
   10382         364 :             attmap = build_attrmap_by_name(RelationGetDescr(partition),
   10383             :                                            RelationGetDescr(rel),
   10384             :                                            false);
   10385         932 :             for (int j = 0; j < numfks; j++)
   10386         568 :                 mapped_fkattnum[j] = attmap->attnums[fkattnum[j] - 1];
   10387             : 
   10388             :             /* Check whether an existing constraint can be repurposed */
   10389         364 :             partFKs = copyObject(RelationGetFKeyList(partition));
   10390         364 :             attached = false;
   10391         382 :             foreach(cell, partFKs)
   10392             :             {
   10393             :                 ForeignKeyCacheInfo *fk;
   10394             : 
   10395          30 :                 fk = lfirst_node(ForeignKeyCacheInfo, cell);
   10396          30 :                 if (tryAttachPartitionForeignKey(fk,
   10397             :                                                  partitionId,
   10398             :                                                  parentConstr,
   10399             :                                                  numfks,
   10400             :                                                  mapped_fkattnum,
   10401             :                                                  pkattnum,
   10402             :                                                  pfeqoperators,
   10403             :                                                  insertTriggerOid,
   10404             :                                                  updateTriggerOid,
   10405             :                                                  trigrel))
   10406             :                 {
   10407          12 :                     attached = true;
   10408          12 :                     break;
   10409             :                 }
   10410             :             }
   10411         364 :             if (attached)
   10412             :             {
   10413          12 :                 table_close(partition, NoLock);
   10414          12 :                 continue;
   10415             :             }
   10416             : 
   10417             :             /*
   10418             :              * No luck finding a good constraint to reuse; create our own.
   10419             :              */
   10420         352 :             if (ConstraintNameIsUsed(CONSTRAINT_RELATION,
   10421             :                                      RelationGetRelid(partition),
   10422         352 :                                      fkconstraint->conname))
   10423           0 :                 conname = ChooseConstraintName(RelationGetRelationName(partition),
   10424           0 :                                                ChooseForeignKeyConstraintNameAddition(fkconstraint->fk_attrs),
   10425             :                                                "fkey",
   10426           0 :                                                RelationGetNamespace(partition), NIL);
   10427             :             else
   10428         352 :                 conname = fkconstraint->conname;
   10429             :             constrOid =
   10430         352 :                 CreateConstraintEntry(conname,
   10431         352 :                                       RelationGetNamespace(partition),
   10432             :                                       CONSTRAINT_FOREIGN,
   10433         352 :                                       fkconstraint->deferrable,
   10434         352 :                                       fkconstraint->initdeferred,
   10435         352 :                                       fkconstraint->initially_valid,
   10436             :                                       parentConstr,
   10437             :                                       partitionId,
   10438             :                                       mapped_fkattnum,
   10439             :                                       numfks,
   10440             :                                       numfks,
   10441             :                                       InvalidOid,
   10442             :                                       indexOid,
   10443             :                                       RelationGetRelid(pkrel),
   10444             :                                       pkattnum,
   10445             :                                       pfeqoperators,
   10446             :                                       ppeqoperators,
   10447             :                                       ffeqoperators,
   10448             :                                       numfks,
   10449         352 :                                       fkconstraint->fk_upd_action,
   10450         352 :                                       fkconstraint->fk_del_action,
   10451             :                                       fkdelsetcols,
   10452             :                                       numfkdelsetcols,
   10453         352 :                                       fkconstraint->fk_matchtype,
   10454             :                                       NULL,
   10455             :                                       NULL,
   10456             :                                       NULL,
   10457             :                                       false,
   10458             :                                       1,
   10459             :                                       false,
   10460             :                                       false);
   10461             : 
   10462             :             /*
   10463             :              * Give this constraint partition-type dependencies on the parent
   10464             :              * constraint as well as the table.
   10465             :              */
   10466         352 :             ObjectAddressSet(address, ConstraintRelationId, constrOid);
   10467         352 :             ObjectAddressSet(referenced, ConstraintRelationId, parentConstr);
   10468         352 :             recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_PRI);
   10469         352 :             ObjectAddressSet(referenced, RelationRelationId, partitionId);
   10470         352 :             recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_SEC);
   10471             : 
   10472             :             /* Make all this visible before recursing */
   10473         352 :             CommandCounterIncrement();
   10474             : 
   10475             :             /* call ourselves to finalize the creation and we're done */
   10476         352 :             addFkRecurseReferencing(wqueue, fkconstraint, partition, pkrel,
   10477             :                                     indexOid,
   10478             :                                     constrOid,
   10479             :                                     numfks,
   10480             :                                     pkattnum,
   10481             :                                     mapped_fkattnum,
   10482             :                                     pfeqoperators,
   10483             :                                     ppeqoperators,
   10484             :                                     ffeqoperators,
   10485             :                                     numfkdelsetcols,
   10486             :                                     fkdelsetcols,
   10487             :                                     old_check_ok,
   10488             :                                     lockmode,
   10489             :                                     insertTriggerOid,
   10490             :                                     updateTriggerOid);
   10491             : 
   10492         352 :             table_close(partition, NoLock);
   10493             :         }
   10494             : 
   10495         400 :         table_close(trigrel, RowExclusiveLock);
   10496             :     }
   10497        2544 : }
   10498             : 
   10499             : /*
   10500             :  * CloneForeignKeyConstraints
   10501             :  *      Clone foreign keys from a partitioned table to a newly acquired
   10502             :  *      partition.
   10503             :  *
   10504             :  * partitionRel is a partition of parentRel, so we can be certain that it has
   10505             :  * the same columns with the same datatypes.  The columns may be in different
   10506             :  * order, though.
   10507             :  *
   10508             :  * wqueue must be passed to set up phase 3 constraint checking, unless the
   10509             :  * referencing-side partition is known to be empty (such as in CREATE TABLE /
   10510             :  * PARTITION OF).
   10511             :  */
   10512             : static void
   10513       10396 : CloneForeignKeyConstraints(List **wqueue, Relation parentRel,
   10514             :                            Relation partitionRel)
   10515             : {
   10516             :     /* This only works for declarative partitioning */
   10517             :     Assert(parentRel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
   10518             : 
   10519             :     /*
   10520             :      * Clone constraints for which the parent is on the referenced side.
   10521             :      */
   10522       10396 :     CloneFkReferenced(parentRel, partitionRel);
   10523             : 
   10524             :     /*
   10525             :      * Now clone constraints where the parent is on the referencing side.
   10526             :      */
   10527       10396 :     CloneFkReferencing(wqueue, parentRel, partitionRel);
   10528       10396 : }
   10529             : 
   10530             : /*
   10531             :  * CloneFkReferenced
   10532             :  *      Subroutine for CloneForeignKeyConstraints
   10533             :  *
   10534             :  * Find all the FKs that have the parent relation on the referenced side;
   10535             :  * clone those constraints to the given partition.  This is to be called
   10536             :  * when the partition is being created or attached.
   10537             :  *
   10538             :  * This ignores self-referencing FKs; those are handled by CloneFkReferencing.
   10539             :  *
   10540             :  * This recurses to partitions, if the relation being attached is partitioned.
   10541             :  * Recursion is done by calling addFkRecurseReferenced.
   10542             :  */
   10543             : static void
   10544       10396 : CloneFkReferenced(Relation parentRel, Relation partitionRel)
   10545             : {
   10546             :     Relation    pg_constraint;
   10547             :     AttrMap    *attmap;
   10548             :     ListCell   *cell;
   10549             :     SysScanDesc scan;
   10550             :     ScanKeyData key[2];
   10551             :     HeapTuple   tuple;
   10552       10396 :     List       *clone = NIL;
   10553             :     Relation    trigrel;
   10554             : 
   10555             :     /*
   10556             :      * Search for any constraints where this partition's parent is in the
   10557             :      * referenced side.  However, we must not clone any constraint whose
   10558             :      * parent constraint is also going to be cloned, to avoid duplicates.  So
   10559             :      * do it in two steps: first construct the list of constraints to clone,
   10560             :      * then go over that list cloning those whose parents are not in the list.
   10561             :      * (We must not rely on the parent being seen first, since the catalog
   10562             :      * scan could return children first.)
   10563             :      */
   10564       10396 :     pg_constraint = table_open(ConstraintRelationId, RowShareLock);
   10565       10396 :     ScanKeyInit(&key[0],
   10566             :                 Anum_pg_constraint_confrelid, BTEqualStrategyNumber,
   10567             :                 F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(parentRel)));
   10568       10396 :     ScanKeyInit(&key[1],
   10569             :                 Anum_pg_constraint_contype, BTEqualStrategyNumber,
   10570             :                 F_CHAREQ, CharGetDatum(CONSTRAINT_FOREIGN));
   10571             :     /* This is a seqscan, as we don't have a usable index ... */
   10572       10396 :     scan = systable_beginscan(pg_constraint, InvalidOid, true,
   10573             :                               NULL, 2, key);
   10574       10726 :     while ((tuple = systable_getnext(scan)) != NULL)
   10575             :     {
   10576         330 :         Form_pg_constraint constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
   10577             : 
   10578         330 :         clone = lappend_oid(clone, constrForm->oid);
   10579             :     }
   10580       10396 :     systable_endscan(scan);
   10581       10396 :     table_close(pg_constraint, RowShareLock);
   10582             : 
   10583             :     /*
   10584             :      * Triggers of the foreign keys will be manipulated a bunch of times in
   10585             :      * the loop below.  To avoid repeatedly opening/closing the trigger
   10586             :      * catalog relation, we open it here and pass it to the subroutines called
   10587             :      * below.
   10588             :      */
   10589       10396 :     trigrel = table_open(TriggerRelationId, RowExclusiveLock);
   10590             : 
   10591       10396 :     attmap = build_attrmap_by_name(RelationGetDescr(partitionRel),
   10592             :                                    RelationGetDescr(parentRel),
   10593             :                                    false);
   10594       10726 :     foreach(cell, clone)
   10595             :     {
   10596         330 :         Oid         constrOid = lfirst_oid(cell);
   10597             :         Form_pg_constraint constrForm;
   10598             :         Relation    fkRel;
   10599             :         Oid         indexOid;
   10600             :         Oid         partIndexId;
   10601             :         int         numfks;
   10602             :         AttrNumber  conkey[INDEX_MAX_KEYS];
   10603             :         AttrNumber  mapped_confkey[INDEX_MAX_KEYS];
   10604             :         AttrNumber  confkey[INDEX_MAX_KEYS];
   10605             :         Oid         conpfeqop[INDEX_MAX_KEYS];
   10606             :         Oid         conppeqop[INDEX_MAX_KEYS];
   10607             :         Oid         conffeqop[INDEX_MAX_KEYS];
   10608             :         int         numfkdelsetcols;
   10609             :         AttrNumber  confdelsetcols[INDEX_MAX_KEYS];
   10610             :         Constraint *fkconstraint;
   10611             :         Oid         deleteTriggerOid,
   10612             :                     updateTriggerOid;
   10613             : 
   10614         330 :         tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constrOid));
   10615         330 :         if (!HeapTupleIsValid(tuple))
   10616           0 :             elog(ERROR, "cache lookup failed for constraint %u", constrOid);
   10617         330 :         constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
   10618             : 
   10619             :         /*
   10620             :          * As explained above: don't try to clone a constraint for which we're
   10621             :          * going to clone the parent.
   10622             :          */
   10623         330 :         if (list_member_oid(clone, constrForm->conparentid))
   10624             :         {
   10625         126 :             ReleaseSysCache(tuple);
   10626         180 :             continue;
   10627             :         }
   10628             : 
   10629             :         /*
   10630             :          * Don't clone self-referencing foreign keys, which can be in the
   10631             :          * partitioned table or in the partition-to-be.
   10632             :          */
   10633         204 :         if (constrForm->conrelid == RelationGetRelid(parentRel) ||
   10634         162 :             constrForm->conrelid == RelationGetRelid(partitionRel))
   10635             :         {
   10636          54 :             ReleaseSysCache(tuple);
   10637          54 :             continue;
   10638             :         }
   10639             : 
   10640             :         /*
   10641             :          * Because we're only expanding the key space at the referenced side,
   10642             :          * we don't need to prevent any operation in the referencing table, so
   10643             :          * AccessShareLock suffices (assumes that dropping the constraint
   10644             :          * acquires AEL).
   10645             :          */
   10646         150 :         fkRel = table_open(constrForm->conrelid, AccessShareLock);
   10647             : 
   10648         150 :         indexOid = constrForm->conindid;
   10649         150 :         DeconstructFkConstraintRow(tuple,
   10650             :                                    &numfks,
   10651             :                                    conkey,
   10652             :                                    confkey,
   10653             :                                    conpfeqop,
   10654             :                                    conppeqop,
   10655             :                                    conffeqop,
   10656             :                                    &numfkdelsetcols,
   10657             :                                    confdelsetcols);
   10658             : 
   10659         300 :         for (int i = 0; i < numfks; i++)
   10660         150 :             mapped_confkey[i] = attmap->attnums[confkey[i] - 1];
   10661             : 
   10662         150 :         fkconstraint = makeNode(Constraint);
   10663         150 :         fkconstraint->contype = CONSTRAINT_FOREIGN;
   10664         150 :         fkconstraint->conname = NameStr(constrForm->conname);
   10665         150 :         fkconstraint->deferrable = constrForm->condeferrable;
   10666         150 :         fkconstraint->initdeferred = constrForm->condeferred;
   10667         150 :         fkconstraint->location = -1;
   10668         150 :         fkconstraint->pktable = NULL;
   10669             :         /* ->fk_attrs determined below */
   10670         150 :         fkconstraint->pk_attrs = NIL;
   10671         150 :         fkconstraint->fk_matchtype = constrForm->confmatchtype;
   10672         150 :         fkconstraint->fk_upd_action = constrForm->confupdtype;
   10673         150 :         fkconstraint->fk_del_action = constrForm->confdeltype;
   10674         150 :         fkconstraint->fk_del_set_cols = NIL;
   10675         150 :         fkconstraint->old_conpfeqop = NIL;
   10676         150 :         fkconstraint->old_pktable_oid = InvalidOid;
   10677         150 :         fkconstraint->skip_validation = false;
   10678         150 :         fkconstraint->initially_valid = true;
   10679             : 
   10680             :         /* set up colnames that are used to generate the constraint name */
   10681         300 :         for (int i = 0; i < numfks; i++)
   10682             :         {
   10683             :             Form_pg_attribute att;
   10684             : 
   10685         150 :             att = TupleDescAttr(RelationGetDescr(fkRel),
   10686             :                                 conkey[i] - 1);
   10687         150 :             fkconstraint->fk_attrs = lappend(fkconstraint->fk_attrs,
   10688         150 :                                              makeString(NameStr(att->attname)));
   10689             :         }
   10690             : 
   10691             :         /*
   10692             :          * Add the new foreign key constraint pointing to the new partition.
   10693             :          * Because this new partition appears in the referenced side of the
   10694             :          * constraint, we don't need to set up for Phase 3 check.
   10695             :          */
   10696         150 :         partIndexId = index_get_partition(partitionRel, indexOid);
   10697         150 :         if (!OidIsValid(partIndexId))
   10698           0 :             elog(ERROR, "index for %u not found in partition %s",
   10699             :                  indexOid, RelationGetRelationName(partitionRel));
   10700             : 
   10701             :         /*
   10702             :          * Get the "action" triggers belonging to the constraint to pass as
   10703             :          * parent OIDs for similar triggers that will be created on the
   10704             :          * partition in addFkRecurseReferenced().
   10705             :          */
   10706         150 :         GetForeignKeyActionTriggers(trigrel, constrOid,
   10707             :                                     constrForm->confrelid, constrForm->conrelid,
   10708             :                                     &deleteTriggerOid, &updateTriggerOid);
   10709             : 
   10710         150 :         addFkRecurseReferenced(NULL,
   10711             :                                fkconstraint,
   10712             :                                fkRel,
   10713             :                                partitionRel,
   10714             :                                partIndexId,
   10715             :                                constrOid,
   10716             :                                numfks,
   10717             :                                mapped_confkey,
   10718             :                                conkey,
   10719             :                                conpfeqop,
   10720             :                                conppeqop,
   10721             :                                conffeqop,
   10722             :                                numfkdelsetcols,
   10723             :                                confdelsetcols,
   10724             :                                true,
   10725             :                                deleteTriggerOid,
   10726             :                                updateTriggerOid);
   10727             : 
   10728         150 :         table_close(fkRel, NoLock);
   10729         150 :         ReleaseSysCache(tuple);
   10730             :     }
   10731             : 
   10732       10396 :     table_close(trigrel, RowExclusiveLock);
   10733       10396 : }
   10734             : 
   10735             : /*
   10736             :  * CloneFkReferencing
   10737             :  *      Subroutine for CloneForeignKeyConstraints
   10738             :  *
   10739             :  * For each FK constraint of the parent relation in the given list, find an
   10740             :  * equivalent constraint in its partition relation that can be reparented;
   10741             :  * if one cannot be found, create a new constraint in the partition as its
   10742             :  * child.
   10743             :  *
   10744             :  * If wqueue is given, it is used to set up phase-3 verification for each
   10745             :  * cloned constraint; if omitted, we assume that such verification is not
   10746             :  * needed (example: the partition is being created anew).
   10747             :  */
   10748             : static void
   10749       10396 : CloneFkReferencing(List **wqueue, Relation parentRel, Relation partRel)
   10750             : {
   10751             :     AttrMap    *attmap;
   10752             :     List       *partFKs;
   10753       10396 :     List       *clone = NIL;
   10754             :     ListCell   *cell;
   10755             :     Relation    trigrel;
   10756             : 
   10757             :     /* obtain a list of constraints that we need to clone */
   10758       11256 :     foreach(cell, RelationGetFKeyList(parentRel))
   10759             :     {
   10760         860 :         ForeignKeyCacheInfo *fk = lfirst(cell);
   10761             : 
   10762         860 :         clone = lappend_oid(clone, fk->conoid);
   10763             :     }
   10764             : 
   10765             :     /*
   10766             :      * Silently do nothing if there's nothing to do.  In particular, this
   10767             :      * avoids throwing a spurious error for foreign tables.
   10768             :      */
   10769       10396 :     if (clone == NIL)
   10770        9960 :         return;
   10771             : 
   10772         436 :     if (partRel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
   10773           0 :         ereport(ERROR,
   10774             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   10775             :                  errmsg("foreign key constraints are not supported on foreign tables")));
   10776             : 
   10777             :     /*
   10778             :      * Triggers of the foreign keys will be manipulated a bunch of times in
   10779             :      * the loop below.  To avoid repeatedly opening/closing the trigger
   10780             :      * catalog relation, we open it here and pass it to the subroutines called
   10781             :      * below.
   10782             :      */
   10783         436 :     trigrel = table_open(TriggerRelationId, RowExclusiveLock);
   10784             : 
   10785             :     /*
   10786             :      * The constraint key may differ, if the columns in the partition are
   10787             :      * different.  This map is used to convert them.
   10788             :      */
   10789         436 :     attmap = build_attrmap_by_name(RelationGetDescr(partRel),
   10790             :                                    RelationGetDescr(parentRel),
   10791             :                                    false);
   10792             : 
   10793         436 :     partFKs = copyObject(RelationGetFKeyList(partRel));
   10794             : 
   10795        1296 :     foreach(cell, clone)
   10796             :     {
   10797         860 :         Oid         parentConstrOid = lfirst_oid(cell);
   10798             :         Form_pg_constraint constrForm;
   10799             :         Relation    pkrel;
   10800             :         HeapTuple   tuple;
   10801             :         int         numfks;
   10802             :         AttrNumber  conkey[INDEX_MAX_KEYS];
   10803             :         AttrNumber  mapped_conkey[INDEX_MAX_KEYS];
   10804             :         AttrNumber  confkey[INDEX_MAX_KEYS];
   10805             :         Oid         conpfeqop[INDEX_MAX_KEYS];
   10806             :         Oid         conppeqop[INDEX_MAX_KEYS];
   10807             :         Oid         conffeqop[INDEX_MAX_KEYS];
   10808             :         int         numfkdelsetcols;
   10809             :         AttrNumber  confdelsetcols[INDEX_MAX_KEYS];
   10810             :         Constraint *fkconstraint;
   10811             :         bool        attached;
   10812             :         Oid         indexOid;
   10813             :         Oid         constrOid;
   10814             :         ObjectAddress address,
   10815             :                     referenced;
   10816             :         ListCell   *lc;
   10817             :         Oid         insertTriggerOid,
   10818             :                     updateTriggerOid;
   10819             : 
   10820         860 :         tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(parentConstrOid));
   10821         860 :         if (!HeapTupleIsValid(tuple))
   10822           0 :             elog(ERROR, "cache lookup failed for constraint %u",
   10823             :                  parentConstrOid);
   10824         860 :         constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
   10825             : 
   10826             :         /* Don't clone constraints whose parents are being cloned */
   10827         860 :         if (list_member_oid(clone, constrForm->conparentid))
   10828             :         {
   10829         400 :             ReleaseSysCache(tuple);
   10830         466 :             continue;
   10831             :         }
   10832             : 
   10833             :         /*
   10834             :          * Need to prevent concurrent deletions.  If pkrel is a partitioned
   10835             :          * relation, that means to lock all partitions.
   10836             :          */
   10837         460 :         pkrel = table_open(constrForm->confrelid, ShareRowExclusiveLock);
   10838         460 :         if (pkrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
   10839         172 :             (void) find_all_inheritors(RelationGetRelid(pkrel),
   10840             :                                        ShareRowExclusiveLock, NULL);
   10841             : 
   10842         460 :         DeconstructFkConstraintRow(tuple, &numfks, conkey, confkey,
   10843             :                                    conpfeqop, conppeqop, conffeqop,
   10844             :                                    &numfkdelsetcols, confdelsetcols);
   10845        1046 :         for (int i = 0; i < numfks; i++)
   10846         586 :             mapped_conkey[i] = attmap->attnums[conkey[i] - 1];
   10847             : 
   10848             :         /*
   10849             :          * Get the "check" triggers belonging to the constraint to pass as
   10850             :          * parent OIDs for similar triggers that will be created on the
   10851             :          * partition in addFkRecurseReferencing().  They are also passed to
   10852             :          * tryAttachPartitionForeignKey() below to simply assign as parents to
   10853             :          * the partition's existing "check" triggers, that is, if the
   10854             :          * corresponding constraints is deemed attachable to the parent
   10855             :          * constraint.
   10856             :          */
   10857         460 :         GetForeignKeyCheckTriggers(trigrel, constrForm->oid,
   10858             :                                    constrForm->confrelid, constrForm->conrelid,
   10859             :                                    &insertTriggerOid, &updateTriggerOid);
   10860             : 
   10861             :         /*
   10862             :          * Before creating a new constraint, see whether any existing FKs are
   10863             :          * fit for the purpose.  If one is, attach the parent constraint to
   10864             :          * it, and don't clone anything.  This way we avoid the expensive
   10865             :          * verification step and don't end up with a duplicate FK, and we
   10866             :          * don't need to recurse to partitions for this constraint.
   10867             :          */
   10868         460 :         attached = false;
   10869         544 :         foreach(lc, partFKs)
   10870             :         {
   10871         150 :             ForeignKeyCacheInfo *fk = lfirst_node(ForeignKeyCacheInfo, lc);
   10872             : 
   10873         150 :             if (tryAttachPartitionForeignKey(fk,
   10874             :                                              RelationGetRelid(partRel),
   10875             :                                              parentConstrOid,
   10876             :                                              numfks,
   10877             :                                              mapped_conkey,
   10878             :                                              confkey,
   10879             :                                              conpfeqop,
   10880             :                                              insertTriggerOid,
   10881             :                                              updateTriggerOid,
   10882             :                                              trigrel))
   10883             :             {
   10884          66 :                 attached = true;
   10885          66 :                 table_close(pkrel, NoLock);
   10886          66 :                 break;
   10887             :             }
   10888             :         }
   10889         460 :         if (attached)
   10890             :         {
   10891          66 :             ReleaseSysCache(tuple);
   10892          66 :             continue;
   10893             :         }
   10894             : 
   10895             :         /* No dice.  Set up to create our own constraint */
   10896         394 :         fkconstraint = makeNode(Constraint);
   10897         394 :         fkconstraint->contype = CONSTRAINT_FOREIGN;
   10898             :         /* ->conname determined below */
   10899         394 :         fkconstraint->deferrable = constrForm->condeferrable;
   10900         394 :         fkconstraint->initdeferred = constrForm->condeferred;
   10901         394 :         fkconstraint->location = -1;
   10902         394 :         fkconstraint->pktable = NULL;
   10903             :         /* ->fk_attrs determined below */
   10904         394 :         fkconstraint->pk_attrs = NIL;
   10905         394 :         fkconstraint->fk_matchtype = constrForm->confmatchtype;
   10906         394 :         fkconstraint->fk_upd_action = constrForm->confupdtype;
   10907         394 :         fkconstraint->fk_del_action = constrForm->confdeltype;
   10908         394 :         fkconstraint->fk_del_set_cols = NIL;
   10909         394 :         fkconstraint->old_conpfeqop = NIL;
   10910         394 :         fkconstraint->old_pktable_oid = InvalidOid;
   10911         394 :         fkconstraint->skip_validation = false;
   10912         394 :         fkconstraint->initially_valid = true;
   10913         866 :         for (int i = 0; i < numfks; i++)
   10914             :         {
   10915             :             Form_pg_attribute att;
   10916             : 
   10917         472 :             att = TupleDescAttr(RelationGetDescr(partRel),
   10918             :                                 mapped_conkey[i] - 1);
   10919         472 :             fkconstraint->fk_attrs = lappend(fkconstraint->fk_attrs,
   10920         472 :                                              makeString(NameStr(att->attname)));
   10921             :         }
   10922         394 :         if (ConstraintNameIsUsed(CONSTRAINT_RELATION,
   10923             :                                  RelationGetRelid(partRel),
   10924         394 :                                  NameStr(constrForm->conname)))
   10925           6 :             fkconstraint->conname =
   10926           6 :                 ChooseConstraintName(RelationGetRelationName(partRel),
   10927           6 :                                      ChooseForeignKeyConstraintNameAddition(fkconstraint->fk_attrs),
   10928             :                                      "fkey",
   10929           6 :                                      RelationGetNamespace(partRel), NIL);
   10930             :         else
   10931         388 :             fkconstraint->conname = pstrdup(NameStr(constrForm->conname));
   10932             : 
   10933         394 :         indexOid = constrForm->conindid;
   10934             :         constrOid =
   10935         394 :             CreateConstraintEntry(fkconstraint->conname,
   10936             :                                   constrForm->connamespace,
   10937             :                                   CONSTRAINT_FOREIGN,
   10938         394 :                                   fkconstraint->deferrable,
   10939         394 :                                   fkconstraint->initdeferred,
   10940         394 :                                   constrForm->convalidated,
   10941             :                                   parentConstrOid,
   10942             :                                   RelationGetRelid(partRel),
   10943             :                                   mapped_conkey,
   10944             :                                   numfks,
   10945             :                                   numfks,
   10946             :                                   InvalidOid,   /* not a domain constraint */
   10947             :                                   indexOid,
   10948             :                                   constrForm->confrelid, /* same foreign rel */
   10949             :                                   confkey,
   10950             :                                   conpfeqop,
   10951             :                                   conppeqop,
   10952             :                                   conffeqop,
   10953             :                                   numfks,
   10954         394 :                                   fkconstraint->fk_upd_action,
   10955         394 :                                   fkconstraint->fk_del_action,
   10956             :                                   confdelsetcols,
   10957             :                                   numfkdelsetcols,
   10958         394 :                                   fkconstraint->fk_matchtype,
   10959             :                                   NULL,
   10960             :                                   NULL,
   10961             :                                   NULL,
   10962             :                                   false,    /* islocal */
   10963             :                                   1,    /* inhcount */
   10964             :                                   false,    /* conNoInherit */
   10965             :                                   true);
   10966             : 
   10967             :         /* Set up partition dependencies for the new constraint */
   10968         394 :         ObjectAddressSet(address, ConstraintRelationId, constrOid);
   10969         394 :         ObjectAddressSet(referenced, ConstraintRelationId, parentConstrOid);
   10970         394 :         recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_PRI);
   10971         394 :         ObjectAddressSet(referenced, RelationRelationId,
   10972             :                          RelationGetRelid(partRel));
   10973         394 :         recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_SEC);
   10974             : 
   10975             :         /* Done with the cloned constraint's tuple */
   10976         394 :         ReleaseSysCache(tuple);
   10977             : 
   10978             :         /* Make all this visible before recursing */
   10979         394 :         CommandCounterIncrement();
   10980             : 
   10981         394 :         addFkRecurseReferencing(wqueue,
   10982             :                                 fkconstraint,
   10983             :                                 partRel,
   10984             :                                 pkrel,
   10985             :                                 indexOid,
   10986             :                                 constrOid,
   10987             :                                 numfks,
   10988             :                                 confkey,
   10989             :                                 mapped_conkey,
   10990             :                                 conpfeqop,
   10991             :                                 conppeqop,
   10992             :                                 conffeqop,
   10993             :                                 numfkdelsetcols,
   10994             :                                 confdelsetcols,
   10995             :                                 false,  /* no old check exists */
   10996             :                                 AccessExclusiveLock,
   10997             :                                 insertTriggerOid,
   10998             :                                 updateTriggerOid);
   10999         394 :         table_close(pkrel, NoLock);
   11000             :     }
   11001             : 
   11002         436 :     table_close(trigrel, RowExclusiveLock);
   11003             : }
   11004             : 
   11005             : /*
   11006             :  * When the parent of a partition receives [the referencing side of] a foreign
   11007             :  * key, we must propagate that foreign key to the partition.  However, the
   11008             :  * partition might already have an equivalent foreign key; this routine
   11009             :  * compares the given ForeignKeyCacheInfo (in the partition) to the FK defined
   11010             :  * by the other parameters.  If they are equivalent, create the link between
   11011             :  * the two constraints and return true.
   11012             :  *
   11013             :  * If the given FK does not match the one defined by rest of the params,
   11014             :  * return false.
   11015             :  */
   11016             : static bool
   11017         180 : tryAttachPartitionForeignKey(ForeignKeyCacheInfo *fk,
   11018             :                              Oid partRelid,
   11019             :                              Oid parentConstrOid,
   11020             :                              int numfks,
   11021             :                              AttrNumber *mapped_conkey,
   11022             :                              AttrNumber *confkey,
   11023             :                              Oid *conpfeqop,
   11024             :                              Oid parentInsTrigger,
   11025             :                              Oid parentUpdTrigger,
   11026             :                              Relation trigrel)
   11027             : {
   11028             :     HeapTuple   parentConstrTup;
   11029             :     Form_pg_constraint parentConstr;
   11030             :     HeapTuple   partcontup;
   11031             :     Form_pg_constraint partConstr;
   11032             :     ScanKeyData key;
   11033             :     SysScanDesc scan;
   11034             :     HeapTuple   trigtup;
   11035             :     Oid         insertTriggerOid,
   11036             :                 updateTriggerOid;
   11037             : 
   11038         180 :     parentConstrTup = SearchSysCache1(CONSTROID,
   11039             :                                       ObjectIdGetDatum(parentConstrOid));
   11040         180 :     if (!HeapTupleIsValid(parentConstrTup))
   11041           0 :         elog(ERROR, "cache lookup failed for constraint %u", parentConstrOid);
   11042         180 :     parentConstr = (Form_pg_constraint) GETSTRUCT(parentConstrTup);
   11043             : 
   11044             :     /*
   11045             :      * Do some quick & easy initial checks.  If any of these fail, we cannot
   11046             :      * use this constraint.
   11047             :      */
   11048         180 :     if (fk->confrelid != parentConstr->confrelid || fk->nkeys != numfks)
   11049             :     {
   11050           0 :         ReleaseSysCache(parentConstrTup);
   11051           0 :         return false;
   11052             :     }
   11053         510 :     for (int i = 0; i < numfks; i++)
   11054             :     {
   11055         330 :         if (fk->conkey[i] != mapped_conkey[i] ||
   11056         330 :             fk->confkey[i] != confkey[i] ||
   11057         330 :             fk->conpfeqop[i] != conpfeqop[i])
   11058             :         {
   11059           0 :             ReleaseSysCache(parentConstrTup);
   11060           0 :             return false;
   11061             :         }
   11062             :     }
   11063             : 
   11064             :     /*
   11065             :      * Looks good so far; do some more extensive checks.  Presumably the check
   11066             :      * for 'convalidated' could be dropped, since we don't really care about
   11067             :      * that, but let's be careful for now.
   11068             :      */
   11069         180 :     partcontup = SearchSysCache1(CONSTROID,
   11070             :                                  ObjectIdGetDatum(fk->conoid));
   11071         180 :     if (!HeapTupleIsValid(partcontup))
   11072           0 :         elog(ERROR, "cache lookup failed for constraint %u", fk->conoid);
   11073         180 :     partConstr = (Form_pg_constraint) GETSTRUCT(partcontup);
   11074         180 :     if (OidIsValid(partConstr->conparentid) ||
   11075         156 :         !partConstr->convalidated ||
   11076         156 :         partConstr->condeferrable != parentConstr->condeferrable ||
   11077         128 :         partConstr->condeferred != parentConstr->condeferred ||
   11078         128 :         partConstr->confupdtype != parentConstr->confupdtype ||
   11079          92 :         partConstr->confdeltype != parentConstr->confdeltype ||
   11080          92 :         partConstr->confmatchtype != parentConstr->confmatchtype)
   11081             :     {
   11082         102 :         ReleaseSysCache(parentConstrTup);
   11083         102 :         ReleaseSysCache(partcontup);
   11084         102 :         return false;
   11085             :     }
   11086             : 
   11087          78 :     ReleaseSysCache(partcontup);
   11088          78 :     ReleaseSysCache(parentConstrTup);
   11089             : 
   11090             :     /*
   11091             :      * Looks good!  Attach this constraint.  The action triggers in the new
   11092             :      * partition become redundant -- the parent table already has equivalent
   11093             :      * ones, and those will be able to reach the partition.  Remove the ones
   11094             :      * in the partition.  We identify them because they have our constraint
   11095             :      * OID, as well as being on the referenced rel.
   11096             :      */
   11097          78 :     ScanKeyInit(&key,
   11098             :                 Anum_pg_trigger_tgconstraint,
   11099             :                 BTEqualStrategyNumber, F_OIDEQ,
   11100             :                 ObjectIdGetDatum(fk->conoid));
   11101          78 :     scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
   11102             :                               NULL, 1, &key);
   11103         390 :     while ((trigtup = systable_getnext(scan)) != NULL)
   11104             :     {
   11105         312 :         Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
   11106             :         ObjectAddress trigger;
   11107             : 
   11108         312 :         if (trgform->tgconstrrelid != fk->conrelid)
   11109         156 :             continue;
   11110         156 :         if (trgform->tgrelid != fk->confrelid)
   11111           0 :             continue;
   11112             : 
   11113             :         /*
   11114             :          * The constraint is originally set up to contain this trigger as an
   11115             :          * implementation object, so there's a dependency record that links
   11116             :          * the two; however, since the trigger is no longer needed, we remove
   11117             :          * the dependency link in order to be able to drop the trigger while
   11118             :          * keeping the constraint intact.
   11119             :          */
   11120         156 :         deleteDependencyRecordsFor(TriggerRelationId,
   11121             :                                    trgform->oid,
   11122             :                                    false);
   11123             :         /* make dependency deletion visible to performDeletion */
   11124         156 :         CommandCounterIncrement();
   11125         156 :         ObjectAddressSet(trigger, TriggerRelationId,
   11126             :                          trgform->oid);
   11127         156 :         performDeletion(&trigger, DROP_RESTRICT, 0);
   11128             :         /* make trigger drop visible, in case the loop iterates */
   11129         156 :         CommandCounterIncrement();
   11130             :     }
   11131             : 
   11132          78 :     systable_endscan(scan);
   11133             : 
   11134          78 :     ConstraintSetParentConstraint(fk->conoid, parentConstrOid, partRelid);
   11135             : 
   11136             :     /*
   11137             :      * Like the constraint, attach partition's "check" triggers to the
   11138             :      * corresponding parent triggers.
   11139             :      */
   11140          78 :     GetForeignKeyCheckTriggers(trigrel,
   11141             :                                fk->conoid, fk->confrelid, fk->conrelid,
   11142             :                                &insertTriggerOid, &updateTriggerOid);
   11143             :     Assert(OidIsValid(insertTriggerOid) && OidIsValid(parentInsTrigger));
   11144          78 :     TriggerSetParentTrigger(trigrel, insertTriggerOid, parentInsTrigger,
   11145             :                             partRelid);
   11146             :     Assert(OidIsValid(updateTriggerOid) && OidIsValid(parentUpdTrigger));
   11147          78 :     TriggerSetParentTrigger(trigrel, updateTriggerOid, parentUpdTrigger,
   11148             :                             partRelid);
   11149             : 
   11150          78 :     CommandCounterIncrement();
   11151          78 :     return true;
   11152             : }
   11153             : 
   11154             : /*
   11155             :  * GetForeignKeyActionTriggers
   11156             :  *      Returns delete and update "action" triggers of the given relation
   11157             :  *      belonging to the given constraint
   11158             :  */
   11159             : static void
   11160         150 : GetForeignKeyActionTriggers(Relation trigrel,
   11161             :                             Oid conoid, Oid confrelid, Oid conrelid,
   11162             :                             Oid *deleteTriggerOid,
   11163             :                             Oid *updateTriggerOid)
   11164             : {
   11165             :     ScanKeyData key;
   11166             :     SysScanDesc scan;
   11167             :     HeapTuple   trigtup;
   11168             : 
   11169         150 :     *deleteTriggerOid = *updateTriggerOid = InvalidOid;
   11170         150 :     ScanKeyInit(&key,
   11171             :                 Anum_pg_trigger_tgconstraint,
   11172             :                 BTEqualStrategyNumber, F_OIDEQ,
   11173             :                 ObjectIdGetDatum(conoid));
   11174             : 
   11175         150 :     scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
   11176             :                               NULL, 1, &key);
   11177         306 :     while ((trigtup = systable_getnext(scan)) != NULL)
   11178             :     {
   11179         306 :         Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
   11180             : 
   11181         306 :         if (trgform->tgconstrrelid != conrelid)
   11182           6 :             continue;
   11183         300 :         if (trgform->tgrelid != confrelid)
   11184           0 :             continue;
   11185             :         /* Only ever look at "action" triggers on the PK side. */
   11186         300 :         if (RI_FKey_trigger_type(trgform->tgfoid) != RI_TRIGGER_PK)
   11187           0 :             continue;
   11188         300 :         if (TRIGGER_FOR_DELETE(trgform->tgtype))
   11189             :         {
   11190             :             Assert(*deleteTriggerOid == InvalidOid);
   11191         150 :             *deleteTriggerOid = trgform->oid;
   11192             :         }
   11193         150 :         else if (TRIGGER_FOR_UPDATE(trgform->tgtype))
   11194             :         {
   11195             :             Assert(*updateTriggerOid == InvalidOid);
   11196         150 :             *updateTriggerOid = trgform->oid;
   11197             :         }
   11198             : #ifndef USE_ASSERT_CHECKING
   11199             :         /* In an assert-enabled build, continue looking to find duplicates */
   11200         300 :         if (OidIsValid(*deleteTriggerOid) && OidIsValid(*updateTriggerOid))
   11201         150 :             break;
   11202             : #endif
   11203             :     }
   11204             : 
   11205         150 :     if (!OidIsValid(*deleteTriggerOid))
   11206           0 :         elog(ERROR, "could not find ON DELETE action trigger of foreign key constraint %u",
   11207             :              conoid);
   11208         150 :     if (!OidIsValid(*updateTriggerOid))
   11209           0 :         elog(ERROR, "could not find ON UPDATE action trigger of foreign key constraint %u",
   11210             :              conoid);
   11211             : 
   11212         150 :     systable_endscan(scan);
   11213         150 : }
   11214             : 
   11215             : /*
   11216             :  * GetForeignKeyCheckTriggers
   11217             :  *      Returns insert and update "check" triggers of the given relation
   11218             :  *      belonging to the given constraint
   11219             :  */
   11220             : static void
   11221         604 : GetForeignKeyCheckTriggers(Relation trigrel,
   11222             :                            Oid conoid, Oid confrelid, Oid conrelid,
   11223             :                            Oid *insertTriggerOid,
   11224             :                            Oid *updateTriggerOid)
   11225             : {
   11226             :     ScanKeyData key;
   11227             :     SysScanDesc scan;
   11228             :     HeapTuple   trigtup;
   11229             : 
   11230         604 :     *insertTriggerOid = *updateTriggerOid = InvalidOid;
   11231         604 :     ScanKeyInit(&key,
   11232             :                 Anum_pg_trigger_tgconstraint,
   11233             :                 BTEqualStrategyNumber, F_OIDEQ,
   11234             :                 ObjectIdGetDatum(conoid));
   11235             : 
   11236         604 :     scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
   11237             :                               NULL, 1, &key);
   11238        2014 :     while ((trigtup = systable_getnext(scan)) != NULL)
   11239             :     {
   11240        2014 :         Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
   11241             : 
   11242        2014 :         if (trgform->tgconstrrelid != confrelid)
   11243         722 :             continue;
   11244        1292 :         if (trgform->tgrelid != conrelid)
   11245           0 :             continue;
   11246             :         /* Only ever look at "check" triggers on the FK side. */
   11247        1292 :         if (RI_FKey_trigger_type(trgform->tgfoid) != RI_TRIGGER_FK)
   11248          84 :             continue;
   11249        1208 :         if (TRIGGER_FOR_INSERT(trgform->tgtype))
   11250             :         {
   11251             :             Assert(*insertTriggerOid == InvalidOid);
   11252         604 :             *insertTriggerOid = trgform->oid;
   11253             :         }
   11254         604 :         else if (TRIGGER_FOR_UPDATE(trgform->tgtype))
   11255             :         {
   11256             :             Assert(*updateTriggerOid == InvalidOid);
   11257         604 :             *updateTriggerOid = trgform->oid;
   11258             :         }
   11259             : #ifndef USE_ASSERT_CHECKING
   11260             :         /* In an assert-enabled build, continue looking to find duplicates. */
   11261        1208 :         if (OidIsValid(*insertTriggerOid) && OidIsValid(*updateTriggerOid))
   11262         604 :             break;
   11263             : #endif
   11264             :     }
   11265             : 
   11266         604 :     if (!OidIsValid(*insertTriggerOid))
   11267           0 :         elog(ERROR, "could not find ON INSERT check triggers of foreign key constraint %u",
   11268             :              conoid);
   11269         604 :     if (!OidIsValid(*updateTriggerOid))
   11270           0 :         elog(ERROR, "could not find ON UPDATE check triggers of foreign key constraint %u",
   11271             :              conoid);
   11272             : 
   11273         604 :     systable_endscan(scan);
   11274         604 : }
   11275             : 
   11276             : /*
   11277             :  * ALTER TABLE ALTER CONSTRAINT
   11278             :  *
   11279             :  * Update the attributes of a constraint.
   11280             :  *
   11281             :  * Currently only works for Foreign Key constraints.
   11282             :  *
   11283             :  * If the constraint is modified, returns its address; otherwise, return
   11284             :  * InvalidObjectAddress.
   11285             :  */
   11286             : static ObjectAddress
   11287          66 : ATExecAlterConstraint(Relation rel, AlterTableCmd *cmd, bool recurse,
   11288             :                       bool recursing, LOCKMODE lockmode)
   11289             : {
   11290             :     Constraint *cmdcon;
   11291             :     Relation    conrel;
   11292             :     Relation    tgrel;
   11293             :     SysScanDesc scan;
   11294             :     ScanKeyData skey[3];
   11295             :     HeapTuple   contuple;
   11296             :     Form_pg_constraint currcon;
   11297             :     ObjectAddress address;
   11298          66 :     List       *otherrelids = NIL;
   11299             :     ListCell   *lc;
   11300             : 
   11301          66 :     cmdcon = castNode(Constraint, cmd->def);
   11302             : 
   11303          66 :     conrel = table_open(ConstraintRelationId, RowExclusiveLock);
   11304          66 :     tgrel = table_open(TriggerRelationId, RowExclusiveLock);
   11305             : 
   11306             :     /*
   11307             :      * Find and check the target constraint
   11308             :      */
   11309          66 :     ScanKeyInit(&skey[0],
   11310             :                 Anum_pg_constraint_conrelid,
   11311             :                 BTEqualStrategyNumber, F_OIDEQ,
   11312             :                 ObjectIdGetDatum(RelationGetRelid(rel)));
   11313          66 :     ScanKeyInit(&skey[1],
   11314             :                 Anum_pg_constraint_contypid,
   11315             :                 BTEqualStrategyNumber, F_OIDEQ,
   11316             :                 ObjectIdGetDatum(InvalidOid));
   11317          66 :     ScanKeyInit(&skey[2],
   11318             :                 Anum_pg_constraint_conname,
   11319             :                 BTEqualStrategyNumber, F_NAMEEQ,
   11320          66 :                 CStringGetDatum(cmdcon->conname));
   11321          66 :     scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
   11322             :                               true, NULL, 3, skey);
   11323             : 
   11324             :     /* There can be at most one matching row */
   11325          66 :     if (!HeapTupleIsValid(contuple = systable_getnext(scan)))
   11326           0 :         ereport(ERROR,
   11327             :                 (errcode(ERRCODE_UNDEFINED_OBJECT),
   11328             :                  errmsg("constraint \"%s\" of relation \"%s\" does not exist",
   11329             :                         cmdcon->conname, RelationGetRelationName(rel))));
   11330             : 
   11331          66 :     currcon = (Form_pg_constraint) GETSTRUCT(contuple);
   11332          66 :     if (currcon->contype != CONSTRAINT_FOREIGN)
   11333           0 :         ereport(ERROR,
   11334             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   11335             :                  errmsg("constraint \"%s\" of relation \"%s\" is not a foreign key constraint",
   11336             :                         cmdcon->conname, RelationGetRelationName(rel))));
   11337             : 
   11338             :     /*
   11339             :      * If it's not the topmost constraint, raise an error.
   11340             :      *
   11341             :      * Altering a non-topmost constraint leaves some triggers untouched, since
   11342             :      * they are not directly connected to this constraint; also, pg_dump would
   11343             :      * ignore the deferrability status of the individual constraint, since it
   11344             :      * only dumps topmost constraints.  Avoid these problems by refusing this
   11345             :      * operation and telling the user to alter the parent constraint instead.
   11346             :      */
   11347          66 :     if (OidIsValid(currcon->conparentid))
   11348             :     {
   11349             :         HeapTuple   tp;
   11350          12 :         Oid         parent = currcon->conparentid;
   11351          12 :         char       *ancestorname = NULL;
   11352          12 :         char       *ancestortable = NULL;
   11353             : 
   11354             :         /* Loop to find the topmost constraint */
   11355          24 :         while (HeapTupleIsValid(tp = SearchSysCache1(CONSTROID, ObjectIdGetDatum(parent))))
   11356             :         {
   11357          24 :             Form_pg_constraint contup = (Form_pg_constraint) GETSTRUCT(tp);
   11358             : 
   11359             :             /* If no parent, this is the constraint we want */
   11360          24 :             if (!OidIsValid(contup->conparentid))
   11361             :             {
   11362          12 :                 ancestorname = pstrdup(NameStr(contup->conname));
   11363          12 :                 ancestortable = get_rel_name(contup->conrelid);
   11364          12 :                 ReleaseSysCache(tp);
   11365          12 :                 break;
   11366             :             }
   11367             : 
   11368          12 :             parent = contup->conparentid;
   11369          12 :             ReleaseSysCache(tp);
   11370             :         }
   11371             : 
   11372          12 :         ereport(ERROR,
   11373             :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
   11374             :                  errmsg("cannot alter constraint \"%s\" on relation \"%s\"",
   11375             :                         cmdcon->conname, RelationGetRelationName(rel)),
   11376             :                  ancestorname && ancestortable ?
   11377             :                  errdetail("Constraint \"%s\" is derived from constraint \"%s\" of relation \"%s\".",
   11378             :                            cmdcon->conname, ancestorname, ancestortable) : 0,
   11379             :                  errhint("You may alter the constraint it derives from instead.")));
   11380             :     }
   11381             : 
   11382             :     /*
   11383             :      * Do the actual catalog work.  We can skip changing if already in the
   11384             :      * desired state, but not if a partitioned table: partitions need to be
   11385             :      * processed regardless, in case they had the constraint locally changed.
   11386             :      */
   11387          54 :     address = InvalidObjectAddress;
   11388          54 :     if (currcon->condeferrable != cmdcon->deferrable ||
   11389           6 :         currcon->condeferred != cmdcon->initdeferred ||
   11390           0 :         rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
   11391             :     {
   11392          54 :         if (ATExecAlterConstrRecurse(cmdcon, conrel, tgrel, rel, contuple,
   11393             :                                      &otherrelids, lockmode))
   11394          54 :             ObjectAddressSet(address, ConstraintRelationId, currcon->oid);
   11395             :     }
   11396             : 
   11397             :     /*
   11398             :      * ATExecAlterConstrRecurse already invalidated relcache for the relations
   11399             :      * having the constraint itself; here we also invalidate for relations
   11400             :      * that have any triggers that are part of the constraint.
   11401             :      */
   11402         138 :     foreach(lc, otherrelids)
   11403          84 :         CacheInvalidateRelcacheByRelid(lfirst_oid(lc));
   11404             : 
   11405          54 :     systable_endscan(scan);
   11406             : 
   11407          54 :     table_close(tgrel, RowExclusiveLock);
   11408          54 :     table_close(conrel, RowExclusiveLock);
   11409             : 
   11410          54 :     return address;
   11411             : }
   11412             : 
   11413             : /*
   11414             :  * Recursive subroutine of ATExecAlterConstraint.  Returns true if the
   11415             :  * constraint is altered.
   11416             :  *
   11417             :  * *otherrelids is appended OIDs of relations containing affected triggers.
   11418             :  *
   11419             :  * Note that we must recurse even when the values are correct, in case
   11420             :  * indirect descendants have had their constraints altered locally.
   11421             :  * (This could be avoided if we forbade altering constraints in partitions
   11422             :  * but existing releases don't do that.)
   11423             :  */
   11424             : static bool
   11425         120 : ATExecAlterConstrRecurse(Constraint *cmdcon, Relation conrel, Relation tgrel,
   11426             :                          Relation rel, HeapTuple contuple, List **otherrelids,
   11427             :                          LOCKMODE lockmode)
   11428             : {
   11429             :     Form_pg_constraint currcon;
   11430             :     Oid         conoid;
   11431             :     Oid         refrelid;
   11432         120 :     bool        changed = false;
   11433             : 
   11434             :     /* since this function recurses, it could be driven to stack overflow */
   11435         120 :     check_stack_depth();
   11436             : 
   11437         120 :     currcon = (Form_pg_constraint) GETSTRUCT(contuple);
   11438         120 :     conoid = currcon->oid;
   11439         120 :     refrelid = currcon->confrelid;
   11440             : 
   11441             :     /*
   11442             :      * Update pg_constraint with the flags from cmdcon.
   11443             :      *
   11444             :      * If called to modify a constraint that's already in the desired state,
   11445             :      * silently do nothing.
   11446             :      */
   11447         120 :     if (currcon->condeferrable != cmdcon->deferrable ||
   11448           6 :         currcon->condeferred != cmdcon->initdeferred)
   11449             :     {
   11450             :         HeapTuple   copyTuple;
   11451             :         Form_pg_constraint copy_con;
   11452             :         HeapTuple   tgtuple;
   11453             :         ScanKeyData tgkey;
   11454             :         SysScanDesc tgscan;
   11455             : 
   11456         120 :         copyTuple = heap_copytuple(contuple);
   11457         120 :         copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
   11458         120 :         copy_con->condeferrable = cmdcon->deferrable;
   11459         120 :         copy_con->condeferred = cmdcon->initdeferred;
   11460         120 :         CatalogTupleUpdate(conrel, &copyTuple->t_self, copyTuple);
   11461             : 
   11462         120 :         InvokeObjectPostAlterHook(ConstraintRelationId,
   11463             :                                   conoid, 0);
   11464             : 
   11465         120 :         heap_freetuple(copyTuple);
   11466         120 :         changed = true;
   11467             : 
   11468             :         /* Make new constraint flags visible to others */
   11469         120 :         CacheInvalidateRelcache(rel);
   11470             : 
   11471             :         /*
   11472             :          * Now we need to update the multiple entries in pg_trigger that
   11473             :          * implement the constraint.
   11474             :          */
   11475         120 :         ScanKeyInit(&tgkey,
   11476             :                     Anum_pg_trigger_tgconstraint,
   11477             :                     BTEqualStrategyNumber, F_OIDEQ,
   11478             :                     ObjectIdGetDatum(conoid));
   11479         120 :         tgscan = systable_beginscan(tgrel, TriggerConstraintIndexId, true,
   11480             :                                     NULL, 1, &tgkey);
   11481         468 :         while (HeapTupleIsValid(tgtuple = systable_getnext(tgscan)))
   11482             :         {
   11483         348 :             Form_pg_trigger tgform = (Form_pg_trigger) GETSTRUCT(tgtuple);
   11484             :             Form_pg_trigger copy_tg;
   11485             :             HeapTuple   tgCopyTuple;
   11486             : 
   11487             :             /*
   11488             :              * Remember OIDs of other relation(s) involved in FK constraint.
   11489             :              * (Note: it's likely that we could skip forcing a relcache inval
   11490             :              * for other rels that don't have a trigger whose properties
   11491             :              * change, but let's be conservative.)
   11492             :              */
   11493         348 :             if (tgform->tgrelid != RelationGetRelid(rel))
   11494         168 :                 *otherrelids = list_append_unique_oid(*otherrelids,
   11495             :                                                       tgform->tgrelid);
   11496             : 
   11497             :             /*
   11498             :              * Update deferrability of RI_FKey_noaction_del,
   11499             :              * RI_FKey_noaction_upd, RI_FKey_check_ins and RI_FKey_check_upd
   11500             :              * triggers, but not others; see createForeignKeyActionTriggers
   11501             :              * and CreateFKCheckTrigger.
   11502             :              */
   11503         348 :             if (tgform->tgfoid != F_RI_FKEY_NOACTION_DEL &&
   11504         282 :                 tgform->tgfoid != F_RI_FKEY_NOACTION_UPD &&
   11505         198 :                 tgform->tgfoid != F_RI_FKEY_CHECK_INS &&
   11506         108 :                 tgform->tgfoid != F_RI_FKEY_CHECK_UPD)
   11507          18 :                 continue;
   11508             : 
   11509         330 :             tgCopyTuple = heap_copytuple(tgtuple);
   11510         330 :             copy_tg = (Form_pg_trigger) GETSTRUCT(tgCopyTuple);
   11511             : 
   11512         330 :             copy_tg->tgdeferrable = cmdcon->deferrable;
   11513         330 :             copy_tg->tginitdeferred = cmdcon->initdeferred;
   11514         330 :             CatalogTupleUpdate(tgrel, &tgCopyTuple->t_self, tgCopyTuple);
   11515             : 
   11516         330 :             InvokeObjectPostAlterHook(TriggerRelationId, tgform->oid, 0);
   11517             : 
   11518         330 :             heap_freetuple(tgCopyTuple);
   11519             :         }
   11520             : 
   11521         120 :         systable_endscan(tgscan);
   11522             :     }
   11523             : 
   11524             :     /*
   11525             :      * If the table at either end of the constraint is partitioned, we need to
   11526             :      * recurse and handle every constraint that is a child of this one.
   11527             :      *
   11528             :      * (This assumes that the recurse flag is forcibly set for partitioned
   11529             :      * tables, and not set for legacy inheritance, though we don't check for
   11530             :      * that here.)
   11531             :      */
   11532         216 :     if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
   11533          96 :         get_rel_relkind(refrelid) == RELKIND_PARTITIONED_TABLE)
   11534             :     {
   11535             :         ScanKeyData pkey;
   11536             :         SysScanDesc pscan;
   11537             :         HeapTuple   childtup;
   11538             : 
   11539          42 :         ScanKeyInit(&pkey,
   11540             :                     Anum_pg_constraint_conparentid,
   11541             :                     BTEqualStrategyNumber, F_OIDEQ,
   11542             :                     ObjectIdGetDatum(conoid));
   11543             : 
   11544          42 :         pscan = systable_beginscan(conrel, ConstraintParentIndexId,
   11545             :                                    true, NULL, 1, &pkey);
   11546             : 
   11547         108 :         while (HeapTupleIsValid(childtup = systable_getnext(pscan)))
   11548             :         {
   11549          66 :             Form_pg_constraint childcon = (Form_pg_constraint) GETSTRUCT(childtup);
   11550             :             Relation    childrel;
   11551             : 
   11552          66 :             childrel = table_open(childcon->conrelid, lockmode);
   11553          66 :             ATExecAlterConstrRecurse(cmdcon, conrel, tgrel, childrel, childtup,
   11554             :                                      otherrelids, lockmode);
   11555          66 :             table_close(childrel, NoLock);
   11556             :         }
   11557             : 
   11558          42 :         systable_endscan(pscan);
   11559             :     }
   11560             : 
   11561         120 :     return changed;
   11562             : }
   11563             : 
   11564             : /*
   11565             :  * ALTER TABLE VALIDATE CONSTRAINT
   11566             :  *
   11567             :  * XXX The reason we handle recursion here rather than at Phase 1 is because
   11568             :  * there's no good way to skip recursing when handling foreign keys: there is
   11569             :  * no need to lock children in that case, yet we wouldn't be able to avoid
   11570             :  * doing so at that level.
   11571             :  *
   11572             :  * Return value is the address of the validated constraint.  If the constraint
   11573             :  * was already validated, InvalidObjectAddress is returned.
   11574             :  */
   11575             : static ObjectAddress
   11576         436 : ATExecValidateConstraint(List **wqueue, Relation rel, char *constrName,
   11577             :                          bool recurse, bool recursing, LOCKMODE lockmode)
   11578             : {
   11579             :     Relation    conrel;
   11580             :     SysScanDesc scan;
   11581             :     ScanKeyData skey[3];
   11582             :     HeapTuple   tuple;
   11583             :     Form_pg_constraint con;
   11584             :     ObjectAddress address;
   11585             : 
   11586         436 :     conrel = table_open(ConstraintRelationId, RowExclusiveLock);
   11587             : 
   11588             :     /*
   11589             :      * Find and check the target constraint
   11590             :      */
   11591         436 :     ScanKeyInit(&skey[0],
   11592             :                 Anum_pg_constraint_conrelid,
   11593             :                 BTEqualStrategyNumber, F_OIDEQ,
   11594             :                 ObjectIdGetDatum(RelationGetRelid(rel)));
   11595         436 :     ScanKeyInit(&skey[1],
   11596             :                 Anum_pg_constraint_contypid,
   11597             :                 BTEqualStrategyNumber, F_OIDEQ,
   11598             :                 ObjectIdGetDatum(InvalidOid));
   11599         436 :     ScanKeyInit(&skey[2],
   11600             :                 Anum_pg_constraint_conname,
   11601             :                 BTEqualStrategyNumber, F_NAMEEQ,
   11602             :                 CStringGetDatum(constrName));
   11603         436 :     scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
   11604             :                               true, NULL, 3, skey);
   11605             : 
   11606             :     /* There can be at most one matching row */
   11607         436 :     if (!HeapTupleIsValid(tuple = systable_getnext(scan)))
   11608           0 :         ereport(ERROR,
   11609             :                 (errcode(ERRCODE_UNDEFINED_OBJECT),
   11610             :                  errmsg("constraint \"%s\" of relation \"%s\" does not exist",
   11611             :                         constrName, RelationGetRelationName(rel))));
   11612             : 
   11613         436 :     con = (Form_pg_constraint) GETSTRUCT(tuple);
   11614         436 :     if (con->contype != CONSTRAINT_FOREIGN &&
   11615         132 :         con->contype != CONSTRAINT_CHECK)
   11616           0 :         ereport(ERROR,
   11617             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   11618             :                  errmsg("constraint \"%s\" of relation \"%s\" is not a foreign key or check constraint",
   11619             :                         constrName, RelationGetRelationName(rel))));
   11620             : 
   11621         436 :     if (!con->convalidated)
   11622             :     {
   11623             :         AlteredTableInfo *tab;
   11624             :         HeapTuple   copyTuple;
   11625             :         Form_pg_constraint copy_con;
   11626             : 
   11627         418 :         if (con->contype == CONSTRAINT_FOREIGN)
   11628             :         {
   11629             :             NewConstraint *newcon;
   11630             :             Constraint *fkconstraint;
   11631             : 
   11632             :             /* Queue validation for phase 3 */
   11633         298 :             fkconstraint = makeNode(Constraint);
   11634             :             /* for now this is all we need */
   11635         298 :             fkconstraint->conname = constrName;
   11636             : 
   11637         298 :             newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
   11638         298 :             newcon->name = constrName;
   11639         298 :             newcon->contype = CONSTR_FOREIGN;
   11640         298 :             newcon->refrelid = con->confrelid;
   11641         298 :             newcon->refindid = con->conindid;
   11642         298 :             newcon->conid = con->oid;
   11643         298 :             newcon->qual = (Node *) fkconstraint;
   11644             : 
   11645             :             /* Find or create work queue entry for this table */
   11646         298 :             tab = ATGetQueueEntry(wqueue, rel);
   11647         298 :             tab->constraints = lappend(tab->constraints, newcon);
   11648             : 
   11649             :             /*
   11650             :              * We disallow creating invalid foreign keys to or from
   11651             :              * partitioned tables, so ignoring the recursion bit is okay.
   11652             :              */
   11653             :         }
   11654         120 :         else if (con->contype == CONSTRAINT_CHECK)
   11655             :         {
   11656         120 :             List       *children = NIL;
   11657             :             ListCell   *child;
   11658             :             NewConstraint *newcon;
   11659             :             Datum       val;
   11660             :             char       *conbin;
   11661             : 
   11662             :             /*
   11663             :              * If we're recursing, the parent has already done this, so skip
   11664             :              * it.  Also, if the constraint is a NO INHERIT constraint, we
   11665             :              * shouldn't try to look for it in the children.
   11666             :              */
   11667         120 :             if (!recursing && !con->connoinherit)
   11668          66 :                 children = find_all_inheritors(RelationGetRelid(rel),
   11669             :                                                lockmode, NULL);
   11670             : 
   11671             :             /*
   11672             :              * For CHECK constraints, we must ensure that we only mark the
   11673             :              * constraint as validated on the parent if it's already validated
   11674             :              * on the children.
   11675             :              *
   11676             :              * We recurse before validating on the parent, to reduce risk of
   11677             :              * deadlocks.
   11678             :              */
   11679         234 :             foreach(child, children)
   11680             :             {
   11681         114 :                 Oid         childoid = lfirst_oid(child);
   11682             :                 Relation    childrel;
   11683             : 
   11684         114 :                 if (childoid == RelationGetRelid(rel))
   11685          66 :                     continue;
   11686             : 
   11687             :                 /*
   11688             :                  * If we are told not to recurse, there had better not be any
   11689             :                  * child tables, because we can't mark the constraint on the
   11690             :                  * parent valid unless it is valid for all child tables.
   11691             :                  */
   11692          48 :                 if (!recurse)
   11693           0 :                     ereport(ERROR,
   11694             :                             (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
   11695             :                              errmsg("constraint must be validated on child tables too")));
   11696             : 
   11697             :                 /* find_all_inheritors already got lock */
   11698          48 :                 childrel = table_open(childoid, NoLock);
   11699             : 
   11700          48 :                 ATExecValidateConstraint(wqueue, childrel, constrName, false,
   11701             :                                          true, lockmode);
   11702          48 :                 table_close(childrel, NoLock);
   11703             :             }
   11704             : 
   11705             :             /* Queue validation for phase 3 */
   11706         120 :             newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
   11707         120 :             newcon->name = constrName;
   11708         120 :             newcon->contype = CONSTR_CHECK;
   11709         120 :             newcon->refrelid = InvalidOid;
   11710         120 :             newcon->refindid = InvalidOid;
   11711         120 :             newcon->conid = con->oid;
   11712             : 
   11713         120 :             val = SysCacheGetAttrNotNull(CONSTROID, tuple,
   11714             :                                          Anum_pg_constraint_conbin);
   11715         120 :             conbin = TextDatumGetCString(val);
   11716         120 :             newcon->qual = (Node *) stringToNode(conbin);
   11717             : 
   11718             :             /* Find or create work queue entry for this table */
   11719         120 :             tab = ATGetQueueEntry(wqueue, rel);
   11720         120 :             tab->constraints = lappend(tab->constraints, newcon);
   11721             : 
   11722             :             /*
   11723             :              * Invalidate relcache so that others see the new validated
   11724             :              * constraint.
   11725             :              */
   11726         120 :             CacheInvalidateRelcache(rel);
   11727             :         }
   11728             : 
   11729             :         /*
   11730             :          * Now update the catalog, while we have the door open.
   11731             :          */
   11732         418 :         copyTuple = heap_copytuple(tuple);
   11733         418 :         copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
   11734         418 :         copy_con->convalidated = true;
   11735         418 :         CatalogTupleUpdate(conrel, &copyTuple->t_self, copyTuple);
   11736             : 
   11737         418 :         InvokeObjectPostAlterHook(ConstraintRelationId, con->oid, 0);
   11738             : 
   11739         418 :         heap_freetuple(copyTuple);
   11740             : 
   11741         418 :         ObjectAddressSet(address, ConstraintRelationId, con->oid);
   11742             :     }
   11743             :     else
   11744          18 :         address = InvalidObjectAddress; /* already validated */
   11745             : 
   11746         436 :     systable_endscan(scan);
   11747             : 
   11748         436 :     table_close(conrel, RowExclusiveLock);
   11749             : 
   11750         436 :     return address;
   11751             : }
   11752             : 
   11753             : 
   11754             : /*
   11755             :  * transformColumnNameList - transform list of column names
   11756             :  *
   11757             :  * Lookup each name and return its attnum and, optionally, type OID
   11758             :  *
   11759             :  * Note: the name of this function suggests that it's general-purpose,
   11760             :  * but actually it's only used to look up names appearing in foreign-key
   11761             :  * clauses.  The error messages would need work to use it in other cases,
   11762             :  * and perhaps the validity checks as well.
   11763             :  */
   11764             : static int
   11765        5296 : transformColumnNameList(Oid relId, List *colList,
   11766             :                         int16 *attnums, Oid *atttypids)
   11767             : {
   11768             :     ListCell   *l;
   11769             :     int         attnum;
   11770             : 
   11771        5296 :     attnum = 0;
   11772        9300 :     foreach(l, colList)
   11773             :     {
   11774        4070 :         char       *attname = strVal(lfirst(l));
   11775             :         HeapTuple   atttuple;
   11776             :         Form_pg_attribute attform;
   11777             : 
   11778        4070 :         atttuple = SearchSysCacheAttName(relId, attname);
   11779        4070 :         if (!HeapTupleIsValid(atttuple))
   11780          54 :             ereport(ERROR,
   11781             :                     (errcode(ERRCODE_UNDEFINED_COLUMN),
   11782             :                      errmsg("column \"%s\" referenced in foreign key constraint does not exist",
   11783             :                             attname)));
   11784        4016 :         attform = (Form_pg_attribute) GETSTRUCT(atttuple);
   11785        4016 :         if (attform->attnum < 0)
   11786          12 :             ereport(ERROR,
   11787             :                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   11788             :                      errmsg("system columns cannot be used in foreign keys")));
   11789        4004 :         if (attnum >= INDEX_MAX_KEYS)
   11790           0 :             ereport(ERROR,
   11791             :                     (errcode(ERRCODE_TOO_MANY_COLUMNS),
   11792             :                      errmsg("cannot have more than %d keys in a foreign key",
   11793             :                             INDEX_MAX_KEYS)));
   11794        4004 :         attnums[attnum] = attform->attnum;
   11795        4004 :         if (atttypids != NULL)
   11796        3974 :             atttypids[attnum] = attform->atttypid;
   11797        4004 :         ReleaseSysCache(atttuple);
   11798        4004 :         attnum++;
   11799             :     }
   11800             : 
   11801        5230 :     return attnum;
   11802             : }
   11803             : 
   11804             : /*
   11805             :  * transformFkeyGetPrimaryKey -
   11806             :  *
   11807             :  *  Look up the names, attnums, and types of the primary key attributes
   11808             :  *  for the pkrel.  Also return the index OID and index opclasses of the
   11809             :  *  index supporting the primary key.
   11810             :  *
   11811             :  *  All parameters except pkrel are output parameters.  Also, the function
   11812             :  *  return value is the number of attributes in the primary key.
   11813             :  *
   11814             :  *  Used when the column list in the REFERENCES specification is omitted.
   11815             :  */
   11816             : static int
   11817         944 : transformFkeyGetPrimaryKey(Relation pkrel, Oid *indexOid,
   11818             :                            List **attnamelist,
   11819             :                            int16 *attnums, Oid *atttypids,
   11820             :                            Oid *opclasses)
   11821             : {
   11822             :     List       *indexoidlist;
   11823             :     ListCell   *indexoidscan;
   11824         944 :     HeapTuple   indexTuple = NULL;
   11825         944 :     Form_pg_index indexStruct = NULL;
   11826             :     Datum       indclassDatum;
   11827             :     oidvector  *indclass;
   11828             :     int         i;
   11829             : 
   11830             :     /*
   11831             :      * Get the list of index OIDs for the table from the relcache, and look up
   11832             :      * each one in the pg_index syscache until we find one marked primary key
   11833             :      * (hopefully there isn't more than one such).  Insist it's valid, too.
   11834             :      */
   11835         944 :     *indexOid = InvalidOid;
   11836             : 
   11837         944 :     indexoidlist = RelationGetIndexList(pkrel);
   11838             : 
   11839         950 :     foreach(indexoidscan, indexoidlist)
   11840             :     {
   11841         950 :         Oid         indexoid = lfirst_oid(indexoidscan);
   11842             : 
   11843         950 :         indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexoid));
   11844         950 :         if (!HeapTupleIsValid(indexTuple))
   11845           0 :             elog(ERROR, "cache lookup failed for index %u", indexoid);
   11846         950 :         indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
   11847         950 :         if (indexStruct->indisprimary && indexStruct->indisvalid)
   11848             :         {
   11849             :             /*
   11850             :              * Refuse to use a deferrable primary key.  This is per SQL spec,
   11851             :              * and there would be a lot of interesting semantic problems if we
   11852             :              * tried to allow it.
   11853             :              */
   11854         944 :             if (!indexStruct->indimmediate)
   11855           0 :                 ereport(ERROR,
   11856             :                         (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
   11857             :                          errmsg("cannot use a deferrable primary key for referenced table \"%s\"",
   11858             :                                 RelationGetRelationName(pkrel))));
   11859             : 
   11860         944 :             *indexOid = indexoid;
   11861         944 :             break;
   11862             :         }
   11863           6 :         ReleaseSysCache(indexTuple);
   11864             :     }
   11865             : 
   11866         944 :     list_free(indexoidlist);
   11867             : 
   11868             :     /*
   11869             :      * Check that we found it
   11870             :      */
   11871         944 :     if (!OidIsValid(*indexOid))
   11872           0 :         ereport(ERROR,
   11873             :                 (errcode(ERRCODE_UNDEFINED_OBJECT),
   11874             :                  errmsg("there is no primary key for referenced table \"%s\"",
   11875             :                         RelationGetRelationName(pkrel))));
   11876             : 
   11877             :     /* Must get indclass the hard way */
   11878         944 :     indclassDatum = SysCacheGetAttrNotNull(INDEXRELID, indexTuple,
   11879             :                                            Anum_pg_index_indclass);
   11880         944 :     indclass = (oidvector *) DatumGetPointer(indclassDatum);
   11881             : 
   11882             :     /*
   11883             :      * Now build the list of PK attributes from the indkey definition (we
   11884             :      * assume a primary key cannot have expressional elements)
   11885             :      */
   11886         944 :     *attnamelist = NIL;
   11887        2120 :     for (i = 0; i < indexStruct->indnkeyatts; i++)
   11888             :     {
   11889        1176 :         int         pkattno = indexStruct->indkey.values[i];
   11890             : 
   11891        1176 :         attnums[i] = pkattno;
   11892        1176 :         atttypids[i] = attnumTypeId(pkrel, pkattno);
   11893        1176 :         opclasses[i] = indclass->values[i];
   11894        1176 :         *attnamelist = lappend(*attnamelist,
   11895        1176 :                                makeString(pstrdup(NameStr(*attnumAttName(pkrel, pkattno)))));
   11896             :     }
   11897             : 
   11898         944 :     ReleaseSysCache(indexTuple);
   11899             : 
   11900         944 :     return i;
   11901             : }
   11902             : 
   11903             : /*
   11904             :  * transformFkeyCheckAttrs -
   11905             :  *
   11906             :  *  Validate that the 'attnums' columns in the 'pkrel' relation are valid to
   11907             :  *  reference as part of a foreign key constraint.
   11908             :  *
   11909             :  *  Returns the OID of the unique index supporting the constraint and
   11910             :  *  populates the caller-provided 'opclasses' array with the opclasses
   11911             :  *  associated with the index columns.
   11912             :  *
   11913             :  *  Raises an ERROR on validation failure.
   11914             :  */
   11915             : static Oid
   11916        1088 : transformFkeyCheckAttrs(Relation pkrel,
   11917             :                         int numattrs, int16 *attnums,
   11918             :                         Oid *opclasses)
   11919             : {
   11920        1088 :     Oid         indexoid = InvalidOid;
   11921        1088 :     bool        found = false;
   11922        1088 :     bool        found_deferrable = false;
   11923             :     List       *indexoidlist;
   11924             :     ListCell   *indexoidscan;
   11925             :     int         i,
   11926             :                 j;
   11927             : 
   11928             :     /*
   11929             :      * Reject duplicate appearances of columns in the referenced-columns list.
   11930             :      * Such a case is forbidden by the SQL standard, and even if we thought it
   11931             :      * useful to allow it, there would be ambiguity about how to match the
   11932             :      * list to unique indexes (in particular, it'd be unclear which index
   11933             :      * opclass goes with which FK column).
   11934             :      */
   11935        2460 :     for (i = 0; i < numattrs; i++)
   11936             :     {
   11937        1698 :         for (j = i + 1; j < numattrs; j++)
   11938             :         {
   11939         326 :             if (attnums[i] == attnums[j])
   11940           0 :                 ereport(ERROR,
   11941             :                         (errcode(ERRCODE_INVALID_FOREIGN_KEY),
   11942             :                          errmsg("foreign key referenced-columns list must not contain duplicates")));
   11943             :         }
   11944             :     }
   11945             : 
   11946             :     /*
   11947             :      * Get the list of index OIDs for the table from the relcache, and look up
   11948             :      * each one in the pg_index syscache, and match unique indexes to the list
   11949             :      * of attnums we are given.
   11950             :      */
   11951        1088 :     indexoidlist = RelationGetIndexList(pkrel);
   11952             : 
   11953        1268 :     foreach(indexoidscan, indexoidlist)
   11954             :     {
   11955             :         HeapTuple   indexTuple;
   11956             :         Form_pg_index indexStruct;
   11957             : 
   11958        1256 :         indexoid = lfirst_oid(indexoidscan);
   11959        1256 :         indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexoid));
   11960        1256 :         if (!HeapTupleIsValid(indexTuple))
   11961           0 :             elog(ERROR, "cache lookup failed for index %u", indexoid);
   11962        1256 :         indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
   11963             : 
   11964             :         /*
   11965             :          * Must have the right number of columns; must be unique and not a
   11966             :          * partial index; forget it if there are any expressions, too. Invalid
   11967             :          * indexes are out as well.
   11968             :          */
   11969        1256 :         if (indexStruct->indnkeyatts == numattrs &&
   11970        1148 :             indexStruct->indisunique &&
   11971        2268 :             indexStruct->indisvalid &&
   11972        2268 :             heap_attisnull(indexTuple, Anum_pg_index_indpred, NULL) &&
   11973        1134 :             heap_attisnull(indexTuple, Anum_pg_index_indexprs, NULL))
   11974             :         {
   11975             :             Datum       indclassDatum;
   11976             :             oidvector  *indclass;
   11977             : 
   11978             :             /* Must get indclass the hard way */
   11979        1134 :             indclassDatum = SysCacheGetAttrNotNull(INDEXRELID, indexTuple,
   11980             :                                                    Anum_pg_index_indclass);
   11981        1134 :             indclass = (oidvector *) DatumGetPointer(indclassDatum);
   11982             : 
   11983             :             /*
   11984             :              * The given attnum list may match the index columns in any order.
   11985             :              * Check for a match, and extract the appropriate opclasses while
   11986             :              * we're at it.
   11987             :              *
   11988             :              * We know that attnums[] is duplicate-free per the test at the
   11989             :              * start of this function, and we checked above that the number of
   11990             :              * index columns agrees, so if we find a match for each attnums[]
   11991             :              * entry then we must have a one-to-one match in some order.
   11992             :              */
   11993        2494 :             for (i = 0; i < numattrs; i++)
   11994             :             {
   11995        1418 :                 found = false;
   11996        1802 :                 for (j = 0; j < numattrs; j++)
   11997             :                 {
   11998        1744 :                     if (attnums[i] == indexStruct->indkey.values[j])
   11999             :                     {
   12000        1360 :                         opclasses[i] = indclass->values[j];
   12001        1360 :                         found = true;
   12002        1360 :                         break;
   12003             :                     }
   12004             :                 }
   12005        1418 :                 if (!found)
   12006          58 :                     break;
   12007             :             }
   12008             : 
   12009             :             /*
   12010             :              * Refuse to use a deferrable unique/primary key.  This is per SQL
   12011             :              * spec, and there would be a lot of interesting semantic problems
   12012             :              * if we tried to allow it.
   12013             :              */
   12014        1134 :             if (found && !indexStruct->indimmediate)
   12015             :             {
   12016             :                 /*
   12017             :                  * Remember that we found an otherwise matching index, so that
   12018             :                  * we can generate a more appropriate error message.
   12019             :                  */
   12020           0 :                 found_deferrable = true;
   12021           0 :                 found = false;
   12022             :             }
   12023             :         }
   12024        1256 :         ReleaseSysCache(indexTuple);
   12025        1256 :         if (found)
   12026        1076 :             break;
   12027             :     }
   12028             : 
   12029        1088 :     if (!found)
   12030             :     {
   12031          12 :         if (found_deferrable)
   12032           0 :             ereport(ERROR,
   12033             :                     (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
   12034             :                      errmsg("cannot use a deferrable unique constraint for referenced table \"%s\"",
   12035             :                             RelationGetRelationName(pkrel))));
   12036             :         else
   12037          12 :             ereport(ERROR,
   12038             :                     (errcode(ERRCODE_INVALID_FOREIGN_KEY),
   12039             :                      errmsg("there is no unique constraint matching given keys for referenced table \"%s\"",
   12040             :                             RelationGetRelationName(pkrel))));
   12041             :     }
   12042             : 
   12043        1076 :     list_free(indexoidlist);
   12044             : 
   12045        1076 :     return indexoid;
   12046             : }
   12047             : 
   12048             : /*
   12049             :  * findFkeyCast -
   12050             :  *
   12051             :  *  Wrapper around find_coercion_pathway() for ATAddForeignKeyConstraint().
   12052             :  *  Caller has equal regard for binary coercibility and for an exact match.
   12053             : */
   12054             : static CoercionPathType
   12055          12 : findFkeyCast(Oid targetTypeId, Oid sourceTypeId, Oid *funcid)
   12056             : {
   12057             :     CoercionPathType ret;
   12058             : 
   12059          12 :     if (targetTypeId == sourceTypeId)
   12060             :     {
   12061          12 :         ret = COERCION_PATH_RELABELTYPE;
   12062          12 :         *funcid = InvalidOid;
   12063             :     }
   12064             :     else
   12065             :     {
   12066           0 :         ret = find_coercion_pathway(targetTypeId, sourceTypeId,
   12067             :                                     COERCION_IMPLICIT, funcid);
   12068           0 :         if (ret == COERCION_PATH_NONE)
   12069             :             /* A previously-relied-upon cast is now gone. */
   12070           0 :             elog(ERROR, "could not find cast from %u to %u",
   12071             :                  sourceTypeId, targetTypeId);
   12072             :     }
   12073             : 
   12074          12 :     return ret;
   12075             : }
   12076             : 
   12077             : /*
   12078             :  * Permissions checks on the referenced table for ADD FOREIGN KEY
   12079             :  *
   12080             :  * Note: we have already checked that the user owns the referencing table,
   12081             :  * else we'd have failed much earlier; no additional checks are needed for it.
   12082             :  */
   12083             : static void
   12084        2020 : checkFkeyPermissions(Relation rel, int16 *attnums, int natts)
   12085             : {
   12086        2020 :     Oid         roleid = GetUserId();
   12087             :     AclResult   aclresult;
   12088             :     int         i;
   12089             : 
   12090             :     /* Okay if we have relation-level REFERENCES permission */
   12091        2020 :     aclresult = pg_class_aclcheck(RelationGetRelid(rel), roleid,
   12092             :                                   ACL_REFERENCES);
   12093        2020 :     if (aclresult == ACLCHECK_OK)
   12094        2020 :         return;
   12095             :     /* Else we must have REFERENCES on each column */
   12096           0 :     for (i = 0; i < natts; i++)
   12097             :     {
   12098           0 :         aclresult = pg_attribute_aclcheck(RelationGetRelid(rel), attnums[i],
   12099             :                                           roleid, ACL_REFERENCES);
   12100           0 :         if (aclresult != ACLCHECK_OK)
   12101           0 :             aclcheck_error(aclresult, get_relkind_objtype(rel->rd_rel->relkind),
   12102           0 :                            RelationGetRelationName(rel));
   12103             :     }
   12104             : }
   12105             : 
   12106             : /*
   12107             :  * Scan the existing rows in a table to verify they meet a proposed FK
   12108             :  * constraint.
   12109             :  *
   12110             :  * Caller must have opened and locked both relations appropriately.
   12111             :  */
   12112             : static void
   12113         944 : validateForeignKeyConstraint(char *conname,
   12114             :                              Relation rel,
   12115             :                              Relation pkrel,
   12116             :                              Oid pkindOid,
   12117             :                              Oid constraintOid)
   12118             : {
   12119             :     TupleTableSlot *slot;
   12120             :     TableScanDesc scan;
   12121         944 :     Trigger     trig = {0};
   12122             :     Snapshot    snapshot;
   12123             :     MemoryContext oldcxt;
   12124             :     MemoryContext perTupCxt;
   12125             : 
   12126         944 :     ereport(DEBUG1,
   12127             :             (errmsg_internal("validating foreign key constraint \"%s\"", conname)));
   12128             : 
   12129             :     /*
   12130             :      * Build a trigger call structure; we'll need it either way.
   12131             :      */
   12132         944 :     trig.tgoid = InvalidOid;
   12133         944 :     trig.tgname = conname;
   12134         944 :     trig.tgenabled = TRIGGER_FIRES_ON_ORIGIN;
   12135         944 :     trig.tgisinternal = true;
   12136         944 :     trig.tgconstrrelid = RelationGetRelid(pkrel);
   12137         944 :     trig.tgconstrindid = pkindOid;
   12138         944 :     trig.tgconstraint = constraintOid;
   12139         944 :     trig.tgdeferrable = false;
   12140         944 :     trig.tginitdeferred = false;
   12141             :     /* we needn't fill in remaining fields */
   12142             : 
   12143             :     /*
   12144             :      * See if we can do it with a single LEFT JOIN query.  A false result
   12145             :      * indicates we must proceed with the fire-the-trigger method.
   12146             :      */
   12147         944 :     if (RI_Initial_Check(&trig, rel, pkrel))
   12148         870 :         return;
   12149             : 
   12150             :     /*
   12151             :      * Scan through each tuple, calling RI_FKey_check_ins (insert trigger) as
   12152             :      * if that tuple had just been inserted.  If any of those fail, it should
   12153             :      * ereport(ERROR) and that's that.
   12154             :      */
   12155          12 :     snapshot = RegisterSnapshot(GetLatestSnapshot());
   12156          12 :     slot = table_slot_create(rel, NULL);
   12157          12 :     scan = table_beginscan(rel, snapshot, 0, NULL);
   12158             : 
   12159          12 :     perTupCxt = AllocSetContextCreate(CurrentMemoryContext,
   12160             :                                       "validateForeignKeyConstraint",
   12161             :                                       ALLOCSET_SMALL_SIZES);
   12162          12 :     oldcxt = MemoryContextSwitchTo(perTupCxt);
   12163             : 
   12164          72 :     while (table_scan_getnextslot(scan, ForwardScanDirection, slot))
   12165             :     {
   12166          66 :         LOCAL_FCINFO(fcinfo, 0);
   12167          66 :         TriggerData trigdata = {0};
   12168             : 
   12169          66 :         CHECK_FOR_INTERRUPTS();
   12170             : 
   12171             :         /*
   12172             :          * Make a call to the trigger function
   12173             :          *
   12174             :          * No parameters are passed, but we do set a context
   12175             :          */
   12176         330 :         MemSet(fcinfo, 0, SizeForFunctionCallInfo(0));
   12177             : 
   12178             :         /*
   12179             :          * We assume RI_FKey_check_ins won't look at flinfo...
   12180             :          */
   12181          66 :         trigdata.type = T_TriggerData;
   12182          66 :         trigdata.tg_event = TRIGGER_EVENT_INSERT | TRIGGER_EVENT_ROW;
   12183          66 :         trigdata.tg_relation = rel;
   12184          66 :         trigdata.tg_trigtuple = ExecFetchSlotHeapTuple(slot, false, NULL);
   12185          66 :         trigdata.tg_trigslot = slot;
   12186          66 :         trigdata.tg_trigger = &trig;
   12187             : 
   12188          66 :         fcinfo->context = (Node *) &trigdata;
   12189             : 
   12190          66 :         RI_FKey_check_ins(fcinfo);
   12191             : 
   12192          60 :         MemoryContextReset(perTupCxt);
   12193             :     }
   12194             : 
   12195           6 :     MemoryContextSwitchTo(oldcxt);
   12196           6 :     MemoryContextDelete(perTupCxt);
   12197           6 :     table_endscan(scan);
   12198           6 :     UnregisterSnapshot(snapshot);
   12199           6 :     ExecDropSingleTupleTableSlot(slot);
   12200             : }
   12201             : 
   12202             : /*
   12203             :  * CreateFKCheckTrigger
   12204             :  *      Creates the insert (on_insert=true) or update "check" trigger that
   12205             :  *      implements a given foreign key
   12206             :  *
   12207             :  * Returns the OID of the so created trigger.
   12208             :  */
   12209             : static Oid
   12210        5088 : CreateFKCheckTrigger(Oid myRelOid, Oid refRelOid, Constraint *fkconstraint,
   12211             :                      Oid constraintOid, Oid indexOid, Oid parentTrigOid,
   12212             :                      bool on_insert)
   12213             : {
   12214             :     ObjectAddress trigAddress;
   12215             :     CreateTrigStmt *fk_trigger;
   12216             : 
   12217             :     /*
   12218             :      * Note: for a self-referential FK (referencing and referenced tables are
   12219             :      * the same), it is important that the ON UPDATE action fires before the
   12220             :      * CHECK action, since both triggers will fire on the same row during an
   12221             :      * UPDATE event; otherwise the CHECK trigger will be checking a non-final
   12222             :      * state of the row.  Triggers fire in name order, so we ensure this by
   12223             :      * using names like "RI_ConstraintTrigger_a_NNNN" for the action triggers
   12224             :      * and "RI_ConstraintTrigger_c_NNNN" for the check triggers.
   12225             :      */
   12226        5088 :     fk_trigger = makeNode(CreateTrigStmt);
   12227        5088 :     fk_trigger->replace = false;
   12228        5088 :     fk_trigger->isconstraint = true;
   12229        5088 :     fk_trigger->trigname = "RI_ConstraintTrigger_c";
   12230        5088 :     fk_trigger->relation = NULL;
   12231             : 
   12232             :     /* Either ON INSERT or ON UPDATE */
   12233        5088 :     if (on_insert)
   12234             :     {
   12235        2544 :         fk_trigger->funcname = SystemFuncName("RI_FKey_check_ins");
   12236        2544 :         fk_trigger->events = TRIGGER_TYPE_INSERT;
   12237             :     }
   12238             :     else
   12239             :     {
   12240        2544 :         fk_trigger->funcname = SystemFuncName("RI_FKey_check_upd");
   12241        2544 :         fk_trigger->events = TRIGGER_TYPE_UPDATE;
   12242             :     }
   12243             : 
   12244        5088 :     fk_trigger->args = NIL;
   12245        5088 :     fk_trigger->row = true;
   12246        5088 :     fk_trigger->timing = TRIGGER_TYPE_AFTER;
   12247        5088 :     fk_trigger->columns = NIL;
   12248        5088 :     fk_trigger->whenClause = NULL;
   12249        5088 :     fk_trigger->transitionRels = NIL;
   12250        5088 :     fk_trigger->deferrable = fkconstraint->deferrable;
   12251        5088 :     fk_trigger->initdeferred = fkconstraint->initdeferred;
   12252        5088 :     fk_trigger->constrrel = NULL;
   12253             : 
   12254        5088 :     trigAddress = CreateTrigger(fk_trigger, NULL, myRelOid, refRelOid,
   12255             :                                 constraintOid, indexOid, InvalidOid,
   12256             :                                 parentTrigOid, NULL, true, false);
   12257             : 
   12258             :     /* Make changes-so-far visible */
   12259        5088 :     CommandCounterIncrement();
   12260             : 
   12261        5088 :     return trigAddress.objectId;
   12262             : }
   12263             : 
   12264             : /*
   12265             :  * createForeignKeyActionTriggers
   12266             :  *      Create the referenced-side "action" triggers that implement a foreign
   12267             :  *      key.
   12268             :  *
   12269             :  * Returns the OIDs of the so created triggers in *deleteTrigOid and
   12270             :  * *updateTrigOid.
   12271             :  */
   12272             : static void
   12273        2560 : createForeignKeyActionTriggers(Relation rel, Oid refRelOid, Constraint *fkconstraint,
   12274             :                                Oid constraintOid, Oid indexOid,
   12275             :                                Oid parentDelTrigger, Oid parentUpdTrigger,
   12276             :                                Oid *deleteTrigOid, Oid *updateTrigOid)
   12277             : {
   12278             :     CreateTrigStmt *fk_trigger;
   12279             :     ObjectAddress trigAddress;
   12280             : 
   12281             :     /*
   12282             :      * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
   12283             :      * DELETE action on the referenced table.
   12284             :      */
   12285        2560 :     fk_trigger = makeNode(CreateTrigStmt);
   12286        2560 :     fk_trigger->replace = false;
   12287        2560 :     fk_trigger->isconstraint = true;
   12288        2560 :     fk_trigger->trigname = "RI_ConstraintTrigger_a";
   12289        2560 :     fk_trigger->relation = NULL;
   12290        2560 :     fk_trigger->args = NIL;
   12291        2560 :     fk_trigger->row = true;
   12292        2560 :     fk_trigger->timing = TRIGGER_TYPE_AFTER;
   12293        2560 :     fk_trigger->events = TRIGGER_TYPE_DELETE;
   12294        2560 :     fk_trigger->columns = NIL;
   12295        2560 :     fk_trigger->whenClause = NULL;
   12296        2560 :     fk_trigger->transitionRels = NIL;
   12297        2560 :     fk_trigger->constrrel = NULL;
   12298        2560 :     switch (fkconstraint->fk_del_action)
   12299             :     {
   12300        1932 :         case FKCONSTR_ACTION_NOACTION:
   12301        1932 :             fk_trigger->deferrable = fkconstraint->deferrable;
   12302        1932 :             fk_trigger->initdeferred = fkconstraint->initdeferred;
   12303        1932 :             fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_del");
   12304        1932 :             break;
   12305          30 :         case FKCONSTR_ACTION_RESTRICT:
   12306          30 :             fk_trigger->deferrable = false;
   12307          30 :             fk_trigger->initdeferred = false;
   12308          30 :             fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_del");
   12309          30 :             break;
   12310         440 :         case FKCONSTR_ACTION_CASCADE:
   12311         440 :             fk_trigger->deferrable = false;
   12312         440 :             fk_trigger->initdeferred = false;
   12313         440 :             fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_del");
   12314         440 :             break;
   12315          98 :         case FKCONSTR_ACTION_SETNULL:
   12316          98 :             fk_trigger->deferrable = false;
   12317          98 :             fk_trigger->initdeferred = false;
   12318          98 :             fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_del");
   12319          98 :             break;
   12320          60 :         case FKCONSTR_ACTION_SETDEFAULT:
   12321          60 :             fk_trigger->deferrable = false;
   12322          60 :             fk_trigger->initdeferred = false;
   12323          60 :             fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_del");
   12324          60 :             break;
   12325           0 :         default:
   12326           0 :             elog(ERROR, "unrecognized FK action type: %d",
   12327             :                  (int) fkconstraint->fk_del_action);
   12328             :             break;
   12329             :     }
   12330             : 
   12331        2560 :     trigAddress = CreateTrigger(fk_trigger, NULL, refRelOid,
   12332             :                                 RelationGetRelid(rel),
   12333             :                                 constraintOid, indexOid, InvalidOid,
   12334             :                                 parentDelTrigger, NULL, true, false);
   12335        2560 :     if (deleteTrigOid)
   12336        2494 :         *deleteTrigOid = trigAddress.objectId;
   12337             : 
   12338             :     /* Make changes-so-far visible */
   12339        2560 :     CommandCounterIncrement();
   12340             : 
   12341             :     /*
   12342             :      * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
   12343             :      * UPDATE action on the referenced table.
   12344             :      */
   12345        2560 :     fk_trigger = makeNode(CreateTrigStmt);
   12346        2560 :     fk_trigger->replace = false;
   12347        2560 :     fk_trigger->isconstraint = true;
   12348        2560 :     fk_trigger->trigname = "RI_ConstraintTrigger_a";
   12349        2560 :     fk_trigger->relation = NULL;
   12350        2560 :     fk_trigger->args = NIL;
   12351        2560 :     fk_trigger->row = true;
   12352        2560 :     fk_trigger->timing = TRIGGER_TYPE_AFTER;
   12353        2560 :     fk_trigger->events = TRIGGER_TYPE_UPDATE;
   12354        2560 :     fk_trigger->columns = NIL;
   12355        2560 :     fk_trigger->whenClause = NULL;
   12356        2560 :     fk_trigger->transitionRels = NIL;
   12357        2560 :     fk_trigger->constrrel = NULL;
   12358        2560 :     switch (fkconstraint->fk_upd_action)
   12359             :     {
   12360        2138 :         case FKCONSTR_ACTION_NOACTION:
   12361        2138 :             fk_trigger->deferrable = fkconstraint->deferrable;
   12362        2138 :             fk_trigger->initdeferred = fkconstraint->initdeferred;
   12363        2138 :             fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_upd");
   12364        2138 :             break;
   12365          30 :         case FKCONSTR_ACTION_RESTRICT:
   12366          30 :             fk_trigger->deferrable = false;
   12367          30 :             fk_trigger->initdeferred = false;
   12368          30 :             fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_upd");
   12369          30 :             break;
   12370         288 :         case FKCONSTR_ACTION_CASCADE:
   12371         288 :             fk_trigger->deferrable = false;
   12372         288 :             fk_trigger->initdeferred = false;
   12373         288 :             fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_upd");
   12374         288 :             break;
   12375          62 :         case FKCONSTR_ACTION_SETNULL:
   12376          62 :             fk_trigger->deferrable = false;
   12377          62 :             fk_trigger->initdeferred = false;
   12378          62 :             fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_upd");
   12379          62 :             break;
   12380          42 :         case FKCONSTR_ACTION_SETDEFAULT:
   12381          42 :             fk_trigger->deferrable = false;
   12382          42 :             fk_trigger->initdeferred = false;
   12383          42 :             fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_upd");
   12384          42 :             break;
   12385           0 :         default:
   12386           0 :             elog(ERROR, "unrecognized FK action type: %d",
   12387             :                  (int) fkconstraint->fk_upd_action);
   12388             :             break;
   12389             :     }
   12390             : 
   12391        2560 :     trigAddress = CreateTrigger(fk_trigger, NULL, refRelOid,
   12392             :                                 RelationGetRelid(rel),
   12393             :                                 constraintOid, indexOid, InvalidOid,
   12394             :                                 parentUpdTrigger, NULL, true, false);
   12395        2560 :     if (updateTrigOid)
   12396        2494 :         *updateTrigOid = trigAddress.objectId;
   12397        2560 : }
   12398             : 
   12399             : /*
   12400             :  * createForeignKeyCheckTriggers
   12401             :  *      Create the referencing-side "check" triggers that implement a foreign
   12402             :  *      key.
   12403             :  *
   12404             :  * Returns the OIDs of the so created triggers in *insertTrigOid and
   12405             :  * *updateTrigOid.
   12406             :  */
   12407             : static void
   12408        2544 : createForeignKeyCheckTriggers(Oid myRelOid, Oid refRelOid,
   12409             :                               Constraint *fkconstraint, Oid constraintOid,
   12410             :                               Oid indexOid,
   12411             :                               Oid parentInsTrigger, Oid parentUpdTrigger,
   12412             :                               Oid *insertTrigOid, Oid *updateTrigOid)
   12413             : {
   12414        2544 :     *insertTrigOid = CreateFKCheckTrigger(myRelOid, refRelOid, fkconstraint,
   12415             :                                           constraintOid, indexOid,
   12416             :                                           parentInsTrigger, true);
   12417        2544 :     *updateTrigOid = CreateFKCheckTrigger(myRelOid, refRelOid, fkconstraint,
   12418             :                                           constraintOid, indexOid,
   12419             :                                           parentUpdTrigger, false);
   12420        2544 : }
   12421             : 
   12422             : /*
   12423             :  * ALTER TABLE DROP CONSTRAINT
   12424             :  *
   12425             :  * Like DROP COLUMN, we can't use the normal ALTER TABLE recursion mechanism.
   12426             :  */
   12427             : static void
   12428         698 : ATExecDropConstraint(Relation rel, const char *constrName,
   12429             :                      DropBehavior behavior,
   12430             :                      bool recurse, bool recursing,
   12431             :                      bool missing_ok, LOCKMODE lockmode)
   12432             : {
   12433             :     List       *children;
   12434             :     Relation    conrel;
   12435             :     Form_pg_constraint con;
   12436             :     SysScanDesc scan;
   12437             :     ScanKeyData skey[3];
   12438             :     HeapTuple   tuple;
   12439         698 :     bool        found = false;
   12440         698 :     bool        is_no_inherit_constraint = false;
   12441             :     char        contype;
   12442             : 
   12443             :     /* At top level, permission check was done in ATPrepCmd, else do it */
   12444         698 :     if (recursing)
   12445         140 :         ATSimplePermissions(AT_DropConstraint, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
   12446             : 
   12447         698 :     conrel = table_open(ConstraintRelationId, RowExclusiveLock);
   12448             : 
   12449             :     /*
   12450             :      * Find and drop the target constraint
   12451             :      */
   12452         698 :     ScanKeyInit(&skey[0],
   12453             :                 Anum_pg_constraint_conrelid,
   12454             :                 BTEqualStrategyNumber, F_OIDEQ,
   12455             :                 ObjectIdGetDatum(RelationGetRelid(rel)));
   12456         698 :     ScanKeyInit(&skey[1],
   12457             :                 Anum_pg_constraint_contypid,
   12458             :                 BTEqualStrategyNumber, F_OIDEQ,
   12459             :                 ObjectIdGetDatum(InvalidOid));
   12460         698 :     ScanKeyInit(&skey[2],
   12461             :                 Anum_pg_constraint_conname,
   12462             :                 BTEqualStrategyNumber, F_NAMEEQ,
   12463             :                 CStringGetDatum(constrName));
   12464         698 :     scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
   12465             :                               true, NULL, 3, skey);
   12466             : 
   12467             :     /* There can be at most one matching row */
   12468         698 :     if (HeapTupleIsValid(tuple = systable_getnext(scan)))
   12469             :     {
   12470             :         ObjectAddress conobj;
   12471             : 
   12472         668 :         con = (Form_pg_constraint) GETSTRUCT(tuple);
   12473             : 
   12474             :         /* Don't drop inherited constraints */
   12475         668 :         if (con->coninhcount > 0 && !recursing)
   12476         120 :             ereport(ERROR,
   12477             :                     (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
   12478             :                      errmsg("cannot drop inherited constraint \"%s\" of relation \"%s\"",
   12479             :                             constrName, RelationGetRelationName(rel))));
   12480             : 
   12481         548 :         is_no_inherit_constraint = con->connoinherit;
   12482         548 :         contype = con->contype;
   12483             : 
   12484             :         /*
   12485             :          * If it's a foreign-key constraint, we'd better lock the referenced
   12486             :          * table and check that that's not in use, just as we've already done
   12487             :          * for the constrained table (else we might, eg, be dropping a trigger
   12488             :          * that has unfired events).  But we can/must skip that in the
   12489             :          * self-referential case.
   12490             :          */
   12491         548 :         if (contype == CONSTRAINT_FOREIGN &&
   12492          72 :             con->confrelid != RelationGetRelid(rel))
   12493             :         {
   12494             :             Relation    frel;
   12495             : 
   12496             :             /* Must match lock taken by RemoveTriggerById: */
   12497          72 :             frel = table_open(con->confrelid, AccessExclusiveLock);
   12498          72 :             CheckAlterTableIsSafe(frel);
   12499          66 :             table_close(frel, NoLock);
   12500             :         }
   12501             : 
   12502             :         /*
   12503             :          * Perform the actual constraint deletion
   12504             :          */
   12505         542 :         conobj.classId = ConstraintRelationId;
   12506         542 :         conobj.objectId = con->oid;
   12507         542 :         conobj.objectSubId = 0;
   12508             : 
   12509         542 :         performDeletion(&conobj, behavior, 0);
   12510             : 
   12511         506 :         found = true;
   12512             :     }
   12513             : 
   12514         536 :     systable_endscan(scan);
   12515             : 
   12516         536 :     if (!found)
   12517             :     {
   12518          30 :         if (!missing_ok)
   12519             :         {
   12520          18 :             ereport(ERROR,
   12521             :                     (errcode(ERRCODE_UNDEFINED_OBJECT),
   12522             :                      errmsg("constraint \"%s\" of relation \"%s\" does not exist",
   12523             :                             constrName, RelationGetRelationName(rel))));
   12524             :         }
   12525             :         else
   12526             :         {
   12527          12 :             ereport(NOTICE,
   12528             :                     (errmsg("constraint \"%s\" of relation \"%s\" does not exist, skipping",
   12529             :                             constrName, RelationGetRelationName(rel))));
   12530          12 :             table_close(conrel, RowExclusiveLock);
   12531          54 :             return;
   12532             :         }
   12533             :     }
   12534             : 
   12535             :     /*
   12536             :      * For partitioned tables, non-CHECK inherited constraints are dropped via
   12537             :      * the dependency mechanism, so we're done here.
   12538             :      */
   12539         506 :     if (contype != CONSTRAINT_CHECK &&
   12540         182 :         rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
   12541             :     {
   12542          42 :         table_close(conrel, RowExclusiveLock);
   12543          42 :         return;
   12544             :     }
   12545             : 
   12546             :     /*
   12547             :      * Propagate to children as appropriate.  Unlike most other ALTER
   12548             :      * routines, we have to do this one level of recursion at a time; we can't
   12549             :      * use find_all_inheritors to do it in one pass.
   12550             :      */
   12551         464 :     if (!is_no_inherit_constraint)
   12552         330 :         children = find_inheritance_children(RelationGetRelid(rel), lockmode);
   12553             :     else
   12554         134 :         children = NIL;
   12555             : 
   12556             :     /*
   12557             :      * For a partitioned table, if partitions exist and we are told not to
   12558             :      * recurse, it's a user error.  It doesn't make sense to have a constraint
   12559             :      * be defined only on the parent, especially if it's a partitioned table.
   12560             :      */
   12561         464 :     if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&
   12562          62 :         children != NIL && !recurse)
   12563           6 :         ereport(ERROR,
   12564             :                 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
   12565             :                  errmsg("cannot remove constraint from only the partitioned table when partitions exist"),
   12566             :                  errhint("Do not specify the ONLY keyword.")));
   12567             : 
   12568        1116 :     foreach_oid(childrelid, children)
   12569             :     {
   12570             :         Relation    childrel;
   12571             :         HeapTuple   copy_tuple;
   12572             : 
   12573             :         /* find_inheritance_children already got lock */
   12574         200 :         childrel = table_open(childrelid, NoLock);
   12575         200 :         CheckAlterTableIsSafe(childrel);
   12576             : 
   12577         200 :         ScanKeyInit(&skey[0],
   12578             :                     Anum_pg_constraint_conrelid,
   12579             :                     BTEqualStrategyNumber, F_OIDEQ,
   12580             :                     ObjectIdGetDatum(childrelid));
   12581         200 :         ScanKeyInit(&skey[1],
   12582             :                     Anum_pg_constraint_contypid,
   12583             :                     BTEqualStrategyNumber, F_OIDEQ,
   12584             :                     ObjectIdGetDatum(InvalidOid));
   12585         200 :         ScanKeyInit(&skey[2],
   12586             :                     Anum_pg_constraint_conname,
   12587             :                     BTEqualStrategyNumber, F_NAMEEQ,
   12588             :                     CStringGetDatum(constrName));
   12589         200 :         scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
   12590             :                                   true, NULL, 3, skey);
   12591             : 
   12592             :         /* There can be at most one matching row */
   12593         200 :         if (!HeapTupleIsValid(tuple = systable_getnext(scan)))
   12594           0 :             ereport(ERROR,
   12595             :                     (errcode(ERRCODE_UNDEFINED_OBJECT),
   12596             :                      errmsg("constraint \"%s\" of relation \"%s\" does not exist",
   12597             :                             constrName,
   12598             :                             RelationGetRelationName(childrel))));
   12599             : 
   12600         200 :         copy_tuple = heap_copytuple(tuple);
   12601             : 
   12602         200 :         systable_endscan(scan);
   12603             : 
   12604         200 :         con = (Form_pg_constraint) GETSTRUCT(copy_tuple);
   12605             : 
   12606             :         /* Right now only CHECK constraints can be inherited */
   12607         200 :         if (con->contype != CONSTRAINT_CHECK)
   12608           0 :             elog(ERROR, "inherited constraint is not a CHECK constraint");
   12609             : 
   12610         200 :         if (con->coninhcount <= 0)    /* shouldn't happen */
   12611           0 :             elog(ERROR, "relation %u has non-inherited constraint \"%s\"",
   12612             :                  childrelid, constrName);
   12613             : 
   12614         200 :         if (recurse)
   12615             :         {
   12616             :             /*
   12617             :              * If the child constraint has other definition sources, just
   12618             :              * decrement its inheritance count; if not, recurse to delete it.
   12619             :              */
   12620         194 :             if (con->coninhcount == 1 && !con->conislocal)
   12621             :             {
   12622             :                 /* Time to delete this child constraint, too */
   12623         140 :                 ATExecDropConstraint(childrel, constrName, behavior,
   12624             :                                      true, true,
   12625             :                                      false, lockmode);
   12626             :             }
   12627             :             else
   12628             :             {
   12629             :                 /* Child constraint must survive my deletion */
   12630          54 :                 con->coninhcount--;
   12631          54 :                 CatalogTupleUpdate(conrel, &copy_tuple->t_self, copy_tuple);
   12632             : 
   12633             :                 /* Make update visible */
   12634          54 :                 CommandCounterIncrement();
   12635             :             }
   12636             :         }
   12637             :         else
   12638             :         {
   12639             :             /*
   12640             :              * If we were told to drop ONLY in this table (no recursion), we
   12641             :              * need to mark the inheritors' constraints as locally defined
   12642             :              * rather than inherited.
   12643             :              */
   12644           6 :             con->coninhcount--;
   12645           6 :             con->conislocal = true;
   12646             : 
   12647           6 :             CatalogTupleUpdate(conrel, &copy_tuple->t_self, copy_tuple);
   12648             : 
   12649             :             /* Make update visible */
   12650           6 :             CommandCounterIncrement();
   12651             :         }
   12652             : 
   12653         200 :         heap_freetuple(copy_tuple);
   12654             : 
   12655         200 :         table_close(childrel, NoLock);
   12656             :     }
   12657             : 
   12658         458 :     table_close(conrel, RowExclusiveLock);
   12659             : }
   12660             : 
   12661             : /*
   12662             :  * ALTER COLUMN TYPE
   12663             :  *
   12664             :  * Unlike other subcommand types, we do parse transformation for ALTER COLUMN
   12665             :  * TYPE during phase 1 --- the AlterTableCmd passed in here is already
   12666             :  * transformed (and must be, because we rely on some transformed fields).
   12667             :  *
   12668             :  * The point of this is that the execution of all ALTER COLUMN TYPEs for a
   12669             :  * table will be done "in parallel" during phase 3, so all the USING
   12670             :  * expressions should be parsed assuming the original column types.  Also,
   12671             :  * this allows a USING expression to refer to a field that will be dropped.
   12672             :  *
   12673             :  * To make this work safely, AT_PASS_DROP then AT_PASS_ALTER_TYPE must be
   12674             :  * the first two execution steps in phase 2; they must not see the effects
   12675             :  * of any other subcommand types, since the USING expressions are parsed
   12676             :  * against the unmodified table's state.
   12677             :  */
   12678             : static void
   12679        1122 : ATPrepAlterColumnType(List **wqueue,
   12680             :                       AlteredTableInfo *tab, Relation rel,
   12681             :                       bool recurse, bool recursing,
   12682             :                       AlterTableCmd *cmd, LOCKMODE lockmode,
   12683             :                       AlterTableUtilityContext *context)
   12684             : {
   12685        1122 :     char       *colName = cmd->name;
   12686        1122 :     ColumnDef  *def = (ColumnDef *) cmd->def;
   12687        1122 :     TypeName   *typeName = def->typeName;
   12688        1122 :     Node       *transform = def->cooked_default;
   12689             :     HeapTuple   tuple;
   12690             :     Form_pg_attribute attTup;
   12691             :     AttrNumber  attnum;
   12692             :     Oid         targettype;
   12693             :     int32       targettypmod;
   12694             :     Oid         targetcollid;
   12695             :     NewColumnValue *newval;
   12696        1122 :     ParseState *pstate = make_parsestate(NULL);
   12697             :     AclResult   aclresult;
   12698             :     bool        is_expr;
   12699             : 
   12700        1122 :     if (rel->rd_rel->reloftype && !recursing)
   12701           6 :         ereport(ERROR,
   12702             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   12703             :                  errmsg("cannot alter column type of typed table")));
   12704             : 
   12705             :     /* lookup the attribute so we can check inheritance status */
   12706        1116 :     tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
   12707        1116 :     if (!HeapTupleIsValid(tuple))
   12708           0 :         ereport(ERROR,
   12709             :                 (errcode(ERRCODE_UNDEFINED_COLUMN),
   12710             :                  errmsg("column \"%s\" of relation \"%s\" does not exist",
   12711             :                         colName, RelationGetRelationName(rel))));
   12712        1116 :     attTup = (Form_pg_attribute) GETSTRUCT(tuple);
   12713        1116 :     attnum = attTup->attnum;
   12714             : 
   12715             :     /* Can't alter a system attribute */
   12716        1116 :     if (attnum <= 0)
   12717           0 :         ereport(ERROR,
   12718             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   12719             :                  errmsg("cannot alter system column \"%s\"",
   12720             :                         colName)));
   12721             : 
   12722             :     /*
   12723             :      * Don't alter inherited columns.  At outer level, there had better not be
   12724             :      * any inherited definition; when recursing, we assume this was checked at
   12725             :      * the parent level (see below).
   12726             :      */
   12727        1116 :     if (attTup->attinhcount > 0 && !recursing)
   12728           6 :         ereport(ERROR,
   12729             :                 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
   12730             :                  errmsg("cannot alter inherited column \"%s\"",
   12731             :                         colName)));
   12732             : 
   12733             :     /* Don't alter columns used in the partition key */
   12734        1110 :     if (has_partition_attrs(rel,
   12735             :                             bms_make_singleton(attnum - FirstLowInvalidHeapAttributeNumber),
   12736             :                             &is_expr))
   12737          18 :         ereport(ERROR,
   12738             :                 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
   12739             :                  errmsg("cannot alter column \"%s\" because it is part of the partition key of relation \"%s\"",
   12740             :                         colName, RelationGetRelationName(rel))));
   12741             : 
   12742             :     /* Look up the target type */
   12743        1092 :     typenameTypeIdAndMod(NULL, typeName, &targettype, &targettypmod);
   12744             : 
   12745        1092 :     aclresult = object_aclcheck(TypeRelationId, targettype, GetUserId(), ACL_USAGE);
   12746        1092 :     if (aclresult != ACLCHECK_OK)
   12747          12 :         aclcheck_error_type(aclresult, targettype);
   12748             : 
   12749             :     /* And the collation */
   12750        1080 :     targetcollid = GetColumnDefCollation(NULL, def, targettype);
   12751             : 
   12752             :     /* make sure datatype is legal for a column */
   12753        1080 :     CheckAttributeType(colName, targettype, targetcollid,
   12754        1080 :                        list_make1_oid(rel->rd_rel->reltype),
   12755             :                        0);
   12756             : 
   12757        1074 :     if (tab->relkind == RELKIND_RELATION ||
   12758         196 :         tab->relkind == RELKIND_PARTITIONED_TABLE)
   12759             :     {
   12760             :         /*
   12761             :          * Set up an expression to transform the old data value to the new
   12762             :          * type. If a USING option was given, use the expression as
   12763             :          * transformed by transformAlterTableStmt, else just take the old
   12764             :          * value and try to coerce it.  We do this first so that type
   12765             :          * incompatibility can be detected before we waste effort, and because
   12766             :          * we need the expression to be parsed against the original table row
   12767             :          * type.
   12768             :          */
   12769         938 :         if (!transform)
   12770             :         {
   12771         734 :             transform = (Node *) makeVar(1, attnum,
   12772             :                                          attTup->atttypid, attTup->atttypmod,
   12773             :                                          attTup->attcollation,
   12774             :                                          0);
   12775             :         }
   12776             : 
   12777         938 :         transform = coerce_to_target_type(pstate,
   12778             :                                           transform, exprType(transform),
   12779             :                                           targettype, targettypmod,
   12780             :                                           COERCION_ASSIGNMENT,
   12781             :                                           COERCE_IMPLICIT_CAST,
   12782             :                                           -1);
   12783         938 :         if (transform == NULL)
   12784             :         {
   12785             :             /* error text depends on whether USING was specified or not */
   12786          24 :             if (def->cooked_default != NULL)
   12787           6 :                 ereport(ERROR,
   12788             :                         (errcode(ERRCODE_DATATYPE_MISMATCH),
   12789             :                          errmsg("result of USING clause for column \"%s\""
   12790             :                                 " cannot be cast automatically to type %s",
   12791             :                                 colName, format_type_be(targettype)),
   12792             :                          errhint("You might need to add an explicit cast.")));
   12793             :             else
   12794          18 :                 ereport(ERROR,
   12795             :                         (errcode(ERRCODE_DATATYPE_MISMATCH),
   12796             :                          errmsg("column \"%s\" cannot be cast automatically to type %s",
   12797             :                                 colName, format_type_be(targettype)),
   12798             :                 /* translator: USING is SQL, don't translate it */
   12799             :                          errhint("You might need to specify \"USING %s::%s\".",
   12800             :                                  quote_identifier(colName),
   12801             :                                  format_type_with_typemod(targettype,
   12802             :                                                           targettypmod))));
   12803             :         }
   12804             : 
   12805             :         /* Fix collations after all else */
   12806         914 :         assign_expr_collations(pstate, transform);
   12807             : 
   12808             :         /* Plan the expr now so we can accurately assess the need to rewrite. */
   12809         914 :         transform = (Node *) expression_planner((Expr *) transform);
   12810             : 
   12811             :         /*
   12812             :          * Add a work queue item to make ATRewriteTable update the column
   12813             :          * contents.
   12814             :          */
   12815         914 :         newval = (NewColumnValue *) palloc0(sizeof(NewColumnValue));
   12816         914 :         newval->attnum = attnum;
   12817         914 :         newval->expr = (Expr *) transform;
   12818         914 :         newval->is_generated = false;
   12819             : 
   12820         914 :         tab->newvals = lappend(tab->newvals, newval);
   12821         914 :         if (ATColumnChangeRequiresRewrite(transform, attnum))
   12822         732 :             tab->rewrite |= AT_REWRITE_COLUMN_REWRITE;
   12823             :     }
   12824         136 :     else if (transform)
   12825          12 :         ereport(ERROR,
   12826             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   12827             :                  errmsg("\"%s\" is not a table",
   12828             :                         RelationGetRelationName(rel))));
   12829             : 
   12830        1038 :     if (!RELKIND_HAS_STORAGE(tab->relkind))
   12831             :     {
   12832             :         /*
   12833             :          * For relations without storage, do this check now.  Regular tables
   12834             :          * will check it later when the table is being rewritten.
   12835             :          */
   12836         184 :         find_composite_type_dependencies(rel->rd_rel->reltype, rel, NULL);
   12837             :     }
   12838             : 
   12839        1008 :     ReleaseSysCache(tuple);
   12840             : 
   12841             :     /*
   12842             :      * Recurse manually by queueing a new command for each child, if
   12843             :      * necessary. We cannot apply ATSimpleRecursion here because we need to
   12844             :      * remap attribute numbers in the USING expression, if any.
   12845             :      *
   12846             :      * If we are told not to recurse, there had better not be any child
   12847             :      * tables; else the alter would put them out of step.
   12848             :      */
   12849        1008 :     if (recurse)
   12850             :     {
   12851         756 :         Oid         relid = RelationGetRelid(rel);
   12852             :         List       *child_oids,
   12853             :                    *child_numparents;
   12854             :         ListCell   *lo,
   12855             :                    *li;
   12856             : 
   12857         756 :         child_oids = find_all_inheritors(relid, lockmode,
   12858             :                                          &child_numparents);
   12859             : 
   12860             :         /*
   12861             :          * find_all_inheritors does the recursive search of the inheritance
   12862             :          * hierarchy, so all we have to do is process all of the relids in the
   12863             :          * list that it returns.
   12864             :          */
   12865        1714 :         forboth(lo, child_oids, li, child_numparents)
   12866             :         {
   12867         982 :             Oid         childrelid = lfirst_oid(lo);
   12868         982 :             int         numparents = lfirst_int(li);
   12869             :             Relation    childrel;
   12870             :             HeapTuple   childtuple;
   12871             :             Form_pg_attribute childattTup;
   12872             : 
   12873         982 :             if (childrelid == relid)
   12874         756 :                 continue;
   12875             : 
   12876             :             /* find_all_inheritors already got lock */
   12877         226 :             childrel = relation_open(childrelid, NoLock);
   12878         226 :             CheckAlterTableIsSafe(childrel);
   12879             : 
   12880             :             /*
   12881             :              * Verify that the child doesn't have any inherited definitions of
   12882             :              * this column that came from outside this inheritance hierarchy.
   12883             :              * (renameatt makes a similar test, though in a different way
   12884             :              * because of its different recursion mechanism.)
   12885             :              */
   12886         226 :             childtuple = SearchSysCacheAttName(RelationGetRelid(childrel),
   12887             :                                                colName);
   12888         226 :             if (!HeapTupleIsValid(childtuple))
   12889           0 :                 ereport(ERROR,
   12890             :                         (errcode(ERRCODE_UNDEFINED_COLUMN),
   12891             :                          errmsg("column \"%s\" of relation \"%s\" does not exist",
   12892             :                                 colName, RelationGetRelationName(childrel))));
   12893         226 :             childattTup = (Form_pg_attribute) GETSTRUCT(childtuple);
   12894             : 
   12895         226 :             if (childattTup->attinhcount > numparents)
   12896           6 :                 ereport(ERROR,
   12897             :                         (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
   12898             :                          errmsg("cannot alter inherited column \"%s\" of relation \"%s\"",
   12899             :                                 colName, RelationGetRelationName(childrel))));
   12900             : 
   12901         220 :             ReleaseSysCache(childtuple);
   12902             : 
   12903             :             /*
   12904             :              * Remap the attribute numbers.  If no USING expression was
   12905             :              * specified, there is no need for this step.
   12906             :              */
   12907         220 :             if (def->cooked_default)
   12908             :             {
   12909             :                 AttrMap    *attmap;
   12910             :                 bool        found_whole_row;
   12911             : 
   12912             :                 /* create a copy to scribble on */
   12913          72 :                 cmd = copyObject(cmd);
   12914             : 
   12915          72 :                 attmap = build_attrmap_by_name(RelationGetDescr(childrel),
   12916             :                                                RelationGetDescr(rel),
   12917             :                                                false);
   12918         144 :                 ((ColumnDef *) cmd->def)->cooked_default =
   12919          72 :                     map_variable_attnos(def->cooked_default,
   12920             :                                         1, 0,
   12921             :                                         attmap,
   12922             :                                         InvalidOid, &found_whole_row);
   12923          72 :                 if (found_whole_row)
   12924           6 :                     ereport(ERROR,
   12925             :                             (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   12926             :                              errmsg("cannot convert whole-row table reference"),
   12927             :                              errdetail("USING expression contains a whole-row table reference.")));
   12928          66 :                 pfree(attmap);
   12929             :             }
   12930         214 :             ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode, context);
   12931         202 :             relation_close(childrel, NoLock);
   12932             :         }
   12933             :     }
   12934         302 :     else if (!recursing &&
   12935          50 :              find_inheritance_children(RelationGetRelid(rel), NoLock) != NIL)
   12936           0 :         ereport(ERROR,
   12937             :                 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
   12938             :                  errmsg("type of inherited column \"%s\" must be changed in child tables too",
   12939             :                         colName)));
   12940             : 
   12941         984 :     if (tab->relkind == RELKIND_COMPOSITE_TYPE)
   12942          50 :         ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
   12943         978 : }
   12944             : 
   12945             : /*
   12946             :  * When the data type of a column is changed, a rewrite might not be required
   12947             :  * if the new type is sufficiently identical to the old one, and the USING
   12948             :  * clause isn't trying to insert some other value.  It's safe to skip the
   12949             :  * rewrite in these cases:
   12950             :  *
   12951             :  * - the old type is binary coercible to the new type
   12952             :  * - the new type is an unconstrained domain over the old type
   12953             :  * - {NEW,OLD} or {OLD,NEW} is {timestamptz,timestamp} and the timezone is UTC
   12954             :  *
   12955             :  * In the case of a constrained domain, we could get by with scanning the
   12956             :  * table and checking the constraint rather than actually rewriting it, but we
   12957             :  * don't currently try to do that.
   12958             :  */
   12959             : static bool
   12960        1018 : ATColumnChangeRequiresRewrite(Node *expr, AttrNumber varattno)
   12961             : {
   12962             :     Assert(expr != NULL);
   12963             : 
   12964             :     for (;;)
   12965             :     {
   12966             :         /* only one varno, so no need to check that */
   12967        1018 :         if (IsA(expr, Var) && ((Var *) expr)->varattno == varattno)
   12968         182 :             return false;
   12969         836 :         else if (IsA(expr, RelabelType))
   12970          92 :             expr = (Node *) ((RelabelType *) expr)->arg;
   12971         744 :         else if (IsA(expr, CoerceToDomain))
   12972             :         {
   12973           0 :             CoerceToDomain *d = (CoerceToDomain *) expr;
   12974             : 
   12975           0 :             if (DomainHasConstraints(d->resulttype))
   12976           0 :                 return true;
   12977           0 :             expr = (Node *) d->arg;
   12978             :         }
   12979         744 :         else if (IsA(expr, FuncExpr))
   12980             :         {
   12981         544 :             FuncExpr   *f = (FuncExpr *) expr;
   12982             : 
   12983         544 :             switch (f->funcid)
   12984             :             {
   12985          18 :                 case F_TIMESTAMPTZ_TIMESTAMP:
   12986             :                 case F_TIMESTAMP_TIMESTAMPTZ:
   12987          18 :                     if (TimestampTimestampTzRequiresRewrite())
   12988           6 :                         return true;
   12989             :                     else
   12990          12 :                         expr = linitial(f->args);
   12991          12 :                     break;
   12992         526 :                 default:
   12993         526 :                     return true;
   12994             :             }
   12995             :         }
   12996             :         else
   12997         200 :             return true;
   12998             :     }
   12999             : }
   13000             : 
   13001             : /*
   13002             :  * ALTER COLUMN .. SET DATA TYPE
   13003             :  *
   13004             :  * Return the address of the modified column.
   13005             :  */
   13006             : static ObjectAddress
   13007         948 : ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
   13008             :                       AlterTableCmd *cmd, LOCKMODE lockmode)
   13009             : {
   13010         948 :     char       *colName = cmd->name;
   13011         948 :     ColumnDef  *def = (ColumnDef *) cmd->def;
   13012         948 :     TypeName   *typeName = def->typeName;
   13013             :     HeapTuple   heapTup;
   13014             :     Form_pg_attribute attTup,
   13015             :                 attOldTup;
   13016             :     AttrNumber  attnum;
   13017             :     HeapTuple   typeTuple;
   13018             :     Form_pg_type tform;
   13019             :     Oid         targettype;
   13020             :     int32       targettypmod;
   13021             :     Oid         targetcollid;
   13022             :     Node       *defaultexpr;
   13023             :     Relation    attrelation;
   13024             :     Relation    depRel;
   13025             :     ScanKeyData key[3];
   13026             :     SysScanDesc scan;
   13027             :     HeapTuple   depTup;
   13028             :     ObjectAddress address;
   13029             : 
   13030             :     /*
   13031             :      * Clear all the missing values if we're rewriting the table, since this
   13032             :      * renders them pointless.
   13033             :      */
   13034         948 :     if (tab->rewrite)
   13035             :     {
   13036             :         Relation    newrel;
   13037             : 
   13038         678 :         newrel = table_open(RelationGetRelid(rel), NoLock);
   13039         678 :         RelationClearMissing(newrel);
   13040         678 :         relation_close(newrel, NoLock);
   13041             :         /* make sure we don't conflict with later attribute modifications */
   13042         678 :         CommandCounterIncrement();
   13043             :     }
   13044             : 
   13045         948 :     attrelation = table_open(AttributeRelationId, RowExclusiveLock);
   13046             : 
   13047             :     /* Look up the target column */
   13048         948 :     heapTup = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
   13049         948 :     if (!HeapTupleIsValid(heapTup)) /* shouldn't happen */
   13050           0 :         ereport(ERROR,
   13051             :                 (errcode(ERRCODE_UNDEFINED_COLUMN),
   13052             :                  errmsg("column \"%s\" of relation \"%s\" does not exist",
   13053             :                         colName, RelationGetRelationName(rel))));
   13054         948 :     attTup = (Form_pg_attribute) GETSTRUCT(heapTup);
   13055         948 :     attnum = attTup->attnum;
   13056         948 :     attOldTup = TupleDescAttr(tab->oldDesc, attnum - 1);
   13057             : 
   13058             :     /* Check for multiple ALTER TYPE on same column --- can't cope */
   13059         948 :     if (attTup->atttypid != attOldTup->atttypid ||
   13060         948 :         attTup->atttypmod != attOldTup->atttypmod)
   13061           0 :         ereport(ERROR,
   13062             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   13063             :                  errmsg("cannot alter type of column \"%s\" twice",
   13064             :                         colName)));
   13065             : 
   13066             :     /* Look up the target type (should not fail, since prep found it) */
   13067         948 :     typeTuple = typenameType(NULL, typeName, &targettypmod);
   13068         948 :     tform = (Form_pg_type) GETSTRUCT(typeTuple);
   13069         948 :     targettype = tform->oid;
   13070             :     /* And the collation */
   13071         948 :     targetcollid = GetColumnDefCollation(NULL, def, targettype);
   13072             : 
   13073             :     /*
   13074             :      * If there is a default expression for the column, get it and ensure we
   13075             :      * can coerce it to the new datatype.  (We must do this before changing
   13076             :      * the column type, because build_column_default itself will try to
   13077             :      * coerce, and will not issue the error message we want if it fails.)
   13078             :      *
   13079             :      * We remove any implicit coercion steps at the top level of the old
   13080             :      * default expression; this has been agreed to satisfy the principle of
   13081             :      * least surprise.  (The conversion to the new column type should act like
   13082             :      * it started from what the user sees as the stored expression, and the
   13083             :      * implicit coercions aren't going to be shown.)
   13084             :      */
   13085         948 :     if (attTup->atthasdef)
   13086             :     {
   13087          62 :         defaultexpr = build_column_default(rel, attnum);
   13088             :         Assert(defaultexpr);
   13089          62 :         defaultexpr = strip_implicit_coercions(defaultexpr);
   13090          62 :         defaultexpr = coerce_to_target_type(NULL,   /* no UNKNOWN params */
   13091             :                                             defaultexpr, exprType(defaultexpr),
   13092             :                                             targettype, targettypmod,
   13093             :                                             COERCION_ASSIGNMENT,
   13094             :                                             COERCE_IMPLICIT_CAST,
   13095             :                                             -1);
   13096          62 :         if (defaultexpr == NULL)
   13097             :         {
   13098          12 :             if (attTup->attgenerated)
   13099           6 :                 ereport(ERROR,
   13100             :                         (errcode(ERRCODE_DATATYPE_MISMATCH),
   13101             :                          errmsg("generation expression for column \"%s\" cannot be cast automatically to type %s",
   13102             :                                 colName, format_type_be(targettype))));
   13103             :             else
   13104           6 :                 ereport(ERROR,
   13105             :                         (errcode(ERRCODE_DATATYPE_MISMATCH),
   13106             :                          errmsg("default for column \"%s\" cannot be cast automatically to type %s",
   13107             :                                 colName, format_type_be(targettype))));
   13108             :         }
   13109             :     }
   13110             :     else
   13111         886 :         defaultexpr = NULL;
   13112             : 
   13113             :     /*
   13114             :      * Find everything that depends on the column (constraints, indexes, etc),
   13115             :      * and record enough information to let us recreate the objects.
   13116             :      *
   13117             :      * The actual recreation does not happen here, but only after we have
   13118             :      * performed all the individual ALTER TYPE operations.  We have to save
   13119             :      * the info before executing ALTER TYPE, though, else the deparser will
   13120             :      * get confused.
   13121             :      */
   13122         936 :     RememberAllDependentForRebuilding(tab, AT_AlterColumnType, rel, attnum, colName);
   13123             : 
   13124             :     /*
   13125             :      * Now scan for dependencies of this column on other things.  The only
   13126             :      * things we should find are the dependency on the column datatype and
   13127             :      * possibly a collation dependency.  Those can be removed.
   13128             :      */
   13129         912 :     depRel = table_open(DependRelationId, RowExclusiveLock);
   13130             : 
   13131         912 :     ScanKeyInit(&key[0],
   13132             :                 Anum_pg_depend_classid,
   13133             :                 BTEqualStrategyNumber, F_OIDEQ,
   13134             :                 ObjectIdGetDatum(RelationRelationId));
   13135         912 :     ScanKeyInit(&key[1],
   13136             :                 Anum_pg_depend_objid,
   13137             :                 BTEqualStrategyNumber, F_OIDEQ,
   13138             :                 ObjectIdGetDatum(RelationGetRelid(rel)));
   13139         912 :     ScanKeyInit(&key[2],
   13140             :                 Anum_pg_depend_objsubid,
   13141             :                 BTEqualStrategyNumber, F_INT4EQ,
   13142             :                 Int32GetDatum((int32) attnum));
   13143             : 
   13144         912 :     scan = systable_beginscan(depRel, DependDependerIndexId, true,
   13145             :                               NULL, 3, key);
   13146             : 
   13147         916 :     while (HeapTupleIsValid(depTup = systable_getnext(scan)))
   13148             :     {
   13149           4 :         Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(depTup);
   13150             :         ObjectAddress foundObject;
   13151             : 
   13152           4 :         foundObject.classId = foundDep->refclassid;
   13153           4 :         foundObject.objectId = foundDep->refobjid;
   13154           4 :         foundObject.objectSubId = foundDep->refobjsubid;
   13155             : 
   13156           4 :         if (foundDep->deptype != DEPENDENCY_NORMAL)
   13157           0 :             elog(ERROR, "found unexpected dependency type '%c'",
   13158             :                  foundDep->deptype);
   13159           4 :         if (!(foundDep->refclassid == TypeRelationId &&
   13160           4 :               foundDep->refobjid == attTup->atttypid) &&
   13161           0 :             !(foundDep->refclassid == CollationRelationId &&
   13162           0 :               foundDep->refobjid == attTup->attcollation))
   13163           0 :             elog(ERROR, "found unexpected dependency for column: %s",
   13164             :                  getObjectDescription(&foundObject, false));
   13165             : 
   13166           4 :         CatalogTupleDelete(depRel, &depTup->t_self);
   13167             :     }
   13168             : 
   13169         912 :     systable_endscan(scan);
   13170             : 
   13171         912 :     table_close(depRel, RowExclusiveLock);
   13172             : 
   13173             :     /*
   13174             :      * Here we go --- change the recorded column type and collation.  (Note
   13175             :      * heapTup is a copy of the syscache entry, so okay to scribble on.) First
   13176             :      * fix up the missing value if any.
   13177             :      */
   13178         912 :     if (attTup->atthasmissing)
   13179             :     {
   13180             :         Datum       missingval;
   13181             :         bool        missingNull;
   13182             : 
   13183             :         /* if rewrite is true the missing value should already be cleared */
   13184             :         Assert(tab->rewrite == 0);
   13185             : 
   13186             :         /* Get the missing value datum */
   13187           6 :         missingval = heap_getattr(heapTup,
   13188             :                                   Anum_pg_attribute_attmissingval,
   13189             :                                   attrelation->rd_att,
   13190             :                                   &missingNull);
   13191             : 
   13192             :         /* if it's a null array there is nothing to do */
   13193             : 
   13194           6 :         if (!missingNull)
   13195             :         {
   13196             :             /*
   13197             :              * Get the datum out of the array and repack it in a new array
   13198             :              * built with the new type data. We assume that since the table
   13199             :              * doesn't need rewriting, the actual Datum doesn't need to be
   13200             :              * changed, only the array metadata.
   13201             :              */
   13202             : 
   13203           6 :             int         one = 1;
   13204             :             bool        isNull;
   13205           6 :             Datum       valuesAtt[Natts_pg_attribute] = {0};
   13206           6 :             bool        nullsAtt[Natts_pg_attribute] = {0};
   13207           6 :             bool        replacesAtt[Natts_pg_attribute] = {0};
   13208             :             HeapTuple   newTup;
   13209             : 
   13210          12 :             missingval = array_get_element(missingval,
   13211             :                                            1,
   13212             :                                            &one,
   13213             :                                            0,
   13214           6 :                                            attTup->attlen,
   13215           6 :                                            attTup->attbyval,
   13216           6 :                                            attTup->attalign,
   13217             :                                            &isNull);
   13218           6 :             missingval = PointerGetDatum(construct_array(&missingval,
   13219             :                                                          1,
   13220             :                                                          targettype,
   13221           6 :                                                          tform->typlen,
   13222           6 :                                                          tform->typbyval,
   13223           6 :                                                          tform->typalign));
   13224             : 
   13225           6 :             valuesAtt[Anum_pg_attribute_attmissingval - 1] = missingval;
   13226           6 :             replacesAtt[Anum_pg_attribute_attmissingval - 1] = true;
   13227           6 :             nullsAtt[Anum_pg_attribute_attmissingval - 1] = false;
   13228             : 
   13229           6 :             newTup = heap_modify_tuple(heapTup, RelationGetDescr(attrelation),
   13230             :                                        valuesAtt, nullsAtt, replacesAtt);
   13231           6 :             heap_freetuple(heapTup);
   13232           6 :             heapTup = newTup;
   13233           6 :             attTup = (Form_pg_attribute) GETSTRUCT(heapTup);
   13234             :         }
   13235             :     }
   13236             : 
   13237         912 :     attTup->atttypid = targettype;
   13238         912 :     attTup->atttypmod = targettypmod;
   13239         912 :     attTup->attcollation = targetcollid;
   13240         912 :     if (list_length(typeName->arrayBounds) > PG_INT16_MAX)
   13241           0 :         ereport(ERROR,
   13242             :                 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
   13243             :                 errmsg("too many array dimensions"));
   13244         912 :     attTup->attndims = list_length(typeName->arrayBounds);
   13245         912 :     attTup->attlen = tform->typlen;
   13246         912 :     attTup->attbyval = tform->typbyval;
   13247         912 :     attTup->attalign = tform->typalign;
   13248         912 :     attTup->attstorage = tform->typstorage;
   13249         912 :     attTup->attcompression = InvalidCompressionMethod;
   13250             : 
   13251         912 :     ReleaseSysCache(typeTuple);
   13252             : 
   13253         912 :     CatalogTupleUpdate(attrelation, &heapTup->t_self, heapTup);
   13254             : 
   13255         912 :     table_close(attrelation, RowExclusiveLock);
   13256             : 
   13257             :     /* Install dependencies on new datatype and collation */
   13258         912 :     add_column_datatype_dependency(RelationGetRelid(rel), attnum, targettype);
   13259         912 :     add_column_collation_dependency(RelationGetRelid(rel), attnum, targetcollid);
   13260             : 
   13261             :     /*
   13262             :      * Drop any pg_statistic entry for the column, since it's now wrong type
   13263             :      */
   13264         912 :     RemoveStatistics(RelationGetRelid(rel), attnum);
   13265             : 
   13266         912 :     InvokeObjectPostAlterHook(RelationRelationId,
   13267             :                               RelationGetRelid(rel), attnum);
   13268             : 
   13269             :     /*
   13270             :      * Update the default, if present, by brute force --- remove and re-add
   13271             :      * the default.  Probably unsafe to take shortcuts, since the new version
   13272             :      * may well have additional dependencies.  (It's okay to do this now,
   13273             :      * rather than after other ALTER TYPE commands, since the default won't
   13274             :      * depend on other column types.)
   13275             :      */
   13276         912 :     if (defaultexpr)
   13277             :     {
   13278             :         /*
   13279             :          * If it's a GENERATED default, drop its dependency records, in
   13280             :          * particular its INTERNAL dependency on the column, which would
   13281             :          * otherwise cause dependency.c to refuse to perform the deletion.
   13282             :          */
   13283          50 :         if (attTup->attgenerated)
   13284             :         {
   13285           6 :             Oid         attrdefoid = GetAttrDefaultOid(RelationGetRelid(rel), attnum);
   13286             : 
   13287           6 :             if (!OidIsValid(attrdefoid))
   13288           0 :                 elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
   13289             :                      RelationGetRelid(rel), attnum);
   13290           6 :             (void) deleteDependencyRecordsFor(AttrDefaultRelationId, attrdefoid, false);
   13291             :         }
   13292             : 
   13293             :         /*
   13294             :          * Make updates-so-far visible, particularly the new pg_attribute row
   13295             :          * which will be updated again.
   13296             :          */
   13297          50 :         CommandCounterIncrement();
   13298             : 
   13299             :         /*
   13300             :          * We use RESTRICT here for safety, but at present we do not expect
   13301             :          * anything to depend on the default.
   13302             :          */
   13303          50 :         RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT, true,
   13304             :                           true);
   13305             : 
   13306          50 :         StoreAttrDefault(rel, attnum, defaultexpr, true, false);
   13307             :     }
   13308             : 
   13309         912 :     ObjectAddressSubSet(address, RelationRelationId,
   13310             :                         RelationGetRelid(rel), attnum);
   13311             : 
   13312             :     /* Cleanup */
   13313         912 :     heap_freetuple(heapTup);
   13314             : 
   13315         912 :     return address;
   13316             : }
   13317             : 
   13318             : /*
   13319             :  * Subroutine for ATExecAlterColumnType and ATExecSetExpression: Find everything
   13320             :  * that depends on the column (constraints, indexes, etc), and record enough
   13321             :  * information to let us recreate the objects.
   13322             :  */
   13323             : static void
   13324        1014 : RememberAllDependentForRebuilding(AlteredTableInfo *tab, AlterTableType subtype,
   13325             :                                   Relation rel, AttrNumber attnum, const char *colName)
   13326             : {
   13327             :     Relation    depRel;
   13328             :     ScanKeyData key[3];
   13329             :     SysScanDesc scan;
   13330             :     HeapTuple   depTup;
   13331             : 
   13332             :     Assert(subtype == AT_AlterColumnType || subtype == AT_SetExpression);
   13333             : 
   13334        1014 :     depRel = table_open(DependRelationId, RowExclusiveLock);
   13335             : 
   13336        1014 :     ScanKeyInit(&key[0],
   13337             :                 Anum_pg_depend_refclassid,
   13338             :                 BTEqualStrategyNumber, F_OIDEQ,
   13339             :                 ObjectIdGetDatum(RelationRelationId));
   13340        1014 :     ScanKeyInit(&key[1],
   13341             :                 Anum_pg_depend_refobjid,
   13342             :                 BTEqualStrategyNumber, F_OIDEQ,
   13343             :                 ObjectIdGetDatum(RelationGetRelid(rel)));
   13344        1014 :     ScanKeyInit(&key[2],
   13345             :                 Anum_pg_depend_refobjsubid,
   13346             :                 BTEqualStrategyNumber, F_INT4EQ,
   13347             :                 Int32GetDatum((int32) attnum));
   13348             : 
   13349        1014 :     scan = systable_beginscan(depRel, DependReferenceIndexId, true,
   13350             :                               NULL, 3, key);
   13351             : 
   13352        1878 :     while (HeapTupleIsValid(depTup = systable_getnext(scan)))
   13353             :     {
   13354         888 :         Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(depTup);
   13355             :         ObjectAddress foundObject;
   13356             : 
   13357         888 :         foundObject.classId = foundDep->classid;
   13358         888 :         foundObject.objectId = foundDep->objid;
   13359         888 :         foundObject.objectSubId = foundDep->objsubid;
   13360             : 
   13361         888 :         switch (foundObject.classId)
   13362             :         {
   13363         270 :             case RelationRelationId:
   13364             :                 {
   13365         270 :                     char        relKind = get_rel_relkind(foundObject.objectId);
   13366             : 
   13367         270 :                     if (relKind == RELKIND_INDEX ||
   13368             :                         relKind == RELKIND_PARTITIONED_INDEX)
   13369             :                     {
   13370             :                         Assert(foundObject.objectSubId == 0);
   13371         232 :                         RememberIndexForRebuilding(foundObject.objectId, tab);
   13372             :                     }
   13373          38 :                     else if (relKind == RELKIND_SEQUENCE)
   13374             :                     {
   13375             :                         /*
   13376             :                          * This must be a SERIAL column's sequence.  We need
   13377             :                          * not do anything to it.
   13378             :                          */
   13379             :                         Assert(foundObject.objectSubId == 0);
   13380             :                     }
   13381             :                     else
   13382             :                     {
   13383             :                         /* Not expecting any other direct dependencies... */
   13384           0 :                         elog(ERROR, "unexpected object depending on column: %s",
   13385             :                              getObjectDescription(&foundObject, false));
   13386             :                     }
   13387         270 :                     break;
   13388             :                 }
   13389             : 
   13390         452 :             case ConstraintRelationId:
   13391             :                 Assert(foundObject.objectSubId == 0);
   13392         452 :                 RememberConstraintForRebuilding(foundObject.objectId, tab);
   13393         452 :                 break;
   13394             : 
   13395           0 :             case ProcedureRelationId:
   13396             : 
   13397             :                 /*
   13398             :                  * A new-style SQL function can depend on a column, if that
   13399             :                  * column is referenced in the parsed function body.  Ideally
   13400             :                  * we'd automatically update the function by deparsing and
   13401             :                  * reparsing it, but that's risky and might well fail anyhow.
   13402             :                  * FIXME someday.
   13403             :                  *
   13404             :                  * This is only a problem for AT_AlterColumnType, not
   13405             :                  * AT_SetExpression.
   13406             :                  */
   13407           0 :                 if (subtype == AT_AlterColumnType)
   13408           0 :                     ereport(ERROR,
   13409             :                             (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   13410             :                              errmsg("cannot alter type of a column used by a function or procedure"),
   13411             :                              errdetail("%s depends on column \"%s\"",
   13412             :                                        getObjectDescription(&foundObject, false),
   13413             :                                        colName)));
   13414           0 :                 break;
   13415             : 
   13416          12 :             case RewriteRelationId:
   13417             : 
   13418             :                 /*
   13419             :                  * View/rule bodies have pretty much the same issues as
   13420             :                  * function bodies.  FIXME someday.
   13421             :                  */
   13422          12 :                 if (subtype == AT_AlterColumnType)
   13423          12 :                     ereport(ERROR,
   13424             :                             (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   13425             :                              errmsg("cannot alter type of a column used by a view or rule"),
   13426             :                              errdetail("%s depends on column \"%s\"",
   13427             :                                        getObjectDescription(&foundObject, false),
   13428             :                                        colName)));
   13429           0 :                 break;
   13430             : 
   13431           0 :             case TriggerRelationId:
   13432             : 
   13433             :                 /*
   13434             :                  * A trigger can depend on a column because the column is
   13435             :                  * specified as an update target, or because the column is
   13436             :                  * used in the trigger's WHEN condition.  The first case would
   13437             :                  * not require any extra work, but the second case would
   13438             :                  * require updating the WHEN expression, which has the same
   13439             :                  * issues as above.  Since we can't easily tell which case
   13440             :                  * applies, we punt for both.  FIXME someday.
   13441             :                  */
   13442           0 :                 if (subtype == AT_AlterColumnType)
   13443           0 :                     ereport(ERROR,
   13444             :                             (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   13445             :                              errmsg("cannot alter type of a column used in a trigger definition"),
   13446             :                              errdetail("%s depends on column \"%s\"",
   13447             :                                        getObjectDescription(&foundObject, false),
   13448             :                                        colName)));
   13449           0 :                 break;
   13450             : 
   13451           0 :             case PolicyRelationId:
   13452             : 
   13453             :                 /*
   13454             :                  * A policy can depend on a column because the column is
   13455             :                  * specified in the policy's USING or WITH CHECK qual
   13456             :                  * expressions.  It might be possible to rewrite and recheck
   13457             :                  * the policy expression, but punt for now.  It's certainly
   13458             :                  * easy enough to remove and recreate the policy; still, FIXME
   13459             :                  * someday.
   13460             :                  */
   13461           0 :                 if (subtype == AT_AlterColumnType)
   13462           0 :                     ereport(ERROR,
   13463             :                             (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   13464             :                              errmsg("cannot alter type of a column used in a policy definition"),
   13465             :                              errdetail("%s depends on column \"%s\"",
   13466             :                                        getObjectDescription(&foundObject, false),
   13467             :                                        colName)));
   13468           0 :                 break;
   13469             : 
   13470         140 :             case AttrDefaultRelationId:
   13471             :                 {
   13472         140 :                     ObjectAddress col = GetAttrDefaultColumnAddress(foundObject.objectId);
   13473             : 
   13474         140 :                     if (col.objectId == RelationGetRelid(rel) &&
   13475         140 :                         col.objectSubId == attnum)
   13476             :                     {
   13477             :                         /*
   13478             :                          * Ignore the column's own default expression.  The
   13479             :                          * caller deals with it.
   13480             :                          */
   13481             :                     }
   13482             :                     else
   13483             :                     {
   13484             :                         /*
   13485             :                          * This must be a reference from the expression of a
   13486             :                          * generated column elsewhere in the same table.
   13487             :                          * Changing the type/generated expression of a column
   13488             :                          * that is used by a generated column is not allowed
   13489             :                          * by SQL standard, so just punt for now.  It might be
   13490             :                          * doable with some thinking and effort.
   13491             :                          */
   13492          12 :                         if (subtype == AT_AlterColumnType)
   13493          12 :                             ereport(ERROR,
   13494             :                                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   13495             :                                      errmsg("cannot alter type of a column used by a generated column"),
   13496             :                                      errdetail("Column \"%s\" is used by generated column \"%s\".",
   13497             :                                                colName,
   13498             :                                                get_attname(col.objectId,
   13499             :                                                            col.objectSubId,
   13500             :                                                            false))));
   13501             :                     }
   13502         128 :                     break;
   13503             :                 }
   13504             : 
   13505          14 :             case StatisticExtRelationId:
   13506             : 
   13507             :                 /*
   13508             :                  * Give the extended-stats machinery a chance to fix anything
   13509             :                  * that this column type change would break.
   13510             :                  */
   13511          14 :                 RememberStatisticsForRebuilding(foundObject.objectId, tab);
   13512          14 :                 break;
   13513             : 
   13514           0 :             case PublicationRelRelationId:
   13515             : 
   13516             :                 /*
   13517             :                  * Column reference in a PUBLICATION ... FOR TABLE ... WHERE
   13518             :                  * clause.  Same issues as above.  FIXME someday.
   13519             :                  */
   13520           0 :                 if (subtype == AT_AlterColumnType)
   13521           0 :                     ereport(ERROR,
   13522             :                             (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   13523             :                              errmsg("cannot alter type of a column used by a publication WHERE clause"),
   13524             :                              errdetail("%s depends on column \"%s\"",
   13525             :                                        getObjectDescription(&foundObject, false),
   13526             :                                        colName)));
   13527           0 :                 break;
   13528             : 
   13529           0 :             default:
   13530             : 
   13531             :                 /*
   13532             :                  * We don't expect any other sorts of objects to depend on a
   13533             :                  * column.
   13534             :                  */
   13535           0 :                 elog(ERROR, "unexpected object depending on column: %s",
   13536             :                      getObjectDescription(&foundObject, false));
   13537             :                 break;
   13538             :         }
   13539             :     }
   13540             : 
   13541         990 :     systable_endscan(scan);
   13542         990 :     table_close(depRel, NoLock);
   13543         990 : }
   13544             : 
   13545             : /*
   13546             :  * Subroutine for ATExecAlterColumnType: remember that a replica identity
   13547             :  * needs to be reset.
   13548             :  */
   13549             : static void
   13550         416 : RememberReplicaIdentityForRebuilding(Oid indoid, AlteredTableInfo *tab)
   13551             : {
   13552         416 :     if (!get_index_isreplident(indoid))
   13553         398 :         return;
   13554             : 
   13555          18 :     if (tab->replicaIdentityIndex)
   13556           0 :         elog(ERROR, "relation %u has multiple indexes marked as replica identity", tab->relid);
   13557             : 
   13558          18 :     tab->replicaIdentityIndex = get_rel_name(indoid);
   13559             : }
   13560             : 
   13561             : /*
   13562             :  * Subroutine for ATExecAlterColumnType: remember any clustered index.
   13563             :  */
   13564             : static void
   13565         416 : RememberClusterOnForRebuilding(Oid indoid, AlteredTableInfo *tab)
   13566             : {
   13567         416 :     if (!get_index_isclustered(indoid))
   13568         398 :         return;
   13569             : 
   13570          18 :     if (tab->clusterOnIndex)
   13571           0 :         elog(ERROR, "relation %u has multiple clustered indexes", tab->relid);
   13572             : 
   13573          18 :     tab->clusterOnIndex = get_rel_name(indoid);
   13574             : }
   13575             : 
   13576             : /*
   13577             :  * Subroutine for ATExecAlterColumnType: remember that a constraint needs
   13578             :  * to be rebuilt (which we might already know).
   13579             :  */
   13580             : static void
   13581         464 : RememberConstraintForRebuilding(Oid conoid, AlteredTableInfo *tab)
   13582             : {
   13583             :     /*
   13584             :      * This de-duplication check is critical for two independent reasons: we
   13585             :      * mustn't try to recreate the same constraint twice, and if a constraint
   13586             :      * depends on more than one column whose type is to be altered, we must
   13587             :      * capture its definition string before applying any of the column type
   13588             :      * changes.  ruleutils.c will get confused if we ask again later.
   13589             :      */
   13590         464 :     if (!list_member_oid(tab->changedConstraintOids, conoid))
   13591             :     {
   13592             :         /* OK, capture the constraint's existing definition string */
   13593         374 :         char       *defstring = pg_get_constraintdef_command(conoid);
   13594             :         Oid         indoid;
   13595             : 
   13596         374 :         tab->changedConstraintOids = lappend_oid(tab->changedConstraintOids,
   13597             :                                                  conoid);
   13598         374 :         tab->changedConstraintDefs = lappend(tab->changedConstraintDefs,
   13599             :                                              defstring);
   13600             : 
   13601             :         /*
   13602             :          * For the index of a constraint, if any, remember if it is used for
   13603             :          * the table's replica identity or if it is a clustered index, so that
   13604             :          * ATPostAlterTypeCleanup() can queue up commands necessary to restore
   13605             :          * those properties.
   13606             :          */
   13607         374 :         indoid = get_constraint_index(conoid);
   13608         374 :         if (OidIsValid(indoid))
   13609             :         {
   13610         204 :             RememberReplicaIdentityForRebuilding(indoid, tab);
   13611         204 :             RememberClusterOnForRebuilding(indoid, tab);
   13612             :         }
   13613             :     }
   13614         464 : }
   13615             : 
   13616             : /*
   13617             :  * Subroutine for ATExecAlterColumnType: remember that an index needs
   13618             :  * to be rebuilt (which we might already know).
   13619             :  */
   13620             : static void
   13621         232 : RememberIndexForRebuilding(Oid indoid, AlteredTableInfo *tab)
   13622             : {
   13623             :     /*
   13624             :      * This de-duplication check is critical for two independent reasons: we
   13625             :      * mustn't try to recreate the same index twice, and if an index depends
   13626             :      * on more than one column whose type is to be altered, we must capture
   13627             :      * its definition string before applying any of the column type changes.
   13628             :      * ruleutils.c will get confused if we ask again later.
   13629             :      */
   13630         232 :     if (!list_member_oid(tab->changedIndexOids, indoid))
   13631             :     {
   13632             :         /*
   13633             :          * Before adding it as an index-to-rebuild, we'd better see if it
   13634             :          * belongs to a constraint, and if so rebuild the constraint instead.
   13635             :          * Typically this check fails, because constraint indexes normally
   13636             :          * have only dependencies on their constraint.  But it's possible for
   13637             :          * such an index to also have direct dependencies on table columns,
   13638             :          * for example with a partial exclusion constraint.
   13639             :          */
   13640         224 :         Oid         conoid = get_index_constraint(indoid);
   13641             : 
   13642         224 :         if (OidIsValid(conoid))
   13643             :         {
   13644          12 :             RememberConstraintForRebuilding(conoid, tab);
   13645             :         }
   13646             :         else
   13647             :         {
   13648             :             /* OK, capture the index's existing definition string */
   13649         212 :             char       *defstring = pg_get_indexdef_string(indoid);
   13650             : 
   13651         212 :             tab->changedIndexOids = lappend_oid(tab->changedIndexOids,
   13652             :                                                 indoid);
   13653         212 :             tab->changedIndexDefs = lappend(tab->changedIndexDefs,
   13654             :                                             defstring);
   13655             : 
   13656             :             /*
   13657             :              * Remember if this index is used for the table's replica identity
   13658             :              * or if it is a clustered index, so that ATPostAlterTypeCleanup()
   13659             :              * can queue up commands necessary to restore those properties.
   13660             :              */
   13661         212 :             RememberReplicaIdentityForRebuilding(indoid, tab);
   13662         212 :             RememberClusterOnForRebuilding(indoid, tab);
   13663             :         }
   13664             :     }
   13665         232 : }
   13666             : 
   13667             : /*
   13668             :  * Subroutine for ATExecAlterColumnType: remember that a statistics object
   13669             :  * needs to be rebuilt (which we might already know).
   13670             :  */
   13671             : static void
   13672          14 : RememberStatisticsForRebuilding(Oid stxoid, AlteredTableInfo *tab)
   13673             : {
   13674             :     /*
   13675             :      * This de-duplication check is critical for two independent reasons: we
   13676             :      * mustn't try to recreate the same statistics object twice, and if the
   13677             :      * statistics object depends on more than one column whose type is to be
   13678             :      * altered, we must capture its definition string before applying any of
   13679             :      * the type changes. ruleutils.c will get confused if we ask again later.
   13680             :      */
   13681          14 :     if (!list_member_oid(tab->changedStatisticsOids, stxoid))
   13682             :     {
   13683             :         /* OK, capture the statistics object's existing definition string */
   13684          14 :         char       *defstring = pg_get_statisticsobjdef_string(stxoid);
   13685             : 
   13686          14 :         tab->changedStatisticsOids = lappend_oid(tab->changedStatisticsOids,
   13687             :                                                  stxoid);
   13688          14 :         tab->changedStatisticsDefs = lappend(tab->changedStatisticsDefs,
   13689             :                                              defstring);
   13690             :     }
   13691          14 : }
   13692             : 
   13693             : /*
   13694             :  * Cleanup after we've finished all the ALTER TYPE or SET EXPRESSION
   13695             :  * operations for a particular relation.  We have to drop and recreate all the
   13696             :  * indexes and constraints that depend on the altered columns.  We do the
   13697             :  * actual dropping here, but re-creation is managed by adding work queue
   13698             :  * entries to do those steps later.
   13699             :  */
   13700             : static void
   13701         960 : ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab, LOCKMODE lockmode)
   13702             : {
   13703             :     ObjectAddress obj;
   13704             :     ObjectAddresses *objects;
   13705             :     ListCell   *def_item;
   13706             :     ListCell   *oid_item;
   13707             : 
   13708             :     /*
   13709             :      * Collect all the constraints and indexes to drop so we can process them
   13710             :      * in a single call.  That way we don't have to worry about dependencies
   13711             :      * among them.
   13712             :      */
   13713         960 :     objects = new_object_addresses();
   13714             : 
   13715             :     /*
   13716             :      * Re-parse the index and constraint definitions, and attach them to the
   13717             :      * appropriate work queue entries.  We do this before dropping because in
   13718             :      * the case of a FOREIGN KEY constraint, we might not yet have exclusive
   13719             :      * lock on the table the constraint is attached to, and we need to get
   13720             :      * that before reparsing/dropping.
   13721             :      *
   13722             :      * We can't rely on the output of deparsing to tell us which relation to
   13723             :      * operate on, because concurrent activity might have made the name
   13724             :      * resolve differently.  Instead, we've got to use the OID of the
   13725             :      * constraint or index we're processing to figure out which relation to
   13726             :      * operate on.
   13727             :      */
   13728        1334 :     forboth(oid_item, tab->changedConstraintOids,
   13729             :             def_item, tab->changedConstraintDefs)
   13730             :     {
   13731         374 :         Oid         oldId = lfirst_oid(oid_item);
   13732             :         HeapTuple   tup;
   13733             :         Form_pg_constraint con;
   13734             :         Oid         relid;
   13735             :         Oid         confrelid;
   13736             :         char        contype;
   13737             :         bool        conislocal;
   13738             : 
   13739         374 :         tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(oldId));
   13740         374 :         if (!HeapTupleIsValid(tup)) /* should not happen */
   13741           0 :             elog(ERROR, "cache lookup failed for constraint %u", oldId);
   13742         374 :         con = (Form_pg_constraint) GETSTRUCT(tup);
   13743         374 :         if (OidIsValid(con->conrelid))
   13744         360 :             relid = con->conrelid;
   13745             :         else
   13746             :         {
   13747             :             /* must be a domain constraint */
   13748          14 :             relid = get_typ_typrelid(getBaseType(con->contypid));
   13749          14 :             if (!OidIsValid(relid))
   13750           0 :                 elog(ERROR, "could not identify relation associated with constraint %u", oldId);
   13751             :         }
   13752         374 :         confrelid = con->confrelid;
   13753         374 :         contype = con->contype;
   13754         374 :         conislocal = con->conislocal;
   13755         374 :         ReleaseSysCache(tup);
   13756             : 
   13757         374 :         ObjectAddressSet(obj, ConstraintRelationId, oldId);
   13758         374 :         add_exact_object_address(&obj, objects);
   13759             : 
   13760             :         /*
   13761             :          * If the constraint is inherited (only), we don't want to inject a
   13762             :          * new definition here; it'll get recreated when ATAddCheckConstraint
   13763             :          * recurses from adding the parent table's constraint.  But we had to
   13764             :          * carry the info this far so that we can drop the constraint below.
   13765             :          */
   13766         374 :         if (!conislocal)
   13767          12 :             continue;
   13768             : 
   13769             :         /*
   13770             :          * When rebuilding an FK constraint that references the table we're
   13771             :          * modifying, we might not yet have any lock on the FK's table, so get
   13772             :          * one now.  We'll need AccessExclusiveLock for the DROP CONSTRAINT
   13773             :          * step, so there's no value in asking for anything weaker.
   13774             :          */
   13775         362 :         if (relid != tab->relid && contype == CONSTRAINT_FOREIGN)
   13776          36 :             LockRelationOid(relid, AccessExclusiveLock);
   13777             : 
   13778         362 :         ATPostAlterTypeParse(oldId, relid, confrelid,
   13779         362 :                              (char *) lfirst(def_item),
   13780         362 :                              wqueue, lockmode, tab->rewrite);
   13781             :     }
   13782        1172 :     forboth(oid_item, tab->changedIndexOids,
   13783             :             def_item, tab->changedIndexDefs)
   13784             :     {
   13785         212 :         Oid         oldId = lfirst_oid(oid_item);
   13786             :         Oid         relid;
   13787             : 
   13788         212 :         relid = IndexGetRelation(oldId, false);
   13789         212 :         ATPostAlterTypeParse(oldId, relid, InvalidOid,
   13790         212 :                              (char *) lfirst(def_item),
   13791         212 :                              wqueue, lockmode, tab->rewrite);
   13792             : 
   13793         212 :         ObjectAddressSet(obj, RelationRelationId, oldId);
   13794         212 :         add_exact_object_address(&obj, objects);
   13795             :     }
   13796             : 
   13797             :     /* add dependencies for new statistics */
   13798         974 :     forboth(oid_item, tab->changedStatisticsOids,
   13799             :             def_item, tab->changedStatisticsDefs)
   13800             :     {
   13801          14 :         Oid         oldId = lfirst_oid(oid_item);
   13802             :         Oid         relid;
   13803             : 
   13804          14 :         relid = StatisticsGetRelation(oldId, false);
   13805          14 :         ATPostAlterTypeParse(oldId, relid, InvalidOid,
   13806          14 :                              (char *) lfirst(def_item),
   13807          14 :                              wqueue, lockmode, tab->rewrite);
   13808             : 
   13809          14 :         ObjectAddressSet(obj, StatisticExtRelationId, oldId);
   13810          14 :         add_exact_object_address(&obj, objects);
   13811             :     }
   13812             : 
   13813             :     /*
   13814             :      * Queue up command to restore replica identity index marking
   13815             :      */
   13816         960 :     if (tab->replicaIdentityIndex)
   13817             :     {
   13818          18 :         AlterTableCmd *cmd = makeNode(AlterTableCmd);
   13819          18 :         ReplicaIdentityStmt *subcmd = makeNode(ReplicaIdentityStmt);
   13820             : 
   13821          18 :         subcmd->identity_type = REPLICA_IDENTITY_INDEX;
   13822          18 :         subcmd->name = tab->replicaIdentityIndex;
   13823          18 :         cmd->subtype = AT_ReplicaIdentity;
   13824          18 :         cmd->def = (Node *) subcmd;
   13825             : 
   13826             :         /* do it after indexes and constraints */
   13827          18 :         tab->subcmds[AT_PASS_OLD_CONSTR] =
   13828          18 :             lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
   13829             :     }
   13830             : 
   13831             :     /*
   13832             :      * Queue up command to restore marking of index used for cluster.
   13833             :      */
   13834         960 :     if (tab->clusterOnIndex)
   13835             :     {
   13836          18 :         AlterTableCmd *cmd = makeNode(AlterTableCmd);
   13837             : 
   13838          18 :         cmd->subtype = AT_ClusterOn;
   13839          18 :         cmd->name = tab->clusterOnIndex;
   13840             : 
   13841             :         /* do it after indexes and constraints */
   13842          18 :         tab->subcmds[AT_PASS_OLD_CONSTR] =
   13843          18 :             lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
   13844             :     }
   13845             : 
   13846             :     /*
   13847             :      * It should be okay to use DROP_RESTRICT here, since nothing else should
   13848             :      * be depending on these objects.
   13849             :      */
   13850         960 :     performMultipleDeletions(objects, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
   13851             : 
   13852         960 :     free_object_addresses(objects);
   13853             : 
   13854             :     /*
   13855             :      * The objects will get recreated during subsequent passes over the work
   13856             :      * queue.
   13857             :      */
   13858         960 : }
   13859             : 
   13860             : /*
   13861             :  * Parse the previously-saved definition string for a constraint, index or
   13862             :  * statistics object against the newly-established column data type(s), and
   13863             :  * queue up the resulting command parsetrees for execution.
   13864             :  *
   13865             :  * This might fail if, for example, you have a WHERE clause that uses an
   13866             :  * operator that's not available for the new column type.
   13867             :  */
   13868             : static void
   13869         588 : ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
   13870             :                      List **wqueue, LOCKMODE lockmode, bool rewrite)
   13871             : {
   13872             :     List       *raw_parsetree_list;
   13873             :     List       *querytree_list;
   13874             :     ListCell   *list_item;
   13875             :     Relation    rel;
   13876             : 
   13877             :     /*
   13878             :      * We expect that we will get only ALTER TABLE and CREATE INDEX
   13879             :      * statements. Hence, there is no need to pass them through
   13880             :      * parse_analyze_*() or the rewriter, but instead we need to pass them
   13881             :      * through parse_utilcmd.c to make them ready for execution.
   13882             :      */
   13883         588 :     raw_parsetree_list = raw_parser(cmd, RAW_PARSE_DEFAULT);
   13884         588 :     querytree_list = NIL;
   13885        1176 :     foreach(list_item, raw_parsetree_list)
   13886             :     {
   13887         588 :         RawStmt    *rs = lfirst_node(RawStmt, list_item);
   13888         588 :         Node       *stmt = rs->stmt;
   13889             : 
   13890         588 :         if (IsA(stmt, IndexStmt))
   13891         212 :             querytree_list = lappend(querytree_list,
   13892         212 :                                      transformIndexStmt(oldRelId,
   13893             :                                                         (IndexStmt *) stmt,
   13894             :                                                         cmd));
   13895         376 :         else if (IsA(stmt, AlterTableStmt))
   13896             :         {
   13897             :             List       *beforeStmts;
   13898             :             List       *afterStmts;
   13899             : 
   13900         348 :             stmt = (Node *) transformAlterTableStmt(oldRelId,
   13901             :                                                     (AlterTableStmt *) stmt,
   13902             :                                                     cmd,
   13903             :                                                     &beforeStmts,
   13904             :                                                     &afterStmts);
   13905         348 :             querytree_list = list_concat(querytree_list, beforeStmts);
   13906         348 :             querytree_list = lappend(querytree_list, stmt);
   13907         348 :             querytree_list = list_concat(querytree_list, afterStmts);
   13908             :         }
   13909          28 :         else if (IsA(stmt, CreateStatsStmt))
   13910          14 :             querytree_list = lappend(querytree_list,
   13911          14 :                                      transformStatsStmt(oldRelId,
   13912             :                                                         (CreateStatsStmt *) stmt,
   13913             :                                                         cmd));
   13914             :         else
   13915          14 :             querytree_list = lappend(querytree_list, stmt);
   13916             :     }
   13917             : 
   13918             :     /* Caller should already have acquired whatever lock we need. */
   13919         588 :     rel = relation_open(oldRelId, NoLock);
   13920             : 
   13921             :     /*
   13922             :      * Attach each generated command to the proper place in the work queue.
   13923             :      * Note this could result in creation of entirely new work-queue entries.
   13924             :      *
   13925             :      * Also note that we have to tweak the command subtypes, because it turns
   13926             :      * out that re-creation of indexes and constraints has to act a bit
   13927             :      * differently from initial creation.
   13928             :      */
   13929        1176 :     foreach(list_item, querytree_list)
   13930             :     {
   13931         588 :         Node       *stm = (Node *) lfirst(list_item);
   13932             :         AlteredTableInfo *tab;
   13933             : 
   13934         588 :         tab = ATGetQueueEntry(wqueue, rel);
   13935             : 
   13936         588 :         if (IsA(stm, IndexStmt))
   13937             :         {
   13938         212 :             IndexStmt  *stmt = (IndexStmt *) stm;
   13939             :             AlterTableCmd *newcmd;
   13940             : 
   13941         212 :             if (!rewrite)
   13942          54 :                 TryReuseIndex(oldId, stmt);
   13943         212 :             stmt->reset_default_tblspc = true;
   13944             :             /* keep the index's comment */
   13945         212 :             stmt->idxcomment = GetComment(oldId, RelationRelationId, 0);
   13946             : 
   13947         212 :             newcmd = makeNode(AlterTableCmd);
   13948         212 :             newcmd->subtype = AT_ReAddIndex;
   13949         212 :             newcmd->def = (Node *) stmt;
   13950         212 :             tab->subcmds[AT_PASS_OLD_INDEX] =
   13951         212 :                 lappend(tab->subcmds[AT_PASS_OLD_INDEX], newcmd);
   13952             :         }
   13953         376 :         else if (IsA(stm, AlterTableStmt))
   13954             :         {
   13955         348 :             AlterTableStmt *stmt = (AlterTableStmt *) stm;
   13956             :             ListCell   *lcmd;
   13957             : 
   13958         816 :             foreach(lcmd, stmt->cmds)
   13959             :             {
   13960         468 :                 AlterTableCmd *cmd = lfirst_node(AlterTableCmd, lcmd);
   13961             : 
   13962         468 :                 if (cmd->subtype == AT_AddIndex)
   13963             :                 {
   13964             :                     IndexStmt  *indstmt;
   13965             :                     Oid         indoid;
   13966             : 
   13967         204 :                     indstmt = castNode(IndexStmt, cmd->def);
   13968         204 :                     indoid = get_constraint_index(oldId);
   13969             : 
   13970         204 :                     if (!rewrite)
   13971          48 :                         TryReuseIndex(indoid, indstmt);
   13972             :                     /* keep any comment on the index */
   13973         204 :                     indstmt->idxcomment = GetComment(indoid,
   13974             :                                                      RelationRelationId, 0);
   13975         204 :                     indstmt->reset_default_tblspc = true;
   13976             : 
   13977         204 :                     cmd->subtype = AT_ReAddIndex;
   13978         204 :                     tab->subcmds[AT_PASS_OLD_INDEX] =
   13979         204 :                         lappend(tab->subcmds[AT_PASS_OLD_INDEX], cmd);
   13980             : 
   13981             :                     /* recreate any comment on the constraint */
   13982         204 :                     RebuildConstraintComment(tab,
   13983             :                                              AT_PASS_OLD_INDEX,
   13984             :                                              oldId,
   13985             :                                              rel,
   13986             :                                              NIL,
   13987         204 :                                              indstmt->idxname);
   13988             :                 }
   13989         264 :                 else if (cmd->subtype == AT_AddConstraint)
   13990             :                 {
   13991         144 :                     Constraint *con = castNode(Constraint, cmd->def);
   13992             : 
   13993         144 :                     con->old_pktable_oid = refRelId;
   13994             :                     /* rewriting neither side of a FK */
   13995         144 :                     if (con->contype == CONSTR_FOREIGN &&
   13996          72 :                         !rewrite && tab->rewrite == 0)
   13997           6 :                         TryReuseForeignKey(oldId, con);
   13998         144 :                     con->reset_default_tblspc = true;
   13999         144 :                     cmd->subtype = AT_ReAddConstraint;
   14000         144 :                     tab->subcmds[AT_PASS_OLD_CONSTR] =
   14001         144 :                         lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
   14002             : 
   14003             :                     /* recreate any comment on the constraint */
   14004         144 :                     RebuildConstraintComment(tab,
   14005             :                                              AT_PASS_OLD_CONSTR,
   14006             :                                              oldId,
   14007             :                                              rel,
   14008             :                                              NIL,
   14009         144 :                                              con->conname);
   14010             :                 }
   14011         120 :                 else if (cmd->subtype == AT_SetNotNull)
   14012             :                 {
   14013             :                     /*
   14014             :                      * The parser will create AT_SetNotNull subcommands for
   14015             :                      * columns of PRIMARY KEY indexes/constraints, but we need
   14016             :                      * not do anything with them here, because the columns'
   14017             :                      * NOT NULL marks will already have been propagated into
   14018             :                      * the new table definition.
   14019             :                      */
   14020             :                 }
   14021             :                 else
   14022           0 :                     elog(ERROR, "unexpected statement subtype: %d",
   14023             :                          (int) cmd->subtype);
   14024             :             }
   14025             :         }
   14026          28 :         else if (IsA(stm, AlterDomainStmt))
   14027             :         {
   14028          14 :             AlterDomainStmt *stmt = (AlterDomainStmt *) stm;
   14029             : 
   14030          14 :             if (stmt->subtype == 'C')    /* ADD CONSTRAINT */
   14031             :             {
   14032          14 :                 Constraint *con = castNode(Constraint, stmt->def);
   14033          14 :                 AlterTableCmd *cmd = makeNode(AlterTableCmd);
   14034             : 
   14035          14 :                 cmd->subtype = AT_ReAddDomainConstraint;
   14036          14 :                 cmd->def = (Node *) stmt;
   14037          14 :                 tab->subcmds[AT_PASS_OLD_CONSTR] =
   14038          14 :                     lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
   14039             : 
   14040             :                 /* recreate any comment on the constraint */
   14041          14 :                 RebuildConstraintComment(tab,
   14042             :                                          AT_PASS_OLD_CONSTR,
   14043             :                                          oldId,
   14044             :                                          NULL,
   14045             :                                          stmt->typeName,
   14046          14 :                                          con->conname);
   14047             :             }
   14048             :             else
   14049           0 :                 elog(ERROR, "unexpected statement subtype: %d",
   14050             :                      (int) stmt->subtype);
   14051             :         }
   14052          14 :         else if (IsA(stm, CreateStatsStmt))
   14053             :         {
   14054          14 :             CreateStatsStmt *stmt = (CreateStatsStmt *) stm;
   14055             :             AlterTableCmd *newcmd;
   14056             : 
   14057             :             /* keep the statistics object's comment */
   14058          14 :             stmt->stxcomment = GetComment(oldId, StatisticExtRelationId, 0);
   14059             : 
   14060          14 :             newcmd = makeNode(AlterTableCmd);
   14061          14 :             newcmd->subtype = AT_ReAddStatistics;
   14062          14 :             newcmd->def = (Node *) stmt;
   14063          14 :             tab->subcmds[AT_PASS_MISC] =
   14064          14 :                 lappend(tab->subcmds[AT_PASS_MISC], newcmd);
   14065             :         }
   14066             :         else
   14067           0 :             elog(ERROR, "unexpected statement type: %d",
   14068             :                  (int) nodeTag(stm));
   14069             :     }
   14070             : 
   14071         588 :     relation_close(rel, NoLock);
   14072         588 : }
   14073             : 
   14074             : /*
   14075             :  * Subroutine for ATPostAlterTypeParse() to recreate any existing comment
   14076             :  * for a table or domain constraint that is being rebuilt.
   14077             :  *
   14078             :  * objid is the OID of the constraint.
   14079             :  * Pass "rel" for a table constraint, or "domname" (domain's qualified name
   14080             :  * as a string list) for a domain constraint.
   14081             :  * (We could dig that info, as well as the conname, out of the pg_constraint
   14082             :  * entry; but callers already have them so might as well pass them.)
   14083             :  */
   14084             : static void
   14085         362 : RebuildConstraintComment(AlteredTableInfo *tab, AlterTablePass pass, Oid objid,
   14086             :                          Relation rel, List *domname,
   14087             :                          const char *conname)
   14088             : {
   14089             :     CommentStmt *cmd;
   14090             :     char       *comment_str;
   14091             :     AlterTableCmd *newcmd;
   14092             : 
   14093             :     /* Look for comment for object wanted, and leave if none */
   14094         362 :     comment_str = GetComment(objid, ConstraintRelationId, 0);
   14095         362 :     if (comment_str == NULL)
   14096         296 :         return;
   14097             : 
   14098             :     /* Build CommentStmt node, copying all input data for safety */
   14099          66 :     cmd = makeNode(CommentStmt);
   14100          66 :     if (rel)
   14101             :     {
   14102          54 :         cmd->objtype = OBJECT_TABCONSTRAINT;
   14103          54 :         cmd->object = (Node *)
   14104          54 :             list_make3(makeString(get_namespace_name(RelationGetNamespace(rel))),
   14105             :                        makeString(pstrdup(RelationGetRelationName(rel))),
   14106             :                        makeString(pstrdup(conname)));
   14107             :     }
   14108             :     else
   14109             :     {
   14110          12 :         cmd->objtype = OBJECT_DOMCONSTRAINT;
   14111          12 :         cmd->object = (Node *)
   14112          12 :             list_make2(makeTypeNameFromNameList(copyObject(domname)),
   14113             :                        makeString(pstrdup(conname)));
   14114             :     }
   14115          66 :     cmd->comment = comment_str;
   14116             : 
   14117             :     /* Append it to list of commands */
   14118          66 :     newcmd = makeNode(AlterTableCmd);
   14119          66 :     newcmd->subtype = AT_ReAddComment;
   14120          66 :     newcmd->def = (Node *) cmd;
   14121          66 :     tab->subcmds[pass] = lappend(tab->subcmds[pass], newcmd);
   14122             : }
   14123             : 
   14124             : /*
   14125             :  * Subroutine for ATPostAlterTypeParse().  Calls out to CheckIndexCompatible()
   14126             :  * for the real analysis, then mutates the IndexStmt based on that verdict.
   14127             :  */
   14128             : static void
   14129         102 : TryReuseIndex(Oid oldId, IndexStmt *stmt)
   14130             : {
   14131         102 :     if (CheckIndexCompatible(oldId,
   14132         102 :                              stmt->accessMethod,
   14133         102 :                              stmt->indexParams,
   14134         102 :                              stmt->excludeOpNames))
   14135             :     {
   14136         102 :         Relation    irel = index_open(oldId, NoLock);
   14137             : 
   14138             :         /* If it's a partitioned index, there is no storage to share. */
   14139         102 :         if (irel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
   14140             :         {
   14141          72 :             stmt->oldNumber = irel->rd_locator.relNumber;
   14142          72 :             stmt->oldCreateSubid = irel->rd_createSubid;
   14143          72 :             stmt->oldFirstRelfilelocatorSubid = irel->rd_firstRelfilelocatorSubid;
   14144             :         }
   14145         102 :         index_close(irel, NoLock);
   14146             :     }
   14147         102 : }
   14148             : 
   14149             : /*
   14150             :  * Subroutine for ATPostAlterTypeParse().
   14151             :  *
   14152             :  * Stash the old P-F equality operator into the Constraint node, for possible
   14153             :  * use by ATAddForeignKeyConstraint() in determining whether revalidation of
   14154             :  * this constraint can be skipped.
   14155             :  */
   14156             : static void
   14157           6 : TryReuseForeignKey(Oid oldId, Constraint *con)
   14158             : {
   14159             :     HeapTuple   tup;
   14160             :     Datum       adatum;
   14161             :     ArrayType  *arr;
   14162             :     Oid        *rawarr;
   14163             :     int         numkeys;
   14164             :     int         i;
   14165             : 
   14166             :     Assert(con->contype == CONSTR_FOREIGN);
   14167             :     Assert(con->old_conpfeqop == NIL);   /* already prepared this node */
   14168             : 
   14169           6 :     tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(oldId));
   14170           6 :     if (!HeapTupleIsValid(tup)) /* should not happen */
   14171           0 :         elog(ERROR, "cache lookup failed for constraint %u", oldId);
   14172             : 
   14173           6 :     adatum = SysCacheGetAttrNotNull(CONSTROID, tup,
   14174             :                                     Anum_pg_constraint_conpfeqop);
   14175           6 :     arr = DatumGetArrayTypeP(adatum);   /* ensure not toasted */
   14176           6 :     numkeys = ARR_DIMS(arr)[0];
   14177             :     /* test follows the one in ri_FetchConstraintInfo() */
   14178           6 :     if (ARR_NDIM(arr) != 1 ||
   14179           6 :         ARR_HASNULL(arr) ||
   14180           6 :         ARR_ELEMTYPE(arr) != OIDOID)
   14181           0 :         elog(ERROR, "conpfeqop is not a 1-D Oid array");
   14182           6 :     rawarr = (Oid *) ARR_DATA_PTR(arr);
   14183             : 
   14184             :     /* stash a List of the operator Oids in our Constraint node */
   14185          12 :     for (i = 0; i < numkeys; i++)
   14186           6 :         con->old_conpfeqop = lappend_oid(con->old_conpfeqop, rawarr[i]);
   14187             : 
   14188           6 :     ReleaseSysCache(tup);
   14189           6 : }
   14190             : 
   14191             : /*
   14192             :  * ALTER COLUMN .. OPTIONS ( ... )
   14193             :  *
   14194             :  * Returns the address of the modified column
   14195             :  */
   14196             : static ObjectAddress
   14197         164 : ATExecAlterColumnGenericOptions(Relation rel,
   14198             :                                 const char *colName,
   14199             :                                 List *options,
   14200             :                                 LOCKMODE lockmode)
   14201             : {
   14202             :     Relation    ftrel;
   14203             :     Relation    attrel;
   14204             :     ForeignServer *server;
   14205             :     ForeignDataWrapper *fdw;
   14206             :     HeapTuple   tuple;
   14207             :     HeapTuple   newtuple;
   14208             :     bool        isnull;
   14209             :     Datum       repl_val[Natts_pg_attribute];
   14210             :     bool        repl_null[Natts_pg_attribute];
   14211             :     bool        repl_repl[Natts_pg_attribute];
   14212             :     Datum       datum;
   14213             :     Form_pg_foreign_table fttableform;
   14214             :     Form_pg_attribute atttableform;
   14215             :     AttrNumber  attnum;
   14216             :     ObjectAddress address;
   14217             : 
   14218         164 :     if (options == NIL)
   14219           0 :         return InvalidObjectAddress;
   14220             : 
   14221             :     /* First, determine FDW validator associated to the foreign table. */
   14222         164 :     ftrel = table_open(ForeignTableRelationId, AccessShareLock);
   14223         164 :     tuple = SearchSysCache1(FOREIGNTABLEREL, ObjectIdGetDatum(rel->rd_id));
   14224         164 :     if (!HeapTupleIsValid(tuple))
   14225           0 :         ereport(ERROR,
   14226             :                 (errcode(ERRCODE_UNDEFINED_OBJECT),
   14227             :                  errmsg("foreign table \"%s\" does not exist",
   14228             :                         RelationGetRelationName(rel))));
   14229         164 :     fttableform = (Form_pg_foreign_table) GETSTRUCT(tuple);
   14230         164 :     server = GetForeignServer(fttableform->ftserver);
   14231         164 :     fdw = GetForeignDataWrapper(server->fdwid);
   14232             : 
   14233         164 :     table_close(ftrel, AccessShareLock);
   14234         164 :     ReleaseSysCache(tuple);
   14235             : 
   14236         164 :     attrel = table_open(AttributeRelationId, RowExclusiveLock);
   14237         164 :     tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
   14238         164 :     if (!HeapTupleIsValid(tuple))
   14239           0 :         ereport(ERROR,
   14240             :                 (errcode(ERRCODE_UNDEFINED_COLUMN),
   14241             :                  errmsg("column \"%s\" of relation \"%s\" does not exist",
   14242             :                         colName, RelationGetRelationName(rel))));
   14243             : 
   14244             :     /* Prevent them from altering a system attribute */
   14245         164 :     atttableform = (Form_pg_attribute) GETSTRUCT(tuple);
   14246         164 :     attnum = atttableform->attnum;
   14247         164 :     if (attnum <= 0)
   14248           6 :         ereport(ERROR,
   14249             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   14250             :                  errmsg("cannot alter system column \"%s\"", colName)));
   14251             : 
   14252             : 
   14253             :     /* Initialize buffers for new tuple values */
   14254         158 :     memset(repl_val, 0, sizeof(repl_val));
   14255         158 :     memset(repl_null, false, sizeof(repl_null));
   14256         158 :     memset(repl_repl, false, sizeof(repl_repl));
   14257             : 
   14258             :     /* Extract the current options */
   14259         158 :     datum = SysCacheGetAttr(ATTNAME,
   14260             :                             tuple,
   14261             :                             Anum_pg_attribute_attfdwoptions,
   14262             :                             &isnull);
   14263         158 :     if (isnull)
   14264         148 :         datum = PointerGetDatum(NULL);
   14265             : 
   14266             :     /* Transform the options */
   14267         158 :     datum = transformGenericOptions(AttributeRelationId,
   14268             :                                     datum,
   14269             :                                     options,
   14270             :                                     fdw->fdwvalidator);
   14271             : 
   14272         158 :     if (PointerIsValid(DatumGetPointer(datum)))
   14273         158 :         repl_val[Anum_pg_attribute_attfdwoptions - 1] = datum;
   14274             :     else
   14275           0 :         repl_null[Anum_pg_attribute_attfdwoptions - 1] = true;
   14276             : 
   14277         158 :     repl_repl[Anum_pg_attribute_attfdwoptions - 1] = true;
   14278             : 
   14279             :     /* Everything looks good - update the tuple */
   14280             : 
   14281         158 :     newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrel),
   14282             :                                  repl_val, repl_null, repl_repl);
   14283             : 
   14284         158 :     CatalogTupleUpdate(attrel, &newtuple->t_self, newtuple);
   14285             : 
   14286         158 :     InvokeObjectPostAlterHook(RelationRelationId,
   14287             :                               RelationGetRelid(rel),
   14288             :                               atttableform->attnum);
   14289         158 :     ObjectAddressSubSet(address, RelationRelationId,
   14290             :                         RelationGetRelid(rel), attnum);
   14291             : 
   14292         158 :     ReleaseSysCache(tuple);
   14293             : 
   14294         158 :     table_close(attrel, RowExclusiveLock);
   14295             : 
   14296         158 :     heap_freetuple(newtuple);
   14297             : 
   14298         158 :     return address;
   14299             : }
   14300             : 
   14301             : /*
   14302             :  * ALTER TABLE OWNER
   14303             :  *
   14304             :  * recursing is true if we are recursing from a table to its indexes,
   14305             :  * sequences, or toast table.  We don't allow the ownership of those things to
   14306             :  * be changed separately from the parent table.  Also, we can skip permission
   14307             :  * checks (this is necessary not just an optimization, else we'd fail to
   14308             :  * handle toast tables properly).
   14309             :  *
   14310             :  * recursing is also true if ALTER TYPE OWNER is calling us to fix up a
   14311             :  * free-standing composite type.
   14312             :  */
   14313             : void
   14314        1996 : ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing, LOCKMODE lockmode)
   14315             : {
   14316             :     Relation    target_rel;
   14317             :     Relation    class_rel;
   14318             :     HeapTuple   tuple;
   14319             :     Form_pg_class tuple_class;
   14320             : 
   14321             :     /*
   14322             :      * Get exclusive lock till end of transaction on the target table. Use
   14323             :      * relation_open so that we can work on indexes and sequences.
   14324             :      */
   14325        1996 :     target_rel = relation_open(relationOid, lockmode);
   14326             : 
   14327             :     /* Get its pg_class tuple, too */
   14328        1996 :     class_rel = table_open(RelationRelationId, RowExclusiveLock);
   14329             : 
   14330        1996 :     tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relationOid));
   14331        1996 :     if (!HeapTupleIsValid(tuple))
   14332           0 :         elog(ERROR, "cache lookup failed for relation %u", relationOid);
   14333        1996 :     tuple_class = (Form_pg_class) GETSTRUCT(tuple);
   14334             : 
   14335             :     /* Can we change the ownership of this tuple? */
   14336        1996 :     switch (tuple_class->relkind)
   14337             :     {
   14338        1730 :         case RELKIND_RELATION:
   14339             :         case RELKIND_VIEW:
   14340             :         case RELKIND_MATVIEW:
   14341             :         case RELKIND_FOREIGN_TABLE:
   14342             :         case RELKIND_PARTITIONED_TABLE:
   14343             :             /* ok to change owner */
   14344        1730 :             break;
   14345          96 :         case RELKIND_INDEX:
   14346          96 :             if (!recursing)
   14347             :             {
   14348             :                 /*
   14349             :                  * Because ALTER INDEX OWNER used to be allowed, and in fact
   14350             :                  * is generated by old versions of pg_dump, we give a warning
   14351             :                  * and do nothing rather than erroring out.  Also, to avoid
   14352             :                  * unnecessary chatter while restoring those old dumps, say
   14353             :                  * nothing at all if the command would be a no-op anyway.
   14354             :                  */
   14355           0 :                 if (tuple_class->relowner != newOwnerId)
   14356           0 :                     ereport(WARNING,
   14357             :                             (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   14358             :                              errmsg("cannot change owner of index \"%s\"",
   14359             :                                     NameStr(tuple_class->relname)),
   14360             :                              errhint("Change the ownership of the index's table instead.")));
   14361             :                 /* quick hack to exit via the no-op path */
   14362           0 :                 newOwnerId = tuple_class->relowner;
   14363             :             }
   14364          96 :             break;
   14365          20 :         case RELKIND_PARTITIONED_INDEX:
   14366          20 :             if (recursing)
   14367          20 :                 break;
   14368           0 :             ereport(ERROR,
   14369             :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   14370             :                      errmsg("cannot change owner of index \"%s\"",
   14371             :                             NameStr(tuple_class->relname)),
   14372             :                      errhint("Change the ownership of the index's table instead.")));
   14373             :             break;
   14374         100 :         case RELKIND_SEQUENCE:
   14375         100 :             if (!recursing &&
   14376          52 :                 tuple_class->relowner != newOwnerId)
   14377             :             {
   14378             :                 /* if it's an owned sequence, disallow changing it by itself */
   14379             :                 Oid         tableId;
   14380             :                 int32       colId;
   14381             : 
   14382           0 :                 if (sequenceIsOwned(relationOid, DEPENDENCY_AUTO, &tableId, &colId) ||
   14383           0 :                     sequenceIsOwned(relationOid, DEPENDENCY_INTERNAL, &tableId, &colId))
   14384           0 :                     ereport(ERROR,
   14385             :                             (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   14386             :                              errmsg("cannot change owner of sequence \"%s\"",
   14387             :                                     NameStr(tuple_class->relname)),
   14388             :                              errdetail("Sequence \"%s\" is linked to table \"%s\".",
   14389             :                                        NameStr(tuple_class->relname),
   14390             :                                        get_rel_name(tableId))));
   14391             :             }
   14392         100 :             break;
   14393           8 :         case RELKIND_COMPOSITE_TYPE:
   14394           8 :             if (recursing)
   14395           8 :                 break;
   14396           0 :             ereport(ERROR,
   14397             :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   14398             :                      errmsg("\"%s\" is a composite type",
   14399             :                             NameStr(tuple_class->relname)),
   14400             :             /* translator: %s is an SQL ALTER command */
   14401             :                      errhint("Use %s instead.",
   14402             :                              "ALTER TYPE")));
   14403             :             break;
   14404          42 :         case RELKIND_TOASTVALUE:
   14405          42 :             if (recursing)
   14406          42 :                 break;
   14407             :             /* FALL THRU */
   14408             :         default:
   14409           0 :             ereport(ERROR,
   14410             :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   14411             :                      errmsg("cannot change owner of relation \"%s\"",
   14412             :                             NameStr(tuple_class->relname)),
   14413             :                      errdetail_relkind_not_supported(tuple_class->relkind)));
   14414             :     }
   14415             : 
   14416             :     /*
   14417             :      * If the new owner is the same as the existing owner, consider the
   14418             :      * command to have succeeded.  This is for dump restoration purposes.
   14419             :      */
   14420        1996 :     if (tuple_class->relowner != newOwnerId)
   14421             :     {
   14422             :         Datum       repl_val[Natts_pg_class];
   14423             :         bool        repl_null[Natts_pg_class];
   14424             :         bool        repl_repl[Natts_pg_class];
   14425             :         Acl        *newAcl;
   14426             :         Datum       aclDatum;
   14427             :         bool        isNull;
   14428             :         HeapTuple   newtuple;
   14429             : 
   14430             :         /* skip permission checks when recursing to index or toast table */
   14431         528 :         if (!recursing)
   14432             :         {
   14433             :             /* Superusers can always do it */
   14434         310 :             if (!superuser())
   14435             :             {
   14436          42 :                 Oid         namespaceOid = tuple_class->relnamespace;
   14437             :                 AclResult   aclresult;
   14438             : 
   14439             :                 /* Otherwise, must be owner of the existing object */
   14440          42 :                 if (!object_ownercheck(RelationRelationId, relationOid, GetUserId()))
   14441           0 :                     aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relationOid)),
   14442           0 :                                    RelationGetRelationName(target_rel));
   14443             : 
   14444             :                 /* Must be able to become new owner */
   14445          42 :                 check_can_set_role(GetUserId(), newOwnerId);
   14446             : 
   14447             :                 /* New owner must have CREATE privilege on namespace */
   14448          30 :                 aclresult = object_aclcheck(NamespaceRelationId, namespaceOid, newOwnerId,
   14449             :                                             ACL_CREATE);
   14450          30 :                 if (aclresult != ACLCHECK_OK)
   14451           0 :                     aclcheck_error(aclresult, OBJECT_SCHEMA,
   14452           0 :                                    get_namespace_name(namespaceOid));
   14453             :             }
   14454             :         }
   14455             : 
   14456         516 :         memset(repl_null, false, sizeof(repl_null));
   14457         516 :         memset(repl_repl, false, sizeof(repl_repl));
   14458             : 
   14459         516 :         repl_repl[Anum_pg_class_relowner - 1] = true;
   14460         516 :         repl_val[Anum_pg_class_relowner - 1] = ObjectIdGetDatum(newOwnerId);
   14461             : 
   14462             :         /*
   14463             :          * Determine the modified ACL for the new owner.  This is only
   14464             :          * necessary when the ACL is non-null.
   14465             :          */
   14466         516 :         aclDatum = SysCacheGetAttr(RELOID, tuple,
   14467             :                                    Anum_pg_class_relacl,
   14468             :                                    &isNull);
   14469         516 :         if (!isNull)
   14470             :         {
   14471          46 :             newAcl = aclnewowner(DatumGetAclP(aclDatum),
   14472             :                                  tuple_class->relowner, newOwnerId);
   14473          46 :             repl_repl[Anum_pg_class_relacl - 1] = true;
   14474          46 :             repl_val[Anum_pg_class_relacl - 1] = PointerGetDatum(newAcl);
   14475             :         }
   14476             : 
   14477         516 :         newtuple = heap_modify_tuple(tuple, RelationGetDescr(class_rel), repl_val, repl_null, repl_repl);
   14478             : 
   14479         516 :         CatalogTupleUpdate(class_rel, &newtuple->t_self, newtuple);
   14480             : 
   14481         516 :         heap_freetuple(newtuple);
   14482             : 
   14483             :         /*
   14484             :          * We must similarly update any per-column ACLs to reflect the new
   14485             :          * owner; for neatness reasons that's split out as a subroutine.
   14486             :          */
   14487         516 :         change_owner_fix_column_acls(relationOid,
   14488             :                                      tuple_class->relowner,
   14489             :                                      newOwnerId);
   14490             : 
   14491             :         /*
   14492             :          * Update owner dependency reference, if any.  A composite type has
   14493             :          * none, because it's tracked for the pg_type entry instead of here;
   14494             :          * indexes and TOAST tables don't have their own entries either.
   14495             :          */
   14496         516 :         if (tuple_class->relkind != RELKIND_COMPOSITE_TYPE &&
   14497         508 :             tuple_class->relkind != RELKIND_INDEX &&
   14498         412 :             tuple_class->relkind != RELKIND_PARTITIONED_INDEX &&
   14499         392 :             tuple_class->relkind != RELKIND_TOASTVALUE)
   14500         350 :             changeDependencyOnOwner(RelationRelationId, relationOid,
   14501             :                                     newOwnerId);
   14502             : 
   14503             :         /*
   14504             :          * Also change the ownership of the table's row type, if it has one
   14505             :          */
   14506         516 :         if (OidIsValid(tuple_class->reltype))
   14507         324 :             AlterTypeOwnerInternal(tuple_class->reltype, newOwnerId);
   14508             : 
   14509             :         /*
   14510             :          * If we are operating on a table or materialized view, also change
   14511             :          * the ownership of any indexes and sequences that belong to the
   14512             :          * relation, as well as its toast table (if it has one).
   14513             :          */
   14514         516 :         if (tuple_class->relkind == RELKIND_RELATION ||
   14515         274 :             tuple_class->relkind == RELKIND_PARTITIONED_TABLE ||
   14516         224 :             tuple_class->relkind == RELKIND_MATVIEW ||
   14517         224 :             tuple_class->relkind == RELKIND_TOASTVALUE)
   14518             :         {
   14519             :             List       *index_oid_list;
   14520             :             ListCell   *i;
   14521             : 
   14522             :             /* Find all the indexes belonging to this relation */
   14523         334 :             index_oid_list = RelationGetIndexList(target_rel);
   14524             : 
   14525             :             /* For each index, recursively change its ownership */
   14526         450 :             foreach(i, index_oid_list)
   14527         116 :                 ATExecChangeOwner(lfirst_oid(i), newOwnerId, true, lockmode);
   14528             : 
   14529         334 :             list_free(index_oid_list);
   14530             :         }
   14531             : 
   14532             :         /* If it has a toast table, recurse to change its ownership */
   14533         516 :         if (tuple_class->reltoastrelid != InvalidOid)
   14534          42 :             ATExecChangeOwner(tuple_class->reltoastrelid, newOwnerId,
   14535             :                               true, lockmode);
   14536             : 
   14537             :         /* If it has dependent sequences, recurse to change them too */
   14538         516 :         change_owner_recurse_to_sequences(relationOid, newOwnerId, lockmode);
   14539             :     }
   14540             : 
   14541        1984 :     InvokeObjectPostAlterHook(RelationRelationId, relationOid, 0);
   14542             : 
   14543        1984 :     ReleaseSysCache(tuple);
   14544        1984 :     table_close(class_rel, RowExclusiveLock);
   14545        1984 :     relation_close(target_rel, NoLock);
   14546        1984 : }
   14547             : 
   14548             : /*
   14549             :  * change_owner_fix_column_acls
   14550             :  *
   14551             :  * Helper function for ATExecChangeOwner.  Scan the columns of the table
   14552             :  * and fix any non-null column ACLs to reflect the new owner.
   14553             :  */
   14554             : static void
   14555         516 : change_owner_fix_column_acls(Oid relationOid, Oid oldOwnerId, Oid newOwnerId)
   14556             : {
   14557             :     Relation    attRelation;
   14558             :     SysScanDesc scan;
   14559             :     ScanKeyData key[1];
   14560             :     HeapTuple   attributeTuple;
   14561             : 
   14562         516 :     attRelation = table_open(AttributeRelationId, RowExclusiveLock);
   14563         516 :     ScanKeyInit(&key[0],
   14564             :                 Anum_pg_attribute_attrelid,
   14565             :                 BTEqualStrategyNumber, F_OIDEQ,
   14566             :                 ObjectIdGetDatum(relationOid));
   14567         516 :     scan = systable_beginscan(attRelation, AttributeRelidNumIndexId,
   14568             :                               true, NULL, 1, key);
   14569        3612 :     while (HeapTupleIsValid(attributeTuple = systable_getnext(scan)))
   14570             :     {
   14571        3096 :         Form_pg_attribute att = (Form_pg_attribute) GETSTRUCT(attributeTuple);
   14572             :         Datum       repl_val[Natts_pg_attribute];
   14573             :         bool        repl_null[Natts_pg_attribute];
   14574             :         bool        repl_repl[Natts_pg_attribute];
   14575             :         Acl        *newAcl;
   14576             :         Datum       aclDatum;
   14577             :         bool        isNull;
   14578             :         HeapTuple   newtuple;
   14579             : 
   14580             :         /* Ignore dropped columns */
   14581        3096 :         if (att->attisdropped)
   14582        3094 :             continue;
   14583             : 
   14584        3096 :         aclDatum = heap_getattr(attributeTuple,
   14585             :                                 Anum_pg_attribute_attacl,
   14586             :                                 RelationGetDescr(attRelation),
   14587             :                                 &isNull);
   14588             :         /* Null ACLs do not require changes */
   14589        3096 :         if (isNull)
   14590        3094 :             continue;
   14591             : 
   14592           2 :         memset(repl_null, false, sizeof(repl_null));
   14593           2 :         memset(repl_repl, false, sizeof(repl_repl));
   14594             : 
   14595           2 :         newAcl = aclnewowner(DatumGetAclP(aclDatum),
   14596             :                              oldOwnerId, newOwnerId);
   14597           2 :         repl_repl[Anum_pg_attribute_attacl - 1] = true;
   14598           2 :         repl_val[Anum_pg_attribute_attacl - 1] = PointerGetDatum(newAcl);
   14599             : 
   14600           2 :         newtuple = heap_modify_tuple(attributeTuple,
   14601             :                                      RelationGetDescr(attRelation),
   14602             :                                      repl_val, repl_null, repl_repl);
   14603             : 
   14604           2 :         CatalogTupleUpdate(attRelation, &newtuple->t_self, newtuple);
   14605             : 
   14606           2 :         heap_freetuple(newtuple);
   14607             :     }
   14608         516 :     systable_endscan(scan);
   14609         516 :     table_close(attRelation, RowExclusiveLock);
   14610         516 : }
   14611             : 
   14612             : /*
   14613             :  * change_owner_recurse_to_sequences
   14614             :  *
   14615             :  * Helper function for ATExecChangeOwner.  Examines pg_depend searching
   14616             :  * for sequences that are dependent on serial columns, and changes their
   14617             :  * ownership.
   14618             :  */
   14619             : static void
   14620         516 : change_owner_recurse_to_sequences(Oid relationOid, Oid newOwnerId, LOCKMODE lockmode)
   14621             : {
   14622             :     Relation    depRel;
   14623             :     SysScanDesc scan;
   14624             :     ScanKeyData key[2];
   14625             :     HeapTuple   tup;
   14626             : 
   14627             :     /*
   14628             :      * SERIAL sequences are those having an auto dependency on one of the
   14629             :      * table's columns (we don't care *which* column, exactly).
   14630             :      */
   14631         516 :     depRel = table_open(DependRelationId, AccessShareLock);
   14632             : 
   14633         516 :     ScanKeyInit(&key[0],
   14634             :                 Anum_pg_depend_refclassid,
   14635             :                 BTEqualStrategyNumber, F_OIDEQ,
   14636             :                 ObjectIdGetDatum(RelationRelationId));
   14637         516 :     ScanKeyInit(&key[1],
   14638             :                 Anum_pg_depend_refobjid,
   14639             :                 BTEqualStrategyNumber, F_OIDEQ,
   14640             :                 ObjectIdGetDatum(relationOid));
   14641             :     /* we leave refobjsubid unspecified */
   14642             : 
   14643         516 :     scan = systable_beginscan(depRel, DependReferenceIndexId, true,
   14644             :                               NULL, 2, key);
   14645             : 
   14646        1414 :     while (HeapTupleIsValid(tup = systable_getnext(scan)))
   14647             :     {
   14648         898 :         Form_pg_depend depForm = (Form_pg_depend) GETSTRUCT(tup);
   14649             :         Relation    seqRel;
   14650             : 
   14651             :         /* skip dependencies other than auto dependencies on columns */
   14652         898 :         if (depForm->refobjsubid == 0 ||
   14653         302 :             depForm->classid != RelationRelationId ||
   14654         142 :             depForm->objsubid != 0 ||
   14655         142 :             !(depForm->deptype == DEPENDENCY_AUTO || depForm->deptype == DEPENDENCY_INTERNAL))
   14656         756 :             continue;
   14657             : 
   14658             :         /* Use relation_open just in case it's an index */
   14659         142 :         seqRel = relation_open(depForm->objid, lockmode);
   14660             : 
   14661             :         /* skip non-sequence relations */
   14662         142 :         if (RelationGetForm(seqRel)->relkind != RELKIND_SEQUENCE)
   14663             :         {
   14664             :             /* No need to keep the lock */
   14665         116 :             relation_close(seqRel, lockmode);
   14666         116 :             continue;
   14667             :         }
   14668             : 
   14669             :         /* We don't need to close the sequence while we alter it. */
   14670          26 :         ATExecChangeOwner(depForm->objid, newOwnerId, true, lockmode);
   14671             : 
   14672             :         /* Now we can close it.  Keep the lock till end of transaction. */
   14673          26 :         relation_close(seqRel, NoLock);
   14674             :     }
   14675             : 
   14676         516 :     systable_endscan(scan);
   14677             : 
   14678         516 :     relation_close(depRel, AccessShareLock);
   14679         516 : }
   14680             : 
   14681             : /*
   14682             :  * ALTER TABLE CLUSTER ON
   14683             :  *
   14684             :  * The only thing we have to do is to change the indisclustered bits.
   14685             :  *
   14686             :  * Return the address of the new clustering index.
   14687             :  */
   14688             : static ObjectAddress
   14689          64 : ATExecClusterOn(Relation rel, const char *indexName, LOCKMODE lockmode)
   14690             : {
   14691             :     Oid         indexOid;
   14692             :     ObjectAddress address;
   14693             : 
   14694          64 :     indexOid = get_relname_relid(indexName, rel->rd_rel->relnamespace);
   14695             : 
   14696          64 :     if (!OidIsValid(indexOid))
   14697           0 :         ereport(ERROR,
   14698             :                 (errcode(ERRCODE_UNDEFINED_OBJECT),
   14699             :                  errmsg("index \"%s\" for table \"%s\" does not exist",
   14700             :                         indexName, RelationGetRelationName(rel))));
   14701             : 
   14702             :     /* Check index is valid to cluster on */
   14703          64 :     check_index_is_clusterable(rel, indexOid, lockmode);
   14704             : 
   14705             :     /* And do the work */
   14706          64 :     mark_index_clustered(rel, indexOid, false);
   14707             : 
   14708          58 :     ObjectAddressSet(address,
   14709             :                      RelationRelationId, indexOid);
   14710             : 
   14711          58 :     return address;
   14712             : }
   14713             : 
   14714             : /*
   14715             :  * ALTER TABLE SET WITHOUT CLUSTER
   14716             :  *
   14717             :  * We have to find any indexes on the table that have indisclustered bit
   14718             :  * set and turn it off.
   14719             :  */
   14720             : static void
   14721          18 : ATExecDropCluster(Relation rel, LOCKMODE lockmode)
   14722             : {
   14723          18 :     mark_index_clustered(rel, InvalidOid, false);
   14724          12 : }
   14725             : 
   14726             : /*
   14727             :  * Preparation phase for SET ACCESS METHOD
   14728             :  *
   14729             :  * Check that the access method exists and determine whether a change is
   14730             :  * actually needed.
   14731             :  */
   14732             : static void
   14733         110 : ATPrepSetAccessMethod(AlteredTableInfo *tab, Relation rel, const char *amname)
   14734             : {
   14735             :     Oid         amoid;
   14736             : 
   14737             :     /*
   14738             :      * Look up the access method name and check that it differs from the
   14739             :      * table's current AM.  If DEFAULT was specified for a partitioned table
   14740             :      * (amname is NULL), set it to InvalidOid to reset the catalogued AM.
   14741             :      */
   14742         110 :     if (amname != NULL)
   14743          74 :         amoid = get_table_am_oid(amname, false);
   14744          36 :     else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
   14745          18 :         amoid = InvalidOid;
   14746             :     else
   14747          18 :         amoid = get_table_am_oid(default_table_access_method, false);
   14748             : 
   14749             :     /* if it's a match, phase 3 doesn't need to do anything */
   14750         110 :     if (rel->rd_rel->relam == amoid)
   14751          12 :         return;
   14752             : 
   14753             :     /* Save info for Phase 3 to do the real work */
   14754          98 :     tab->rewrite |= AT_REWRITE_ACCESS_METHOD;
   14755          98 :     tab->newAccessMethod = amoid;
   14756          98 :     tab->chgAccessMethod = true;
   14757             : }
   14758             : 
   14759             : /*
   14760             :  * Special handling of ALTER TABLE SET ACCESS METHOD for relations with no
   14761             :  * storage that have an interest in preserving AM.
   14762             :  *
   14763             :  * Since these have no storage, setting the access method is a catalog only
   14764             :  * operation.
   14765             :  */
   14766             : static void
   14767          44 : ATExecSetAccessMethodNoStorage(Relation rel, Oid newAccessMethodId)
   14768             : {
   14769             :     Relation    pg_class;
   14770             :     Oid         oldAccessMethodId;
   14771             :     HeapTuple   tuple;
   14772             :     Form_pg_class rd_rel;
   14773          44 :     Oid         reloid = RelationGetRelid(rel);
   14774             : 
   14775             :     /*
   14776             :      * Shouldn't be called on relations having storage; these are processed in
   14777             :      * phase 3.
   14778             :      */
   14779             :     Assert(!RELKIND_HAS_STORAGE(rel->rd_rel->relkind));
   14780             : 
   14781             :     /* Get a modifiable copy of the relation's pg_class row. */
   14782          44 :     pg_class = table_open(RelationRelationId, RowExclusiveLock);
   14783             : 
   14784          44 :     tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(reloid));
   14785          44 :     if (!HeapTupleIsValid(tuple))
   14786           0 :         elog(ERROR, "cache lookup failed for relation %u", reloid);
   14787          44 :     rd_rel = (Form_pg_class) GETSTRUCT(tuple);
   14788             : 
   14789             :     /* Update the pg_class row. */
   14790          44 :     oldAccessMethodId = rd_rel->relam;
   14791          44 :     rd_rel->relam = newAccessMethodId;
   14792             : 
   14793             :     /* Leave if no update required */
   14794          44 :     if (rd_rel->relam == oldAccessMethodId)
   14795             :     {
   14796           0 :         heap_freetuple(tuple);
   14797           0 :         table_close(pg_class, RowExclusiveLock);
   14798           0 :         return;
   14799             :     }
   14800             : 
   14801          44 :     CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
   14802             : 
   14803             :     /*
   14804             :      * Update the dependency on the new access method.  No dependency is added
   14805             :      * if the new access method is InvalidOid (default case).  Be very careful
   14806             :      * that this has to compare the previous value stored in pg_class with the
   14807             :      * new one.
   14808             :      */
   14809          44 :     if (!OidIsValid(oldAccessMethodId) && OidIsValid(rd_rel->relam))
   14810          20 :     {
   14811             :         ObjectAddress relobj,
   14812             :                     referenced;
   14813             : 
   14814             :         /*
   14815             :          * New access method is defined and there was no dependency
   14816             :          * previously, so record a new one.
   14817             :          */
   14818          20 :         ObjectAddressSet(relobj, RelationRelationId, reloid);
   14819          20 :         ObjectAddressSet(referenced, AccessMethodRelationId, rd_rel->relam);
   14820          20 :         recordDependencyOn(&relobj, &referenced, DEPENDENCY_NORMAL);
   14821             :     }
   14822          24 :     else if (OidIsValid(oldAccessMethodId) &&
   14823          24 :              !OidIsValid(rd_rel->relam))
   14824             :     {
   14825             :         /*
   14826             :          * There was an access method defined, and no new one, so just remove
   14827             :          * the existing dependency.
   14828             :          */
   14829          12 :         deleteDependencyRecordsForClass(RelationRelationId, reloid,
   14830             :                                         AccessMethodRelationId,
   14831             :                                         DEPENDENCY_NORMAL);
   14832             :     }
   14833             :     else
   14834             :     {
   14835             :         Assert(OidIsValid(oldAccessMethodId) &&
   14836             :                OidIsValid(rd_rel->relam));
   14837             : 
   14838             :         /* Both are valid, so update the dependency */
   14839          12 :         changeDependencyFor(RelationRelationId, reloid,
   14840             :                             AccessMethodRelationId,
   14841             :                             oldAccessMethodId, rd_rel->relam);
   14842             :     }
   14843             : 
   14844             :     /* make the relam and dependency changes visible */
   14845          44 :     CommandCounterIncrement();
   14846             : 
   14847          44 :     InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
   14848             : 
   14849          44 :     heap_freetuple(tuple);
   14850          44 :     table_close(pg_class, RowExclusiveLock);
   14851             : }
   14852             : 
   14853             : /*
   14854             :  * ALTER TABLE SET TABLESPACE
   14855             :  */
   14856             : static void
   14857         158 : ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel, const char *tablespacename, LOCKMODE lockmode)
   14858             : {
   14859             :     Oid         tablespaceId;
   14860             : 
   14861             :     /* Check that the tablespace exists */
   14862         158 :     tablespaceId = get_tablespace_oid(tablespacename, false);
   14863             : 
   14864             :     /* Check permissions except when moving to database's default */
   14865         158 :     if (OidIsValid(tablespaceId) && tablespaceId != MyDatabaseTableSpace)
   14866             :     {
   14867             :         AclResult   aclresult;
   14868             : 
   14869          66 :         aclresult = object_aclcheck(TableSpaceRelationId, tablespaceId, GetUserId(), ACL_CREATE);
   14870          66 :         if (aclresult != ACLCHECK_OK)
   14871           0 :             aclcheck_error(aclresult, OBJECT_TABLESPACE, tablespacename);
   14872             :     }
   14873             : 
   14874             :     /* Save info for Phase 3 to do the real work */
   14875         158 :     if (OidIsValid(tab->newTableSpace))
   14876           0 :         ereport(ERROR,
   14877             :                 (errcode(ERRCODE_SYNTAX_ERROR),
   14878             :                  errmsg("cannot have multiple SET TABLESPACE subcommands")));
   14879             : 
   14880         158 :     tab->newTableSpace = tablespaceId;
   14881         158 : }
   14882             : 
   14883             : /*
   14884             :  * Set, reset, or replace reloptions.
   14885             :  */
   14886             : static void
   14887         946 : ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation,
   14888             :                     LOCKMODE lockmode)
   14889             : {
   14890             :     Oid         relid;
   14891             :     Relation    pgclass;
   14892             :     HeapTuple   tuple;
   14893             :     HeapTuple   newtuple;
   14894             :     Datum       datum;
   14895             :     bool        isnull;
   14896             :     Datum       newOptions;
   14897             :     Datum       repl_val[Natts_pg_class];
   14898             :     bool        repl_null[Natts_pg_class];
   14899             :     bool        repl_repl[Natts_pg_class];
   14900             :     static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
   14901             : 
   14902         946 :     if (defList == NIL && operation != AT_ReplaceRelOptions)
   14903           0 :         return;                 /* nothing to do */
   14904             : 
   14905         946 :     pgclass = table_open(RelationRelationId, RowExclusiveLock);
   14906             : 
   14907             :     /* Fetch heap tuple */
   14908         946 :     relid = RelationGetRelid(rel);
   14909         946 :     tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
   14910         946 :     if (!HeapTupleIsValid(tuple))
   14911           0 :         elog(ERROR, "cache lookup failed for relation %u", relid);
   14912             : 
   14913         946 :     if (operation == AT_ReplaceRelOptions)
   14914             :     {
   14915             :         /*
   14916             :          * If we're supposed to replace the reloptions list, we just pretend
   14917             :          * there were none before.
   14918             :          */
   14919         194 :         datum = (Datum) 0;
   14920         194 :         isnull = true;
   14921             :     }
   14922             :     else
   14923             :     {
   14924             :         /* Get the old reloptions */
   14925         752 :         datum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions,
   14926             :                                 &isnull);
   14927             :     }
   14928             : 
   14929             :     /* Generate new proposed reloptions (text array) */
   14930         946 :     newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
   14931             :                                      defList, NULL, validnsps, false,
   14932             :                                      operation == AT_ResetRelOptions);
   14933             : 
   14934             :     /* Validate */
   14935         940 :     switch (rel->rd_rel->relkind)
   14936             :     {
   14937         524 :         case RELKIND_RELATION:
   14938             :         case RELKIND_TOASTVALUE:
   14939             :         case RELKIND_MATVIEW:
   14940         524 :             (void) heap_reloptions(rel->rd_rel->relkind, newOptions, true);
   14941         524 :             break;
   14942           6 :         case RELKIND_PARTITIONED_TABLE:
   14943           6 :             (void) partitioned_table_reloptions(newOptions, true);
   14944           0 :             break;
   14945         296 :         case RELKIND_VIEW:
   14946         296 :             (void) view_reloptions(newOptions, true);
   14947         278 :             break;
   14948         114 :         case RELKIND_INDEX:
   14949             :         case RELKIND_PARTITIONED_INDEX:
   14950         114 :             (void) index_reloptions(rel->rd_indam->amoptions, newOptions, true);
   14951          92 :             break;
   14952           0 :         default:
   14953           0 :             ereport(ERROR,
   14954             :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   14955             :                      errmsg("cannot set options for relation \"%s\"",
   14956             :                             RelationGetRelationName(rel)),
   14957             :                      errdetail_relkind_not_supported(rel->rd_rel->relkind)));
   14958             :             break;
   14959             :     }
   14960             : 
   14961             :     /* Special-case validation of view options */
   14962         894 :     if (rel->rd_rel->relkind == RELKIND_VIEW)
   14963             :     {
   14964         278 :         Query      *view_query = get_view_query(rel);
   14965         278 :         List       *view_options = untransformRelOptions(newOptions);
   14966             :         ListCell   *cell;
   14967         278 :         bool        check_option = false;
   14968             : 
   14969         380 :         foreach(cell, view_options)
   14970             :         {
   14971         102 :             DefElem    *defel = (DefElem *) lfirst(cell);
   14972             : 
   14973         102 :             if (strcmp(defel->defname, "check_option") == 0)
   14974          24 :                 check_option = true;
   14975             :         }
   14976             : 
   14977             :         /*
   14978             :          * If the check option is specified, look to see if the view is
   14979             :          * actually auto-updatable or not.
   14980             :          */
   14981         278 :         if (check_option)
   14982             :         {
   14983             :             const char *view_updatable_error =
   14984          24 :                 view_query_is_auto_updatable(view_query, true);
   14985             : 
   14986          24 :             if (view_updatable_error)
   14987           0 :                 ereport(ERROR,
   14988             :                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   14989             :                          errmsg("WITH CHECK OPTION is supported only on automatically updatable views"),
   14990             :                          errhint("%s", _(view_updatable_error))));
   14991             :         }
   14992             :     }
   14993             : 
   14994             :     /*
   14995             :      * All we need do here is update the pg_class row; the new options will be
   14996             :      * propagated into relcaches during post-commit cache inval.
   14997             :      */
   14998         894 :     memset(repl_val, 0, sizeof(repl_val));
   14999         894 :     memset(repl_null, false, sizeof(repl_null));
   15000         894 :     memset(repl_repl, false, sizeof(repl_repl));
   15001             : 
   15002         894 :     if (newOptions != (Datum) 0)
   15003         600 :         repl_val[Anum_pg_class_reloptions - 1] = newOptions;
   15004             :     else
   15005         294 :         repl_null[Anum_pg_class_reloptions - 1] = true;
   15006             : 
   15007         894 :     repl_repl[Anum_pg_class_reloptions - 1] = true;
   15008             : 
   15009         894 :     newtuple = heap_modify_tuple(tuple, RelationGetDescr(pgclass),
   15010             :                                  repl_val, repl_null, repl_repl);
   15011             : 
   15012         894 :     CatalogTupleUpdate(pgclass, &newtuple->t_self, newtuple);
   15013             : 
   15014         894 :     InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
   15015             : 
   15016         894 :     heap_freetuple(newtuple);
   15017             : 
   15018         894 :     ReleaseSysCache(tuple);
   15019             : 
   15020             :     /* repeat the whole exercise for the toast table, if there's one */
   15021         894 :     if (OidIsValid(rel->rd_rel->reltoastrelid))
   15022             :     {
   15023             :         Relation    toastrel;
   15024         256 :         Oid         toastid = rel->rd_rel->reltoastrelid;
   15025             : 
   15026         256 :         toastrel = table_open(toastid, lockmode);
   15027             : 
   15028             :         /* Fetch heap tuple */
   15029         256 :         tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(toastid));
   15030         256 :         if (!HeapTupleIsValid(tuple))
   15031           0 :             elog(ERROR, "cache lookup failed for relation %u", toastid);
   15032             : 
   15033         256 :         if (operation == AT_ReplaceRelOptions)
   15034             :         {
   15035             :             /*
   15036             :              * If we're supposed to replace the reloptions list, we just
   15037             :              * pretend there were none before.
   15038             :              */
   15039           0 :             datum = (Datum) 0;
   15040           0 :             isnull = true;
   15041             :         }
   15042             :         else
   15043             :         {
   15044             :             /* Get the old reloptions */
   15045         256 :             datum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions,
   15046             :                                     &isnull);
   15047             :         }
   15048             : 
   15049         256 :         newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
   15050             :                                          defList, "toast", validnsps, false,
   15051             :                                          operation == AT_ResetRelOptions);
   15052             : 
   15053         256 :         (void) heap_reloptions(RELKIND_TOASTVALUE, newOptions, true);
   15054             : 
   15055         256 :         memset(repl_val, 0, sizeof(repl_val));
   15056         256 :         memset(repl_null, false, sizeof(repl_null));
   15057         256 :         memset(repl_repl, false, sizeof(repl_repl));
   15058             : 
   15059         256 :         if (newOptions != (Datum) 0)
   15060          42 :             repl_val[Anum_pg_class_reloptions - 1] = newOptions;
   15061             :         else
   15062         214 :             repl_null[Anum_pg_class_reloptions - 1] = true;
   15063             : 
   15064         256 :         repl_repl[Anum_pg_class_reloptions - 1] = true;
   15065             : 
   15066         256 :         newtuple = heap_modify_tuple(tuple, RelationGetDescr(pgclass),
   15067             :                                      repl_val, repl_null, repl_repl);
   15068             : 
   15069         256 :         CatalogTupleUpdate(pgclass, &newtuple->t_self, newtuple);
   15070             : 
   15071         256 :         InvokeObjectPostAlterHookArg(RelationRelationId,
   15072             :                                      RelationGetRelid(toastrel), 0,
   15073             :                                      InvalidOid, true);
   15074             : 
   15075         256 :         heap_freetuple(newtuple);
   15076             : 
   15077         256 :         ReleaseSysCache(tuple);
   15078             : 
   15079         256 :         table_close(toastrel, NoLock);
   15080             :     }
   15081             : 
   15082         894 :     table_close(pgclass, RowExclusiveLock);
   15083             : }
   15084             : 
   15085             : /*
   15086             :  * Execute ALTER TABLE SET TABLESPACE for cases where there is no tuple
   15087             :  * rewriting to be done, so we just want to copy the data as fast as possible.
   15088             :  */
   15089             : static void
   15090         162 : ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode)
   15091             : {
   15092             :     Relation    rel;
   15093             :     Oid         reltoastrelid;
   15094             :     RelFileNumber newrelfilenumber;
   15095             :     RelFileLocator newrlocator;
   15096         162 :     List       *reltoastidxids = NIL;
   15097             :     ListCell   *lc;
   15098             : 
   15099             :     /*
   15100             :      * Need lock here in case we are recursing to toast table or index
   15101             :      */
   15102         162 :     rel = relation_open(tableOid, lockmode);
   15103             : 
   15104             :     /* Check first if relation can be moved to new tablespace */
   15105         162 :     if (!CheckRelationTableSpaceMove(rel, newTableSpace))
   15106             :     {
   15107           2 :         InvokeObjectPostAlterHook(RelationRelationId,
   15108             :                                   RelationGetRelid(rel), 0);
   15109           2 :         relation_close(rel, NoLock);
   15110           2 :         return;
   15111             :     }
   15112             : 
   15113         160 :     reltoastrelid = rel->rd_rel->reltoastrelid;
   15114             :     /* Fetch the list of indexes on toast relation if necessary */
   15115         160 :     if (OidIsValid(reltoastrelid))
   15116             :     {
   15117          20 :         Relation    toastRel = relation_open(reltoastrelid, lockmode);
   15118             : 
   15119          20 :         reltoastidxids = RelationGetIndexList(toastRel);
   15120          20 :         relation_close(toastRel, lockmode);
   15121             :     }
   15122             : 
   15123             :     /*
   15124             :      * Relfilenumbers are not unique in databases across tablespaces, so we
   15125             :      * need to allocate a new one in the new tablespace.
   15126             :      */
   15127         160 :     newrelfilenumber = GetNewRelFileNumber(newTableSpace, NULL,
   15128         160 :                                            rel->rd_rel->relpersistence);
   15129             : 
   15130             :     /* Open old and new relation */
   15131         160 :     newrlocator = rel->rd_locator;
   15132         160 :     newrlocator.relNumber = newrelfilenumber;
   15133         160 :     newrlocator.spcOid = newTableSpace;
   15134             : 
   15135             :     /* hand off to AM to actually create new rel storage and copy the data */
   15136         160 :     if (rel->rd_rel->relkind == RELKIND_INDEX)
   15137             :     {
   15138          62 :         index_copy_data(rel, newrlocator);
   15139             :     }
   15140             :     else
   15141             :     {
   15142             :         Assert(RELKIND_HAS_TABLE_AM(rel->rd_rel->relkind));
   15143          98 :         table_relation_copy_data(rel, &newrlocator);
   15144             :     }
   15145             : 
   15146             :     /*
   15147             :      * Update the pg_class row.
   15148             :      *
   15149             :      * NB: This wouldn't work if ATExecSetTableSpace() were allowed to be
   15150             :      * executed on pg_class or its indexes (the above copy wouldn't contain
   15151             :      * the updated pg_class entry), but that's forbidden with
   15152             :      * CheckRelationTableSpaceMove().
   15153             :      */
   15154         160 :     SetRelationTableSpace(rel, newTableSpace, newrelfilenumber);
   15155             : 
   15156         160 :     InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
   15157             : 
   15158         160 :     RelationAssumeNewRelfilelocator(rel);
   15159             : 
   15160         160 :     relation_close(rel, NoLock);
   15161             : 
   15162             :     /* Make sure the reltablespace change is visible */
   15163         160 :     CommandCounterIncrement();
   15164             : 
   15165             :     /* Move associated toast relation and/or indexes, too */
   15166         160 :     if (OidIsValid(reltoastrelid))
   15167          20 :         ATExecSetTableSpace(reltoastrelid, newTableSpace, lockmode);
   15168         180 :     foreach(lc, reltoastidxids)
   15169          20 :         ATExecSetTableSpace(lfirst_oid(lc), newTableSpace, lockmode);
   15170             : 
   15171             :     /* Clean up */
   15172         160 :     list_free(reltoastidxids);
   15173             : }
   15174             : 
   15175             : /*
   15176             :  * Special handling of ALTER TABLE SET TABLESPACE for relations with no
   15177             :  * storage that have an interest in preserving tablespace.
   15178             :  *
   15179             :  * Since these have no storage the tablespace can be updated with a simple
   15180             :  * metadata only operation to update the tablespace.
   15181             :  */
   15182             : static void
   15183          36 : ATExecSetTableSpaceNoStorage(Relation rel, Oid newTableSpace)
   15184             : {
   15185             :     /*
   15186             :      * Shouldn't be called on relations having storage; these are processed in
   15187             :      * phase 3.
   15188             :      */
   15189             :     Assert(!RELKIND_HAS_STORAGE(rel->rd_rel->relkind));
   15190             : 
   15191             :     /* check if relation can be moved to its new tablespace */
   15192          36 :     if (!CheckRelationTableSpaceMove(rel, newTableSpace))
   15193             :     {
   15194           0 :         InvokeObjectPostAlterHook(RelationRelationId,
   15195             :                                   RelationGetRelid(rel),
   15196             :                                   0);
   15197           0 :         return;
   15198             :     }
   15199             : 
   15200             :     /* Update can be done, so change reltablespace */
   15201          30 :     SetRelationTableSpace(rel, newTableSpace, InvalidOid);
   15202             : 
   15203          30 :     InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
   15204             : 
   15205             :     /* Make sure the reltablespace change is visible */
   15206          30 :     CommandCounterIncrement();
   15207             : }
   15208             : 
   15209             : /*
   15210             :  * Alter Table ALL ... SET TABLESPACE
   15211             :  *
   15212             :  * Allows a user to move all objects of some type in a given tablespace in the
   15213             :  * current database to another tablespace.  Objects can be chosen based on the
   15214             :  * owner of the object also, to allow users to move only their objects.
   15215             :  * The user must have CREATE rights on the new tablespace, as usual.   The main
   15216             :  * permissions handling is done by the lower-level table move function.
   15217             :  *
   15218             :  * All to-be-moved objects are locked first. If NOWAIT is specified and the
   15219             :  * lock can't be acquired then we ereport(ERROR).
   15220             :  */
   15221             : Oid
   15222          30 : AlterTableMoveAll(AlterTableMoveAllStmt *stmt)
   15223             : {
   15224          30 :     List       *relations = NIL;
   15225             :     ListCell   *l;
   15226             :     ScanKeyData key[1];
   15227             :     Relation    rel;
   15228             :     TableScanDesc scan;
   15229             :     HeapTuple   tuple;
   15230             :     Oid         orig_tablespaceoid;
   15231             :     Oid         new_tablespaceoid;
   15232          30 :     List       *role_oids = roleSpecsToIds(stmt->roles);
   15233             : 
   15234             :     /* Ensure we were not asked to move something we can't */
   15235          30 :     if (stmt->objtype != OBJECT_TABLE && stmt->objtype != OBJECT_INDEX &&
   15236          12 :         stmt->objtype != OBJECT_MATVIEW)
   15237           0 :         ereport(ERROR,
   15238             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
   15239             :                  errmsg("only tables, indexes, and materialized views exist in tablespaces")));
   15240             : 
   15241             :     /* Get the orig and new tablespace OIDs */
   15242          30 :     orig_tablespaceoid = get_tablespace_oid(stmt->orig_tablespacename, false);
   15243          30 :     new_tablespaceoid = get_tablespace_oid(stmt->new_tablespacename, false);
   15244             : 
   15245             :     /* Can't move shared relations in to or out of pg_global */
   15246             :     /* This is also checked by ATExecSetTableSpace, but nice to stop earlier */
   15247          30 :     if (orig_tablespaceoid == GLOBALTABLESPACE_OID ||
   15248             :         new_tablespaceoid == GLOBALTABLESPACE_OID)
   15249           0 :         ereport(ERROR,
   15250             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
   15251             :                  errmsg("cannot move relations in to or out of pg_global tablespace")));
   15252             : 
   15253             :     /*
   15254             :      * Must have CREATE rights on the new tablespace, unless it is the
   15255             :      * database default tablespace (which all users implicitly have CREATE
   15256             :      * rights on).
   15257             :      */
   15258          30 :     if (OidIsValid(new_tablespaceoid) && new_tablespaceoid != MyDatabaseTableSpace)
   15259             :     {
   15260             :         AclResult   aclresult;
   15261             : 
   15262           0 :         aclresult = object_aclcheck(TableSpaceRelationId, new_tablespaceoid, GetUserId(),
   15263             :                                     ACL_CREATE);
   15264           0 :         if (aclresult != ACLCHECK_OK)
   15265           0 :             aclcheck_error(aclresult, OBJECT_TABLESPACE,
   15266           0 :                            get_tablespace_name(new_tablespaceoid));
   15267             :     }
   15268             : 
   15269             :     /*
   15270             :      * Now that the checks are done, check if we should set either to
   15271             :      * InvalidOid because it is our database's default tablespace.
   15272             :      */
   15273          30 :     if (orig_tablespaceoid == MyDatabaseTableSpace)
   15274           0 :         orig_tablespaceoid = InvalidOid;
   15275             : 
   15276          30 :     if (new_tablespaceoid == MyDatabaseTableSpace)
   15277          30 :         new_tablespaceoid = InvalidOid;
   15278             : 
   15279             :     /* no-op */
   15280          30 :     if (orig_tablespaceoid == new_tablespaceoid)
   15281           0 :         return new_tablespaceoid;
   15282             : 
   15283             :     /*
   15284             :      * Walk the list of objects in the tablespace and move them. This will
   15285             :      * only find objects in our database, of course.
   15286             :      */
   15287          30 :     ScanKeyInit(&key[0],
   15288             :                 Anum_pg_class_reltablespace,
   15289             :                 BTEqualStrategyNumber, F_OIDEQ,
   15290             :                 ObjectIdGetDatum(orig_tablespaceoid));
   15291             : 
   15292          30 :     rel = table_open(RelationRelationId, AccessShareLock);
   15293          30 :     scan = table_beginscan_catalog(rel, 1, key);
   15294         132 :     while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
   15295             :     {
   15296         102 :         Form_pg_class relForm = (Form_pg_class) GETSTRUCT(tuple);
   15297         102 :         Oid         relOid = relForm->oid;
   15298             : 
   15299             :         /*
   15300             :          * Do not move objects in pg_catalog as part of this, if an admin
   15301             :          * really wishes to do so, they can issue the individual ALTER
   15302             :          * commands directly.
   15303             :          *
   15304             :          * Also, explicitly avoid any shared tables, temp tables, or TOAST
   15305             :          * (TOAST will be moved with the main table).
   15306             :          */
   15307         102 :         if (IsCatalogNamespace(relForm->relnamespace) ||
   15308         204 :             relForm->relisshared ||
   15309         204 :             isAnyTempNamespace(relForm->relnamespace) ||
   15310         102 :             IsToastNamespace(relForm->relnamespace))
   15311           0 :             continue;
   15312             : 
   15313             :         /* Only move the object type requested */
   15314         102 :         if ((stmt->objtype == OBJECT_TABLE &&
   15315          60 :              relForm->relkind != RELKIND_RELATION &&
   15316          36 :              relForm->relkind != RELKIND_PARTITIONED_TABLE) ||
   15317          66 :             (stmt->objtype == OBJECT_INDEX &&
   15318          36 :              relForm->relkind != RELKIND_INDEX &&
   15319           6 :              relForm->relkind != RELKIND_PARTITIONED_INDEX) ||
   15320          60 :             (stmt->objtype == OBJECT_MATVIEW &&
   15321           6 :              relForm->relkind != RELKIND_MATVIEW))
   15322          42 :             continue;
   15323             : 
   15324             :         /* Check if we are only moving objects owned by certain roles */
   15325          60 :         if (role_oids != NIL && !list_member_oid(role_oids, relForm->relowner))
   15326           0 :             continue;
   15327             : 
   15328             :         /*
   15329             :          * Handle permissions-checking here since we are locking the tables
   15330             :          * and also to avoid doing a bunch of work only to fail part-way. Note
   15331             :          * that permissions will also be checked by AlterTableInternal().
   15332             :          *
   15333             :          * Caller must be considered an owner on the table to move it.
   15334             :          */
   15335          60 :         if (!object_ownercheck(RelationRelationId, relOid, GetUserId()))
   15336           0 :             aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relOid)),
   15337           0 :                            NameStr(relForm->relname));
   15338             : 
   15339          60 :         if (stmt->nowait &&
   15340           0 :             !ConditionalLockRelationOid(relOid, AccessExclusiveLock))
   15341           0 :             ereport(ERROR,
   15342             :                     (errcode(ERRCODE_OBJECT_IN_USE),
   15343             :                      errmsg("aborting because lock on relation \"%s.%s\" is not available",
   15344             :                             get_namespace_name(relForm->relnamespace),
   15345             :                             NameStr(relForm->relname))));
   15346             :         else
   15347          60 :             LockRelationOid(relOid, AccessExclusiveLock);
   15348             : 
   15349             :         /* Add to our list of objects to move */
   15350          60 :         relations = lappend_oid(relations, relOid);
   15351             :     }
   15352             : 
   15353          30 :     table_endscan(scan);
   15354          30 :     table_close(rel, AccessShareLock);
   15355             : 
   15356          30 :     if (relations == NIL)
   15357          12 :         ereport(NOTICE,
   15358             :                 (errcode(ERRCODE_NO_DATA_FOUND),
   15359             :                  errmsg("no matching relations in tablespace \"%s\" found",
   15360             :                         orig_tablespaceoid == InvalidOid ? "(database default)" :
   15361             :                         get_tablespace_name(orig_tablespaceoid))));
   15362             : 
   15363             :     /* Everything is locked, loop through and move all of the relations. */
   15364          90 :     foreach(l, relations)
   15365             :     {
   15366          60 :         List       *cmds = NIL;
   15367          60 :         AlterTableCmd *cmd = makeNode(AlterTableCmd);
   15368             : 
   15369          60 :         cmd->subtype = AT_SetTableSpace;
   15370          60 :         cmd->name = stmt->new_tablespacename;
   15371             : 
   15372          60 :         cmds = lappend(cmds, cmd);
   15373             : 
   15374          60 :         EventTriggerAlterTableStart((Node *) stmt);
   15375             :         /* OID is set by AlterTableInternal */
   15376          60 :         AlterTableInternal(lfirst_oid(l), cmds, false);
   15377          60 :         EventTriggerAlterTableEnd();
   15378             :     }
   15379             : 
   15380          30 :     return new_tablespaceoid;
   15381             : }
   15382             : 
   15383             : static void
   15384          62 : index_copy_data(Relation rel, RelFileLocator newrlocator)
   15385             : {
   15386             :     SMgrRelation dstrel;
   15387             : 
   15388             :     /*
   15389             :      * Since we copy the file directly without looking at the shared buffers,
   15390             :      * we'd better first flush out any pages of the source relation that are
   15391             :      * in shared buffers.  We assume no new changes will be made while we are
   15392             :      * holding exclusive lock on the rel.
   15393             :      */
   15394          62 :     FlushRelationBuffers(rel);
   15395             : 
   15396             :     /*
   15397             :      * Create and copy all forks of the relation, and schedule unlinking of
   15398             :      * old physical files.
   15399             :      *
   15400             :      * NOTE: any conflict in relfilenumber value will be caught in
   15401             :      * RelationCreateStorage().
   15402             :      */
   15403          62 :     dstrel = RelationCreateStorage(newrlocator, rel->rd_rel->relpersistence, true);
   15404             : 
   15405             :     /* copy main fork */
   15406          62 :     RelationCopyStorage(RelationGetSmgr(rel), dstrel, MAIN_FORKNUM,
   15407          62 :                         rel->rd_rel->relpersistence);
   15408             : 
   15409             :     /* copy those extra forks that exist */
   15410         248 :     for (ForkNumber forkNum = MAIN_FORKNUM + 1;
   15411         186 :          forkNum <= MAX_FORKNUM; forkNum++)
   15412             :     {
   15413         186 :         if (smgrexists(RelationGetSmgr(rel), forkNum))
   15414             :         {
   15415           0 :             smgrcreate(dstrel, forkNum, false);
   15416             : 
   15417             :             /*
   15418             :              * WAL log creation if the relation is persistent, or this is the
   15419             :              * init fork of an unlogged relation.
   15420             :              */
   15421           0 :             if (RelationIsPermanent(rel) ||
   15422           0 :                 (rel->rd_rel->relpersistence == RELPERSISTENCE_UNLOGGED &&
   15423             :                  forkNum == INIT_FORKNUM))
   15424           0 :                 log_smgrcreate(&newrlocator, forkNum);
   15425           0 :             RelationCopyStorage(RelationGetSmgr(rel), dstrel, forkNum,
   15426           0 :                                 rel->rd_rel->relpersistence);
   15427             :         }
   15428             :     }
   15429             : 
   15430             :     /* drop old relation, and close new one */
   15431          62 :     RelationDropStorage(rel);
   15432          62 :     smgrclose(dstrel);
   15433          62 : }
   15434             : 
   15435             : /*
   15436             :  * ALTER TABLE ENABLE/DISABLE TRIGGER
   15437             :  *
   15438             :  * We just pass this off to trigger.c.
   15439             :  */
   15440             : static void
   15441         340 : ATExecEnableDisableTrigger(Relation rel, const char *trigname,
   15442             :                            char fires_when, bool skip_system, bool recurse,
   15443             :                            LOCKMODE lockmode)
   15444             : {
   15445         340 :     EnableDisableTrigger(rel, trigname, InvalidOid,
   15446             :                          fires_when, skip_system, recurse,
   15447             :                          lockmode);
   15448             : 
   15449         340 :     InvokeObjectPostAlterHook(RelationRelationId,
   15450             :                               RelationGetRelid(rel), 0);
   15451         340 : }
   15452             : 
   15453             : /*
   15454             :  * ALTER TABLE ENABLE/DISABLE RULE
   15455             :  *
   15456             :  * We just pass this off to rewriteDefine.c.
   15457             :  */
   15458             : static void
   15459          46 : ATExecEnableDisableRule(Relation rel, const char *rulename,
   15460             :                         char fires_when, LOCKMODE lockmode)
   15461             : {
   15462          46 :     EnableDisableRule(rel, rulename, fires_when);
   15463             : 
   15464          46 :     InvokeObjectPostAlterHook(RelationRelationId,
   15465             :                               RelationGetRelid(rel), 0);
   15466          46 : }
   15467             : 
   15468             : /*
   15469             :  * ALTER TABLE INHERIT
   15470             :  *
   15471             :  * Add a parent to the child's parents. This verifies that all the columns and
   15472             :  * check constraints of the parent appear in the child and that they have the
   15473             :  * same data types and expressions.
   15474             :  */
   15475             : static void
   15476         272 : ATPrepAddInherit(Relation child_rel)
   15477             : {
   15478         272 :     if (child_rel->rd_rel->reloftype)
   15479           6 :         ereport(ERROR,
   15480             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   15481             :                  errmsg("cannot change inheritance of typed table")));
   15482             : 
   15483         266 :     if (child_rel->rd_rel->relispartition)
   15484           6 :         ereport(ERROR,
   15485             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   15486             :                  errmsg("cannot change inheritance of a partition")));
   15487             : 
   15488         260 :     if (child_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
   15489           6 :         ereport(ERROR,
   15490             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   15491             :                  errmsg("cannot change inheritance of partitioned table")));
   15492         254 : }
   15493             : 
   15494             : /*
   15495             :  * Return the address of the new parent relation.
   15496             :  */
   15497             : static ObjectAddress
   15498         254 : ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode)
   15499             : {
   15500             :     Relation    parent_rel;
   15501             :     List       *children;
   15502             :     ObjectAddress address;
   15503             :     const char *trigger_name;
   15504             : 
   15505             :     /*
   15506             :      * A self-exclusive lock is needed here.  See the similar case in
   15507             :      * MergeAttributes() for a full explanation.
   15508             :      */
   15509         254 :     parent_rel = table_openrv(parent, ShareUpdateExclusiveLock);
   15510             : 
   15511             :     /*
   15512             :      * Must be owner of both parent and child -- child was checked by
   15513             :      * ATSimplePermissions call in ATPrepCmd
   15514             :      */
   15515         254 :     ATSimplePermissions(AT_AddInherit, parent_rel, ATT_TABLE | ATT_FOREIGN_TABLE);
   15516             : 
   15517             :     /* Permanent rels cannot inherit from temporary ones */
   15518         254 :     if (parent_rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
   15519           6 :         child_rel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
   15520           0 :         ereport(ERROR,
   15521             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   15522             :                  errmsg("cannot inherit from temporary relation \"%s\"",
   15523             :                         RelationGetRelationName(parent_rel))));
   15524             : 
   15525             :     /* If parent rel is temp, it must belong to this session */
   15526         254 :     if (parent_rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
   15527           6 :         !parent_rel->rd_islocaltemp)
   15528           0 :         ereport(ERROR,
   15529             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   15530             :                  errmsg("cannot inherit from temporary relation of another session")));
   15531             : 
   15532             :     /* Ditto for the child */
   15533         254 :     if (child_rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
   15534           6 :         !child_rel->rd_islocaltemp)
   15535           0 :         ereport(ERROR,
   15536             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   15537             :                  errmsg("cannot inherit to temporary relation of another session")));
   15538             : 
   15539             :     /* Prevent partitioned tables from becoming inheritance parents */
   15540         254 :     if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
   15541           6 :         ereport(ERROR,
   15542             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   15543             :                  errmsg("cannot inherit from partitioned table \"%s\"",
   15544             :                         parent->relname)));
   15545             : 
   15546             :     /* Likewise for partitions */
   15547         248 :     if (parent_rel->rd_rel->relispartition)
   15548           6 :         ereport(ERROR,
   15549             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   15550             :                  errmsg("cannot inherit from a partition")));
   15551             : 
   15552             :     /*
   15553             :      * Prevent circularity by seeing if proposed parent inherits from child.
   15554             :      * (In particular, this disallows making a rel inherit from itself.)
   15555             :      *
   15556             :      * This is not completely bulletproof because of race conditions: in
   15557             :      * multi-level inheritance trees, someone else could concurrently be
   15558             :      * making another inheritance link that closes the loop but does not join
   15559             :      * either of the rels we have locked.  Preventing that seems to require
   15560             :      * exclusive locks on the entire inheritance tree, which is a cure worse
   15561             :      * than the disease.  find_all_inheritors() will cope with circularity
   15562             :      * anyway, so don't sweat it too much.
   15563             :      *
   15564             :      * We use weakest lock we can on child's children, namely AccessShareLock.
   15565             :      */
   15566         242 :     children = find_all_inheritors(RelationGetRelid(child_rel),
   15567             :                                    AccessShareLock, NULL);
   15568             : 
   15569         242 :     if (list_member_oid(children, RelationGetRelid(parent_rel)))
   15570          12 :         ereport(ERROR,
   15571             :                 (errcode(ERRCODE_DUPLICATE_TABLE),
   15572             :                  errmsg("circular inheritance not allowed"),
   15573             :                  errdetail("\"%s\" is already a child of \"%s\".",
   15574             :                            parent->relname,
   15575             :                            RelationGetRelationName(child_rel))));
   15576             : 
   15577             :     /*
   15578             :      * If child_rel has row-level triggers with transition tables, we
   15579             :      * currently don't allow it to become an inheritance child.  See also
   15580             :      * prohibitions in ATExecAttachPartition() and CreateTrigger().
   15581             :      */
   15582         230 :     trigger_name = FindTriggerIncompatibleWithInheritance(child_rel->trigdesc);
   15583         230 :     if (trigger_name != NULL)
   15584           6 :         ereport(ERROR,
   15585             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   15586             :                  errmsg("trigger \"%s\" prevents table \"%s\" from becoming an inheritance child",
   15587             :                         trigger_name, RelationGetRelationName(child_rel)),
   15588             :                  errdetail("ROW triggers with transition tables are not supported in inheritance hierarchies.")));
   15589             : 
   15590             :     /* OK to create inheritance */
   15591         224 :     CreateInheritance(child_rel, parent_rel, false);
   15592             : 
   15593         182 :     ObjectAddressSet(address, RelationRelationId,
   15594             :                      RelationGetRelid(parent_rel));
   15595             : 
   15596             :     /* keep our lock on the parent relation until commit */
   15597         182 :     table_close(parent_rel, NoLock);
   15598             : 
   15599         182 :     return address;
   15600             : }
   15601             : 
   15602             : /*
   15603             :  * CreateInheritance
   15604             :  *      Catalog manipulation portion of creating inheritance between a child
   15605             :  *      table and a parent table.
   15606             :  *
   15607             :  * Common to ATExecAddInherit() and ATExecAttachPartition().
   15608             :  */
   15609             : static void
   15610        2798 : CreateInheritance(Relation child_rel, Relation parent_rel, bool ispartition)
   15611             : {
   15612             :     Relation    catalogRelation;
   15613             :     SysScanDesc scan;
   15614             :     ScanKeyData key;
   15615             :     HeapTuple   inheritsTuple;
   15616             :     int32       inhseqno;
   15617             : 
   15618             :     /* Note: get RowExclusiveLock because we will write pg_inherits below. */
   15619        2798 :     catalogRelation = table_open(InheritsRelationId, RowExclusiveLock);
   15620             : 
   15621             :     /*
   15622             :      * Check for duplicates in the list of parents, and determine the highest
   15623             :      * inhseqno already present; we'll use the next one for the new parent.
   15624             :      * Also, if proposed child is a partition, it cannot already be
   15625             :      * inheriting.
   15626             :      *
   15627             :      * Note: we do not reject the case where the child already inherits from
   15628             :      * the parent indirectly; CREATE TABLE doesn't reject comparable cases.
   15629             :      */
   15630        2798 :     ScanKeyInit(&key,
   15631             :                 Anum_pg_inherits_inhrelid,
   15632             :                 BTEqualStrategyNumber, F_OIDEQ,
   15633             :                 ObjectIdGetDatum(RelationGetRelid(child_rel)));
   15634        2798 :     scan = systable_beginscan(catalogRelation, InheritsRelidSeqnoIndexId,
   15635             :                               true, NULL, 1, &key);
   15636             : 
   15637             :     /* inhseqno sequences start at 1 */
   15638        2798 :     inhseqno = 0;
   15639        2842 :     while (HeapTupleIsValid(inheritsTuple = systable_getnext(scan)))
   15640             :     {
   15641          50 :         Form_pg_inherits inh = (Form_pg_inherits) GETSTRUCT(inheritsTuple);
   15642             : 
   15643          50 :         if (inh->inhparent == RelationGetRelid(parent_rel))
   15644           6 :             ereport(ERROR,
   15645             :                     (errcode(ERRCODE_DUPLICATE_TABLE),
   15646             :                      errmsg("relation \"%s\" would be inherited from more than once",
   15647             :                             RelationGetRelationName(parent_rel))));
   15648             : 
   15649          44 :         if (inh->inhseqno > inhseqno)
   15650          44 :             inhseqno = inh->inhseqno;
   15651             :     }
   15652        2792 :     systable_endscan(scan);
   15653             : 
   15654             :     /* Match up the columns and bump attinhcount as needed */
   15655        2792 :     MergeAttributesIntoExisting(child_rel, parent_rel, ispartition);
   15656             : 
   15657             :     /* Match up the constraints and bump coninhcount as needed */
   15658        2720 :     MergeConstraintsIntoExisting(child_rel, parent_rel);
   15659             : 
   15660             :     /*
   15661             :      * OK, it looks valid.  Make the catalog entries that show inheritance.
   15662             :      */
   15663        2690 :     StoreCatalogInheritance1(RelationGetRelid(child_rel),
   15664             :                              RelationGetRelid(parent_rel),
   15665             :                              inhseqno + 1,
   15666             :                              catalogRelation,
   15667        2690 :                              parent_rel->rd_rel->relkind ==
   15668             :                              RELKIND_PARTITIONED_TABLE);
   15669             : 
   15670             :     /* Now we're done with pg_inherits */
   15671        2690 :     table_close(catalogRelation, RowExclusiveLock);
   15672        2690 : }
   15673             : 
   15674             : /*
   15675             :  * Obtain the source-text form of the constraint expression for a check
   15676             :  * constraint, given its pg_constraint tuple
   15677             :  */
   15678             : static char *
   15679         180 : decompile_conbin(HeapTuple contup, TupleDesc tupdesc)
   15680             : {
   15681             :     Form_pg_constraint con;
   15682             :     bool        isnull;
   15683             :     Datum       attr;
   15684             :     Datum       expr;
   15685             : 
   15686         180 :     con = (Form_pg_constraint) GETSTRUCT(contup);
   15687         180 :     attr = heap_getattr(contup, Anum_pg_constraint_conbin, tupdesc, &isnull);
   15688         180 :     if (isnull)
   15689           0 :         elog(ERROR, "null conbin for constraint %u", con->oid);
   15690             : 
   15691         180 :     expr = DirectFunctionCall2(pg_get_expr, attr,
   15692             :                                ObjectIdGetDatum(con->conrelid));
   15693         180 :     return TextDatumGetCString(expr);
   15694             : }
   15695             : 
   15696             : /*
   15697             :  * Determine whether two check constraints are functionally equivalent
   15698             :  *
   15699             :  * The test we apply is to see whether they reverse-compile to the same
   15700             :  * source string.  This insulates us from issues like whether attributes
   15701             :  * have the same physical column numbers in parent and child relations.
   15702             :  */
   15703             : static bool
   15704          90 : constraints_equivalent(HeapTuple a, HeapTuple b, TupleDesc tupleDesc)
   15705             : {
   15706          90 :     Form_pg_constraint acon = (Form_pg_constraint) GETSTRUCT(a);
   15707          90 :     Form_pg_constraint bcon = (Form_pg_constraint) GETSTRUCT(b);
   15708             : 
   15709          90 :     if (acon->condeferrable != bcon->condeferrable ||
   15710          90 :         acon->condeferred != bcon->condeferred ||
   15711          90 :         strcmp(decompile_conbin(a, tupleDesc),
   15712          90 :                decompile_conbin(b, tupleDesc)) != 0)
   15713           6 :         return false;
   15714             :     else
   15715          84 :         return true;
   15716             : }
   15717             : 
   15718             : /*
   15719             :  * Check columns in child table match up with columns in parent, and increment
   15720             :  * their attinhcount.
   15721             :  *
   15722             :  * Called by CreateInheritance
   15723             :  *
   15724             :  * Currently all parent columns must be found in child. Missing columns are an
   15725             :  * error.  One day we might consider creating new columns like CREATE TABLE
   15726             :  * does.  However, that is widely unpopular --- in the common use case of
   15727             :  * partitioned tables it's a foot-gun.
   15728             :  *
   15729             :  * The data type must match exactly. If the parent column is NOT NULL then
   15730             :  * the child must be as well. Defaults are not compared, however.
   15731             :  */
   15732             : static void
   15733        2792 : MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel, bool ispartition)
   15734             : {
   15735             :     Relation    attrrel;
   15736             :     TupleDesc   parent_desc;
   15737             : 
   15738        2792 :     attrrel = table_open(AttributeRelationId, RowExclusiveLock);
   15739        2792 :     parent_desc = RelationGetDescr(parent_rel);
   15740             : 
   15741        9532 :     for (AttrNumber parent_attno = 1; parent_attno <= parent_desc->natts; parent_attno++)
   15742             :     {
   15743        6812 :         Form_pg_attribute parent_att = TupleDescAttr(parent_desc, parent_attno - 1);
   15744        6812 :         char       *parent_attname = NameStr(parent_att->attname);
   15745             :         HeapTuple   tuple;
   15746             : 
   15747             :         /* Ignore dropped columns in the parent. */
   15748        6812 :         if (parent_att->attisdropped)
   15749         296 :             continue;
   15750             : 
   15751             :         /* Find same column in child (matching on column name). */
   15752        6516 :         tuple = SearchSysCacheCopyAttName(RelationGetRelid(child_rel), parent_attname);
   15753        6516 :         if (HeapTupleIsValid(tuple))
   15754             :         {
   15755        6504 :             Form_pg_attribute child_att = (Form_pg_attribute) GETSTRUCT(tuple);
   15756             : 
   15757        6504 :             if (parent_att->atttypid != child_att->atttypid ||
   15758        6498 :                 parent_att->atttypmod != child_att->atttypmod)
   15759          12 :                 ereport(ERROR,
   15760             :                         (errcode(ERRCODE_DATATYPE_MISMATCH),
   15761             :                          errmsg("child table \"%s\" has different type for column \"%s\"",
   15762             :                                 RelationGetRelationName(child_rel), parent_attname)));
   15763             : 
   15764        6492 :             if (parent_att->attcollation != child_att->attcollation)
   15765           6 :                 ereport(ERROR,
   15766             :                         (errcode(ERRCODE_COLLATION_MISMATCH),
   15767             :                          errmsg("child table \"%s\" has different collation for column \"%s\"",
   15768             :                                 RelationGetRelationName(child_rel), parent_attname)));
   15769             : 
   15770             :             /*
   15771             :              * Check child doesn't discard NOT NULL property.  (Other
   15772             :              * constraints are checked elsewhere.)
   15773             :              */
   15774        6486 :             if (parent_att->attnotnull && !child_att->attnotnull)
   15775          12 :                 ereport(ERROR,
   15776             :                         (errcode(ERRCODE_DATATYPE_MISMATCH),
   15777             :                          errmsg("column \"%s\" in child table must be marked NOT NULL",
   15778             :                                 parent_attname)));
   15779             : 
   15780             :             /*
   15781             :              * Child column must be generated if and only if parent column is.
   15782             :              */
   15783        6474 :             if (parent_att->attgenerated && !child_att->attgenerated)
   15784          18 :                 ereport(ERROR,
   15785             :                         (errcode(ERRCODE_DATATYPE_MISMATCH),
   15786             :                          errmsg("column \"%s\" in child table must be a generated column", parent_attname)));
   15787        6456 :             if (child_att->attgenerated && !parent_att->attgenerated)
   15788          12 :                 ereport(ERROR,
   15789             :                         (errcode(ERRCODE_DATATYPE_MISMATCH),
   15790             :                          errmsg("column \"%s\" in child table must not be a generated column", parent_attname)));
   15791             : 
   15792             :             /*
   15793             :              * Regular inheritance children are independent enough not to
   15794             :              * inherit identity columns.  But partitions are integral part of
   15795             :              * a partitioned table and inherit identity column.
   15796             :              */
   15797        6444 :             if (ispartition)
   15798        5984 :                 child_att->attidentity = parent_att->attidentity;
   15799             : 
   15800             :             /*
   15801             :              * OK, bump the child column's inheritance count.  (If we fail
   15802             :              * later on, this change will just roll back.)
   15803             :              */
   15804        6444 :             child_att->attinhcount++;
   15805        6444 :             if (child_att->attinhcount < 0)
   15806           0 :                 ereport(ERROR,
   15807             :                         errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
   15808             :                         errmsg("too many inheritance parents"));
   15809             : 
   15810             :             /*
   15811             :              * In case of partitions, we must enforce that value of attislocal
   15812             :              * is same in all partitions. (Note: there are only inherited
   15813             :              * attributes in partitions)
   15814             :              */
   15815        6444 :             if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
   15816             :             {
   15817             :                 Assert(child_att->attinhcount == 1);
   15818        5984 :                 child_att->attislocal = false;
   15819             :             }
   15820             : 
   15821        6444 :             CatalogTupleUpdate(attrrel, &tuple->t_self, tuple);
   15822        6444 :             heap_freetuple(tuple);
   15823             :         }
   15824             :         else
   15825             :         {
   15826          12 :             ereport(ERROR,
   15827             :                     (errcode(ERRCODE_DATATYPE_MISMATCH),
   15828             :                      errmsg("child table is missing column \"%s\"", parent_attname)));
   15829             :         }
   15830             :     }
   15831             : 
   15832        2720 :     table_close(attrrel, RowExclusiveLock);
   15833        2720 : }
   15834             : 
   15835             : /*
   15836             :  * Check constraints in child table match up with constraints in parent,
   15837             :  * and increment their coninhcount.
   15838             :  *
   15839             :  * Constraints that are marked ONLY in the parent are ignored.
   15840             :  *
   15841             :  * Called by CreateInheritance
   15842             :  *
   15843             :  * Currently all constraints in parent must be present in the child. One day we
   15844             :  * may consider adding new constraints like CREATE TABLE does.
   15845             :  *
   15846             :  * XXX This is O(N^2) which may be an issue with tables with hundreds of
   15847             :  * constraints. As long as tables have more like 10 constraints it shouldn't be
   15848             :  * a problem though. Even 100 constraints ought not be the end of the world.
   15849             :  *
   15850             :  * XXX See MergeWithExistingConstraint too if you change this code.
   15851             :  */
   15852             : static void
   15853        2720 : MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel)
   15854             : {
   15855             :     Relation    constraintrel;
   15856             :     SysScanDesc parent_scan;
   15857             :     ScanKeyData parent_key;
   15858             :     HeapTuple   parent_tuple;
   15859        2720 :     Oid         parent_relid = RelationGetRelid(parent_rel);
   15860             : 
   15861        2720 :     constraintrel = table_open(ConstraintRelationId, RowExclusiveLock);
   15862             : 
   15863             :     /* Outer loop scans through the parent's constraint definitions */
   15864        2720 :     ScanKeyInit(&parent_key,
   15865             :                 Anum_pg_constraint_conrelid,
   15866             :                 BTEqualStrategyNumber, F_OIDEQ,
   15867             :                 ObjectIdGetDatum(parent_relid));
   15868        2720 :     parent_scan = systable_beginscan(constraintrel, ConstraintRelidTypidNameIndexId,
   15869             :                                      true, NULL, 1, &parent_key);
   15870             : 
   15871        3528 :     while (HeapTupleIsValid(parent_tuple = systable_getnext(parent_scan)))
   15872             :     {
   15873         838 :         Form_pg_constraint parent_con = (Form_pg_constraint) GETSTRUCT(parent_tuple);
   15874             :         SysScanDesc child_scan;
   15875             :         ScanKeyData child_key;
   15876             :         HeapTuple   child_tuple;
   15877         838 :         bool        found = false;
   15878             : 
   15879         838 :         if (parent_con->contype != CONSTRAINT_CHECK)
   15880         724 :             continue;
   15881             : 
   15882             :         /* if the parent's constraint is marked NO INHERIT, it's not inherited */
   15883         134 :         if (parent_con->connoinherit)
   15884          20 :             continue;
   15885             : 
   15886             :         /* Search for a child constraint matching this one */
   15887         114 :         ScanKeyInit(&child_key,
   15888             :                     Anum_pg_constraint_conrelid,
   15889             :                     BTEqualStrategyNumber, F_OIDEQ,
   15890             :                     ObjectIdGetDatum(RelationGetRelid(child_rel)));
   15891         114 :         child_scan = systable_beginscan(constraintrel, ConstraintRelidTypidNameIndexId,
   15892             :                                         true, NULL, 1, &child_key);
   15893             : 
   15894         144 :         while (HeapTupleIsValid(child_tuple = systable_getnext(child_scan)))
   15895             :         {
   15896         120 :             Form_pg_constraint child_con = (Form_pg_constraint) GETSTRUCT(child_tuple);
   15897             :             HeapTuple   child_copy;
   15898             : 
   15899         120 :             if (child_con->contype != CONSTRAINT_CHECK)
   15900           0 :                 continue;
   15901             : 
   15902         120 :             if (strcmp(NameStr(parent_con->conname),
   15903         120 :                        NameStr(child_con->conname)) != 0)
   15904          30 :                 continue;
   15905             : 
   15906          90 :             if (!constraints_equivalent(parent_tuple, child_tuple, RelationGetDescr(constraintrel)))
   15907           6 :                 ereport(ERROR,
   15908             :                         (errcode(ERRCODE_DATATYPE_MISMATCH),
   15909             :                          errmsg("child table \"%s\" has different definition for check constraint \"%s\"",
   15910             :                                 RelationGetRelationName(child_rel), NameStr(parent_con->conname))));
   15911             : 
   15912             :             /* If the child constraint is "no inherit" then cannot merge */
   15913          84 :             if (child_con->connoinherit)
   15914           0 :                 ereport(ERROR,
   15915             :                         (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
   15916             :                          errmsg("constraint \"%s\" conflicts with non-inherited constraint on child table \"%s\"",
   15917             :                                 NameStr(child_con->conname), RelationGetRelationName(child_rel))));
   15918             : 
   15919             :             /*
   15920             :              * If the child constraint is "not valid" then cannot merge with a
   15921             :              * valid parent constraint
   15922             :              */
   15923          84 :             if (parent_con->convalidated && !child_con->convalidated)
   15924           0 :                 ereport(ERROR,
   15925             :                         (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
   15926             :                          errmsg("constraint \"%s\" conflicts with NOT VALID constraint on child table \"%s\"",
   15927             :                                 NameStr(child_con->conname), RelationGetRelationName(child_rel))));
   15928             : 
   15929             :             /*
   15930             :              * OK, bump the child constraint's inheritance count.  (If we fail
   15931             :              * later on, this change will just roll back.)
   15932             :              */
   15933          84 :             child_copy = heap_copytuple(child_tuple);
   15934          84 :             child_con = (Form_pg_constraint) GETSTRUCT(child_copy);
   15935          84 :             child_con->coninhcount++;
   15936          84 :             if (child_con->coninhcount < 0)
   15937           0 :                 ereport(ERROR,
   15938             :                         errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
   15939             :                         errmsg("too many inheritance parents"));
   15940             : 
   15941             :             /*
   15942             :              * In case of partitions, an inherited constraint must be
   15943             :              * inherited only once since it cannot have multiple parents and
   15944             :              * it is never considered local.
   15945             :              */
   15946          84 :             if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
   15947             :             {
   15948             :                 Assert(child_con->coninhcount == 1);
   15949          68 :                 child_con->conislocal = false;
   15950             :             }
   15951             : 
   15952          84 :             CatalogTupleUpdate(constraintrel, &child_copy->t_self, child_copy);
   15953          84 :             heap_freetuple(child_copy);
   15954             : 
   15955          84 :             found = true;
   15956          84 :             break;
   15957             :         }
   15958             : 
   15959         108 :         systable_endscan(child_scan);
   15960             : 
   15961         108 :         if (!found)
   15962          24 :             ereport(ERROR,
   15963             :                     (errcode(ERRCODE_DATATYPE_MISMATCH),
   15964             :                      errmsg("child table is missing constraint \"%s\"",
   15965             :                             NameStr(parent_con->conname))));
   15966             :     }
   15967             : 
   15968        2690 :     systable_endscan(parent_scan);
   15969        2690 :     table_close(constraintrel, RowExclusiveLock);
   15970        2690 : }
   15971             : 
   15972             : /*
   15973             :  * ALTER TABLE NO INHERIT
   15974             :  *
   15975             :  * Return value is the address of the relation that is no longer parent.
   15976             :  */
   15977             : static ObjectAddress
   15978          38 : ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode)
   15979             : {
   15980             :     ObjectAddress address;
   15981             :     Relation    parent_rel;
   15982             : 
   15983          38 :     if (rel->rd_rel->relispartition)
   15984           0 :         ereport(ERROR,
   15985             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   15986             :                  errmsg("cannot change inheritance of a partition")));
   15987             : 
   15988             :     /*
   15989             :      * AccessShareLock on the parent is probably enough, seeing that DROP
   15990             :      * TABLE doesn't lock parent tables at all.  We need some lock since we'll
   15991             :      * be inspecting the parent's schema.
   15992             :      */
   15993          38 :     parent_rel = table_openrv(parent, AccessShareLock);
   15994             : 
   15995             :     /*
   15996             :      * We don't bother to check ownership of the parent table --- ownership of
   15997             :      * the child is presumed enough rights.
   15998             :      */
   15999             : 
   16000             :     /* Off to RemoveInheritance() where most of the work happens */
   16001          38 :     RemoveInheritance(rel, parent_rel, false);
   16002             : 
   16003          32 :     ObjectAddressSet(address, RelationRelationId,
   16004             :                      RelationGetRelid(parent_rel));
   16005             : 
   16006             :     /* keep our lock on the parent relation until commit */
   16007          32 :     table_close(parent_rel, NoLock);
   16008             : 
   16009          32 :     return address;
   16010             : }
   16011             : 
   16012             : /*
   16013             :  * MarkInheritDetached
   16014             :  *
   16015             :  * Set inhdetachpending for a partition, for ATExecDetachPartition
   16016             :  * in concurrent mode.  While at it, verify that no other partition is
   16017             :  * already pending detach.
   16018             :  */
   16019             : static void
   16020         146 : MarkInheritDetached(Relation child_rel, Relation parent_rel)
   16021             : {
   16022             :     Relation    catalogRelation;
   16023             :     SysScanDesc scan;
   16024             :     ScanKeyData key;
   16025             :     HeapTuple   inheritsTuple;
   16026         146 :     bool        found = false;
   16027             : 
   16028             :     Assert(parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
   16029             : 
   16030             :     /*
   16031             :      * Find pg_inherits entries by inhparent.  (We need to scan them all in
   16032             :      * order to verify that no other partition is pending detach.)
   16033             :      */
   16034         146 :     catalogRelation = table_open(InheritsRelationId, RowExclusiveLock);
   16035         146 :     ScanKeyInit(&key,
   16036             :                 Anum_pg_inherits_inhparent,
   16037             :                 BTEqualStrategyNumber, F_OIDEQ,
   16038             :                 ObjectIdGetDatum(RelationGetRelid(parent_rel)));
   16039         146 :     scan = systable_beginscan(catalogRelation, InheritsParentIndexId,
   16040             :                               true, NULL, 1, &key);
   16041             : 
   16042         430 :     while (HeapTupleIsValid(inheritsTuple = systable_getnext(scan)))
   16043             :     {
   16044             :         Form_pg_inherits inhForm;
   16045             : 
   16046         286 :         inhForm = (Form_pg_inherits) GETSTRUCT(inheritsTuple);
   16047         286 :         if (inhForm->inhdetachpending)
   16048           2 :             ereport(ERROR,
   16049             :                     errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
   16050             :                     errmsg("partition \"%s\" already pending detach in partitioned table \"%s.%s\"",
   16051             :                            get_rel_name(inhForm->inhrelid),
   16052             :                            get_namespace_name(parent_rel->rd_rel->relnamespace),
   16053             :                            RelationGetRelationName(parent_rel)),
   16054             :                     errhint("Use ALTER TABLE ... DETACH PARTITION ... FINALIZE to complete the pending detach operation."));
   16055             : 
   16056         284 :         if (inhForm->inhrelid == RelationGetRelid(child_rel))
   16057             :         {
   16058             :             HeapTuple   newtup;
   16059             : 
   16060         144 :             newtup = heap_copytuple(inheritsTuple);
   16061         144 :             ((Form_pg_inherits) GETSTRUCT(newtup))->inhdetachpending = true;
   16062             : 
   16063         144 :             CatalogTupleUpdate(catalogRelation,
   16064             :                                &inheritsTuple->t_self,
   16065             :                                newtup);
   16066         144 :             found = true;
   16067         144 :             heap_freetuple(newtup);
   16068             :             /* keep looking, to ensure we catch others pending detach */
   16069             :         }
   16070             :     }
   16071             : 
   16072             :     /* Done */
   16073         144 :     systable_endscan(scan);
   16074         144 :     table_close(catalogRelation, RowExclusiveLock);
   16075             : 
   16076         144 :     if (!found)
   16077           0 :         ereport(ERROR,
   16078             :                 (errcode(ERRCODE_UNDEFINED_TABLE),
   16079             :                  errmsg("relation \"%s\" is not a partition of relation \"%s\"",
   16080             :                         RelationGetRelationName(child_rel),
   16081             :                         RelationGetRelationName(parent_rel))));
   16082         144 : }
   16083             : 
   16084             : /*
   16085             :  * RemoveInheritance
   16086             :  *
   16087             :  * Drop a parent from the child's parents. This just adjusts the attinhcount
   16088             :  * and attislocal of the columns and removes the pg_inherit and pg_depend
   16089             :  * entries.  expect_detached is passed down to DeleteInheritsTuple, q.v..
   16090             :  *
   16091             :  * If attinhcount goes to 0 then attislocal gets set to true. If it goes back
   16092             :  * up attislocal stays true, which means if a child is ever removed from a
   16093             :  * parent then its columns will never be automatically dropped which may
   16094             :  * surprise. But at least we'll never surprise by dropping columns someone
   16095             :  * isn't expecting to be dropped which would actually mean data loss.
   16096             :  *
   16097             :  * coninhcount and conislocal for inherited constraints are adjusted in
   16098             :  * exactly the same way.
   16099             :  *
   16100             :  * Common to ATExecDropInherit() and ATExecDetachPartition().
   16101             :  */
   16102             : static void
   16103         932 : RemoveInheritance(Relation child_rel, Relation parent_rel, bool expect_detached)
   16104             : {
   16105             :     Relation    catalogRelation;
   16106             :     SysScanDesc scan;
   16107             :     ScanKeyData key[3];
   16108             :     HeapTuple   attributeTuple,
   16109             :                 constraintTuple;
   16110             :     List       *connames;
   16111             :     bool        found;
   16112             :     bool        is_partitioning;
   16113             : 
   16114         932 :     is_partitioning = (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
   16115             : 
   16116         932 :     found = DeleteInheritsTuple(RelationGetRelid(child_rel),
   16117             :                                 RelationGetRelid(parent_rel),
   16118             :                                 expect_detached,
   16119         932 :                                 RelationGetRelationName(child_rel));
   16120         932 :     if (!found)
   16121             :     {
   16122          24 :         if (is_partitioning)
   16123          18 :             ereport(ERROR,
   16124             :                     (errcode(ERRCODE_UNDEFINED_TABLE),
   16125             :                      errmsg("relation \"%s\" is not a partition of relation \"%s\"",
   16126             :                             RelationGetRelationName(child_rel),
   16127             :                             RelationGetRelationName(parent_rel))));
   16128             :         else
   16129           6 :             ereport(ERROR,
   16130             :                     (errcode(ERRCODE_UNDEFINED_TABLE),
   16131             :                      errmsg("relation \"%s\" is not a parent of relation \"%s\"",
   16132             :                             RelationGetRelationName(parent_rel),
   16133             :                             RelationGetRelationName(child_rel))));
   16134             :     }
   16135             : 
   16136             :     /*
   16137             :      * Search through child columns looking for ones matching parent rel
   16138             :      */
   16139         908 :     catalogRelation = table_open(AttributeRelationId, RowExclusiveLock);
   16140         908 :     ScanKeyInit(&key[0],
   16141             :                 Anum_pg_attribute_attrelid,
   16142             :                 BTEqualStrategyNumber, F_OIDEQ,
   16143             :                 ObjectIdGetDatum(RelationGetRelid(child_rel)));
   16144         908 :     scan = systable_beginscan(catalogRelation, AttributeRelidNumIndexId,
   16145             :                               true, NULL, 1, key);
   16146        8402 :     while (HeapTupleIsValid(attributeTuple = systable_getnext(scan)))
   16147             :     {
   16148        7494 :         Form_pg_attribute att = (Form_pg_attribute) GETSTRUCT(attributeTuple);
   16149             : 
   16150             :         /* Ignore if dropped or not inherited */
   16151        7494 :         if (att->attisdropped)
   16152          36 :             continue;
   16153        7458 :         if (att->attinhcount <= 0)
   16154        5466 :             continue;
   16155             : 
   16156        1992 :         if (SearchSysCacheExistsAttName(RelationGetRelid(parent_rel),
   16157        1992 :                                         NameStr(att->attname)))
   16158             :         {
   16159             :             /* Decrement inhcount and possibly set islocal to true */
   16160        1980 :             HeapTuple   copyTuple = heap_copytuple(attributeTuple);
   16161        1980 :             Form_pg_attribute copy_att = (Form_pg_attribute) GETSTRUCT(copyTuple);
   16162             : 
   16163        1980 :             copy_att->attinhcount--;
   16164        1980 :             if (copy_att->attinhcount == 0)
   16165        1980 :                 copy_att->attislocal = true;
   16166             : 
   16167        1980 :             CatalogTupleUpdate(catalogRelation, &copyTuple->t_self, copyTuple);
   16168        1980 :             heap_freetuple(copyTuple);
   16169             :         }
   16170             :     }
   16171         908 :     systable_endscan(scan);
   16172         908 :     table_close(catalogRelation, RowExclusiveLock);
   16173             : 
   16174             :     /*
   16175             :      * Likewise, find inherited check constraints and disinherit them. To do
   16176             :      * this, we first need a list of the names of the parent's check
   16177             :      * constraints.  (We cheat a bit by only checking for name matches,
   16178             :      * assuming that the expressions will match.)
   16179             :      */
   16180         908 :     catalogRelation = table_open(ConstraintRelationId, RowExclusiveLock);
   16181         908 :     ScanKeyInit(&key[0],
   16182             :                 Anum_pg_constraint_conrelid,
   16183             :                 BTEqualStrategyNumber, F_OIDEQ,
   16184             :                 ObjectIdGetDatum(RelationGetRelid(parent_rel)));
   16185         908 :     scan = systable_beginscan(catalogRelation, ConstraintRelidTypidNameIndexId,
   16186             :                               true, NULL, 1, key);
   16187             : 
   16188         908 :     connames = NIL;
   16189             : 
   16190        1174 :     while (HeapTupleIsValid(constraintTuple = systable_getnext(scan)))
   16191             :     {
   16192         266 :         Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(constraintTuple);
   16193             : 
   16194         266 :         if (con->contype == CONSTRAINT_CHECK)
   16195          18 :             connames = lappend(connames, pstrdup(NameStr(con->conname)));
   16196             :     }
   16197             : 
   16198         908 :     systable_endscan(scan);
   16199             : 
   16200             :     /* Now scan the child's constraints */
   16201         908 :     ScanKeyInit(&key[0],
   16202             :                 Anum_pg_constraint_conrelid,
   16203             :                 BTEqualStrategyNumber, F_OIDEQ,
   16204             :                 ObjectIdGetDatum(RelationGetRelid(child_rel)));
   16205         908 :     scan = systable_beginscan(catalogRelation, ConstraintRelidTypidNameIndexId,
   16206             :                               true, NULL, 1, key);
   16207             : 
   16208        1382 :     while (HeapTupleIsValid(constraintTuple = systable_getnext(scan)))
   16209             :     {
   16210         474 :         Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(constraintTuple);
   16211             :         bool        match;
   16212             : 
   16213         474 :         if (con->contype != CONSTRAINT_CHECK)
   16214         296 :             continue;
   16215             : 
   16216         178 :         match = false;
   16217         368 :         foreach_ptr(char, chkname, connames)
   16218             :         {
   16219          30 :             if (strcmp(NameStr(con->conname), chkname) == 0)
   16220             :             {
   16221          18 :                 match = true;
   16222          18 :                 break;
   16223             :             }
   16224             :         }
   16225             : 
   16226         178 :         if (match)
   16227             :         {
   16228             :             /* Decrement inhcount and possibly set islocal to true */
   16229          18 :             HeapTuple   copyTuple = heap_copytuple(constraintTuple);
   16230          18 :             Form_pg_constraint copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
   16231             : 
   16232          18 :             if (copy_con->coninhcount <= 0) /* shouldn't happen */
   16233           0 :                 elog(ERROR, "relation %u has non-inherited constraint \"%s\"",
   16234             :                      RelationGetRelid(child_rel), NameStr(copy_con->conname));
   16235             : 
   16236          18 :             copy_con->coninhcount--;
   16237          18 :             if (copy_con->coninhcount == 0)
   16238          18 :                 copy_con->conislocal = true;
   16239             : 
   16240          18 :             CatalogTupleUpdate(catalogRelation, &copyTuple->t_self, copyTuple);
   16241          18 :             heap_freetuple(copyTuple);
   16242             :         }
   16243             :     }
   16244             : 
   16245         908 :     systable_endscan(scan);
   16246         908 :     table_close(catalogRelation, RowExclusiveLock);
   16247             : 
   16248         908 :     drop_parent_dependency(RelationGetRelid(child_rel),
   16249             :                            RelationRelationId,
   16250             :                            RelationGetRelid(parent_rel),
   16251             :                            child_dependency_type(is_partitioning));
   16252             : 
   16253             :     /*
   16254             :      * Post alter hook of this inherits. Since object_access_hook doesn't take
   16255             :      * multiple object identifiers, we relay oid of parent relation using
   16256             :      * auxiliary_id argument.
   16257             :      */
   16258         908 :     InvokeObjectPostAlterHookArg(InheritsRelationId,
   16259             :                                  RelationGetRelid(child_rel), 0,
   16260             :                                  RelationGetRelid(parent_rel), false);
   16261         908 : }
   16262             : 
   16263             : /*
   16264             :  * Drop the dependency created by StoreCatalogInheritance1 (CREATE TABLE
   16265             :  * INHERITS/ALTER TABLE INHERIT -- refclassid will be RelationRelationId) or
   16266             :  * heap_create_with_catalog (CREATE TABLE OF/ALTER TABLE OF -- refclassid will
   16267             :  * be TypeRelationId).  There's no convenient way to do this, so go trawling
   16268             :  * through pg_depend.
   16269             :  */
   16270             : static void
   16271         920 : drop_parent_dependency(Oid relid, Oid refclassid, Oid refobjid,
   16272             :                        DependencyType deptype)
   16273             : {
   16274             :     Relation    catalogRelation;
   16275             :     SysScanDesc scan;
   16276             :     ScanKeyData key[3];
   16277             :     HeapTuple   depTuple;
   16278             : 
   16279         920 :     catalogRelation = table_open(DependRelationId, RowExclusiveLock);
   16280             : 
   16281         920 :     ScanKeyInit(&key[0],
   16282             :                 Anum_pg_depend_classid,
   16283             :                 BTEqualStrategyNumber, F_OIDEQ,
   16284             :                 ObjectIdGetDatum(RelationRelationId));
   16285         920 :     ScanKeyInit(&key[1],
   16286             :                 Anum_pg_depend_objid,
   16287             :                 BTEqualStrategyNumber, F_OIDEQ,
   16288             :                 ObjectIdGetDatum(relid));
   16289         920 :     ScanKeyInit(&key[2],
   16290             :                 Anum_pg_depend_objsubid,
   16291             :                 BTEqualStrategyNumber, F_INT4EQ,
   16292             :                 Int32GetDatum(0));
   16293             : 
   16294         920 :     scan = systable_beginscan(catalogRelation, DependDependerIndexId, true,
   16295             :                               NULL, 3, key);
   16296             : 
   16297        2808 :     while (HeapTupleIsValid(depTuple = systable_getnext(scan)))
   16298             :     {
   16299        1888 :         Form_pg_depend dep = (Form_pg_depend) GETSTRUCT(depTuple);
   16300             : 
   16301        1888 :         if (dep->refclassid == refclassid &&
   16302         932 :             dep->refobjid == refobjid &&
   16303         920 :             dep->refobjsubid == 0 &&
   16304         920 :             dep->deptype == deptype)
   16305         920 :             CatalogTupleDelete(catalogRelation, &depTuple->t_self);
   16306             :     }
   16307             : 
   16308         920 :     systable_endscan(scan);
   16309         920 :     table_close(catalogRelation, RowExclusiveLock);
   16310         920 : }
   16311             : 
   16312             : /*
   16313             :  * ALTER TABLE OF
   16314             :  *
   16315             :  * Attach a table to a composite type, as though it had been created with CREATE
   16316             :  * TABLE OF.  All attname, atttypid, atttypmod and attcollation must match.  The
   16317             :  * subject table must not have inheritance parents.  These restrictions ensure
   16318             :  * that you cannot create a configuration impossible with CREATE TABLE OF alone.
   16319             :  *
   16320             :  * The address of the type is returned.
   16321             :  */
   16322             : static ObjectAddress
   16323          66 : ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode)
   16324             : {
   16325          66 :     Oid         relid = RelationGetRelid(rel);
   16326             :     Type        typetuple;
   16327             :     Form_pg_type typeform;
   16328             :     Oid         typeid;
   16329             :     Relation    inheritsRelation,
   16330             :                 relationRelation;
   16331             :     SysScanDesc scan;
   16332             :     ScanKeyData key;
   16333             :     AttrNumber  table_attno,
   16334             :                 type_attno;
   16335             :     TupleDesc   typeTupleDesc,
   16336             :                 tableTupleDesc;
   16337             :     ObjectAddress tableobj,
   16338             :                 typeobj;
   16339             :     HeapTuple   classtuple;
   16340             : 
   16341             :     /* Validate the type. */
   16342          66 :     typetuple = typenameType(NULL, ofTypename, NULL);
   16343          66 :     check_of_type(typetuple);
   16344          66 :     typeform = (Form_pg_type) GETSTRUCT(typetuple);
   16345          66 :     typeid = typeform->oid;
   16346             : 
   16347             :     /* Fail if the table has any inheritance parents. */
   16348          66 :     inheritsRelation = table_open(InheritsRelationId, AccessShareLock);
   16349          66 :     ScanKeyInit(&key,
   16350             :                 Anum_pg_inherits_inhrelid,
   16351             :                 BTEqualStrategyNumber, F_OIDEQ,
   16352             :                 ObjectIdGetDatum(relid));
   16353          66 :     scan = systable_beginscan(inheritsRelation, InheritsRelidSeqnoIndexId,
   16354             :                               true, NULL, 1, &key);
   16355          66 :     if (HeapTupleIsValid(systable_getnext(scan)))
   16356           6 :         ereport(ERROR,
   16357             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   16358             :                  errmsg("typed tables cannot inherit")));
   16359          60 :     systable_endscan(scan);
   16360          60 :     table_close(inheritsRelation, AccessShareLock);
   16361             : 
   16362             :     /*
   16363             :      * Check the tuple descriptors for compatibility.  Unlike inheritance, we
   16364             :      * require that the order also match.  However, attnotnull need not match.
   16365             :      */
   16366          60 :     typeTupleDesc = lookup_rowtype_tupdesc(typeid, -1);
   16367          60 :     tableTupleDesc = RelationGetDescr(rel);
   16368          60 :     table_attno = 1;
   16369         190 :     for (type_attno = 1; type_attno <= typeTupleDesc->natts; type_attno++)
   16370             :     {
   16371             :         Form_pg_attribute type_attr,
   16372             :                     table_attr;
   16373             :         const char *type_attname,
   16374             :                    *table_attname;
   16375             : 
   16376             :         /* Get the next non-dropped type attribute. */
   16377         154 :         type_attr = TupleDescAttr(typeTupleDesc, type_attno - 1);
   16378         154 :         if (type_attr->attisdropped)
   16379          44 :             continue;
   16380         110 :         type_attname = NameStr(type_attr->attname);
   16381             : 
   16382             :         /* Get the next non-dropped table attribute. */
   16383             :         do
   16384             :         {
   16385         122 :             if (table_attno > tableTupleDesc->natts)
   16386           6 :                 ereport(ERROR,
   16387             :                         (errcode(ERRCODE_DATATYPE_MISMATCH),
   16388             :                          errmsg("table is missing column \"%s\"",
   16389             :                                 type_attname)));
   16390         116 :             table_attr = TupleDescAttr(tableTupleDesc, table_attno - 1);
   16391         116 :             table_attno++;
   16392         116 :         } while (table_attr->attisdropped);
   16393         104 :         table_attname = NameStr(table_attr->attname);
   16394             : 
   16395             :         /* Compare name. */
   16396         104 :         if (strncmp(table_attname, type_attname, NAMEDATALEN) != 0)
   16397           6 :             ereport(ERROR,
   16398             :                     (errcode(ERRCODE_DATATYPE_MISMATCH),
   16399             :                      errmsg("table has column \"%s\" where type requires \"%s\"",
   16400             :                             table_attname, type_attname)));
   16401             : 
   16402             :         /* Compare type. */
   16403          98 :         if (table_attr->atttypid != type_attr->atttypid ||
   16404          92 :             table_attr->atttypmod != type_attr->atttypmod ||
   16405          86 :             table_attr->attcollation != type_attr->attcollation)
   16406          12 :             ereport(ERROR,
   16407             :                     (errcode(ERRCODE_DATATYPE_MISMATCH),
   16408             :                      errmsg("table \"%s\" has different type for column \"%s\"",
   16409             :                             RelationGetRelationName(rel), type_attname)));
   16410             :     }
   16411          36 :     ReleaseTupleDesc(typeTupleDesc);
   16412             : 
   16413             :     /* Any remaining columns at the end of the table had better be dropped. */
   16414          36 :     for (; table_attno <= tableTupleDesc->natts; table_attno++)
   16415             :     {
   16416           6 :         Form_pg_attribute table_attr = TupleDescAttr(tableTupleDesc,
   16417             :                                                      table_attno - 1);
   16418             : 
   16419           6 :         if (!table_attr->attisdropped)
   16420           6 :             ereport(ERROR,
   16421             :                     (errcode(ERRCODE_DATATYPE_MISMATCH),
   16422             :                      errmsg("table has extra column \"%s\"",
   16423             :                             NameStr(table_attr->attname))));
   16424             :     }
   16425             : 
   16426             :     /* If the table was already typed, drop the existing dependency. */
   16427          30 :     if (rel->rd_rel->reloftype)
   16428           6 :         drop_parent_dependency(relid, TypeRelationId, rel->rd_rel->reloftype,
   16429             :                                DEPENDENCY_NORMAL);
   16430             : 
   16431             :     /* Record a dependency on the new type. */
   16432          30 :     tableobj.classId = RelationRelationId;
   16433          30 :     tableobj.objectId = relid;
   16434          30 :     tableobj.objectSubId = 0;
   16435          30 :     typeobj.classId = TypeRelationId;
   16436          30 :     typeobj.objectId = typeid;
   16437          30 :     typeobj.objectSubId = 0;
   16438          30 :     recordDependencyOn(&tableobj, &typeobj, DEPENDENCY_NORMAL);
   16439             : 
   16440             :     /* Update pg_class.reloftype */
   16441          30 :     relationRelation = table_open(RelationRelationId, RowExclusiveLock);
   16442          30 :     classtuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
   16443          30 :     if (!HeapTupleIsValid(classtuple))
   16444           0 :         elog(ERROR, "cache lookup failed for relation %u", relid);
   16445          30 :     ((Form_pg_class) GETSTRUCT(classtuple))->reloftype = typeid;
   16446          30 :     CatalogTupleUpdate(relationRelation, &classtuple->t_self, classtuple);
   16447             : 
   16448          30 :     InvokeObjectPostAlterHook(RelationRelationId, relid, 0);
   16449             : 
   16450          30 :     heap_freetuple(classtuple);
   16451          30 :     table_close(relationRelation, RowExclusiveLock);
   16452             : 
   16453          30 :     ReleaseSysCache(typetuple);
   16454             : 
   16455          30 :     return typeobj;
   16456             : }
   16457             : 
   16458             : /*
   16459             :  * ALTER TABLE NOT OF
   16460             :  *
   16461             :  * Detach a typed table from its originating type.  Just clear reloftype and
   16462             :  * remove the dependency.
   16463             :  */
   16464             : static void
   16465           6 : ATExecDropOf(Relation rel, LOCKMODE lockmode)
   16466             : {
   16467           6 :     Oid         relid = RelationGetRelid(rel);
   16468             :     Relation    relationRelation;
   16469             :     HeapTuple   tuple;
   16470             : 
   16471           6 :     if (!OidIsValid(rel->rd_rel->reloftype))
   16472           0 :         ereport(ERROR,
   16473             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   16474             :                  errmsg("\"%s\" is not a typed table",
   16475             :                         RelationGetRelationName(rel))));
   16476             : 
   16477             :     /*
   16478             :      * We don't bother to check ownership of the type --- ownership of the
   16479             :      * table is presumed enough rights.  No lock required on the type, either.
   16480             :      */
   16481             : 
   16482           6 :     drop_parent_dependency(relid, TypeRelationId, rel->rd_rel->reloftype,
   16483             :                            DEPENDENCY_NORMAL);
   16484             : 
   16485             :     /* Clear pg_class.reloftype */
   16486           6 :     relationRelation = table_open(RelationRelationId, RowExclusiveLock);
   16487           6 :     tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
   16488           6 :     if (!HeapTupleIsValid(tuple))
   16489           0 :         elog(ERROR, "cache lookup failed for relation %u", relid);
   16490           6 :     ((Form_pg_class) GETSTRUCT(tuple))->reloftype = InvalidOid;
   16491           6 :     CatalogTupleUpdate(relationRelation, &tuple->t_self, tuple);
   16492             : 
   16493           6 :     InvokeObjectPostAlterHook(RelationRelationId, relid, 0);
   16494             : 
   16495           6 :     heap_freetuple(tuple);
   16496           6 :     table_close(relationRelation, RowExclusiveLock);
   16497           6 : }
   16498             : 
   16499             : /*
   16500             :  * relation_mark_replica_identity: Update a table's replica identity
   16501             :  *
   16502             :  * Iff ri_type = REPLICA_IDENTITY_INDEX, indexOid must be the Oid of a suitable
   16503             :  * index. Otherwise, it must be InvalidOid.
   16504             :  *
   16505             :  * Caller had better hold an exclusive lock on the relation, as the results
   16506             :  * of running two of these concurrently wouldn't be pretty.
   16507             :  */
   16508             : static void
   16509         388 : relation_mark_replica_identity(Relation rel, char ri_type, Oid indexOid,
   16510             :                                bool is_internal)
   16511             : {
   16512             :     Relation    pg_index;
   16513             :     Relation    pg_class;
   16514             :     HeapTuple   pg_class_tuple;
   16515             :     HeapTuple   pg_index_tuple;
   16516             :     Form_pg_class pg_class_form;
   16517             :     Form_pg_index pg_index_form;
   16518             :     ListCell   *index;
   16519             : 
   16520             :     /*
   16521             :      * Check whether relreplident has changed, and update it if so.
   16522             :      */
   16523         388 :     pg_class = table_open(RelationRelationId, RowExclusiveLock);
   16524         388 :     pg_class_tuple = SearchSysCacheCopy1(RELOID,
   16525             :                                          ObjectIdGetDatum(RelationGetRelid(rel)));
   16526         388 :     if (!HeapTupleIsValid(pg_class_tuple))
   16527           0 :         elog(ERROR, "cache lookup failed for relation \"%s\"",
   16528             :              RelationGetRelationName(rel));
   16529         388 :     pg_class_form = (Form_pg_class) GETSTRUCT(pg_class_tuple);
   16530         388 :     if (pg_class_form->relreplident != ri_type)
   16531             :     {
   16532         338 :         pg_class_form->relreplident = ri_type;
   16533         338 :         CatalogTupleUpdate(pg_class, &pg_class_tuple->t_self, pg_class_tuple);
   16534             :     }
   16535         388 :     table_close(pg_class, RowExclusiveLock);
   16536         388 :     heap_freetuple(pg_class_tuple);
   16537             : 
   16538             :     /*
   16539             :      * Update the per-index indisreplident flags correctly.
   16540             :      */
   16541         388 :     pg_index = table_open(IndexRelationId, RowExclusiveLock);
   16542        1048 :     foreach(index, RelationGetIndexList(rel))
   16543             :     {
   16544         660 :         Oid         thisIndexOid = lfirst_oid(index);
   16545         660 :         bool        dirty = false;
   16546             : 
   16547         660 :         pg_index_tuple = SearchSysCacheCopy1(INDEXRELID,
   16548             :                                              ObjectIdGetDatum(thisIndexOid));
   16549         660 :         if (!HeapTupleIsValid(pg_index_tuple))
   16550           0 :             elog(ERROR, "cache lookup failed for index %u", thisIndexOid);
   16551         660 :         pg_index_form = (Form_pg_index) GETSTRUCT(pg_index_tuple);
   16552             : 
   16553         660 :         if (thisIndexOid == indexOid)
   16554             :         {
   16555             :             /* Set the bit if not already set. */
   16556         214 :             if (!pg_index_form->indisreplident)
   16557             :             {
   16558         196 :                 dirty = true;
   16559         196 :                 pg_index_form->indisreplident = true;
   16560             :             }
   16561             :         }
   16562             :         else
   16563             :         {
   16564             :             /* Unset the bit if set. */
   16565         446 :             if (pg_index_form->indisreplident)
   16566             :             {
   16567          40 :                 dirty = true;
   16568          40 :                 pg_index_form->indisreplident = false;
   16569             :             }
   16570             :         }
   16571             : 
   16572         660 :         if (dirty)
   16573             :         {
   16574         236 :             CatalogTupleUpdate(pg_index, &pg_index_tuple->t_self, pg_index_tuple);
   16575         236 :             InvokeObjectPostAlterHookArg(IndexRelationId, thisIndexOid, 0,
   16576             :                                          InvalidOid, is_internal);
   16577             : 
   16578             :             /*
   16579             :              * Invalidate the relcache for the table, so that after we commit
   16580             :              * all sessions will refresh the table's replica identity index
   16581             :              * before attempting any UPDATE or DELETE on the table.  (If we
   16582             :              * changed the table's pg_class row above, then a relcache inval
   16583             :              * is already queued due to that; but we might not have.)
   16584             :              */
   16585         236 :             CacheInvalidateRelcache(rel);
   16586             :         }
   16587         660 :         heap_freetuple(pg_index_tuple);
   16588             :     }
   16589             : 
   16590         388 :     table_close(pg_index, RowExclusiveLock);
   16591         388 : }
   16592             : 
   16593             : /*
   16594             :  * ALTER TABLE <name> REPLICA IDENTITY ...
   16595             :  */
   16596             : static void
   16597         436 : ATExecReplicaIdentity(Relation rel, ReplicaIdentityStmt *stmt, LOCKMODE lockmode)
   16598             : {
   16599             :     Oid         indexOid;
   16600             :     Relation    indexRel;
   16601             :     int         key;
   16602             : 
   16603         436 :     if (stmt->identity_type == REPLICA_IDENTITY_DEFAULT)
   16604             :     {
   16605           6 :         relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
   16606           6 :         return;
   16607             :     }
   16608         430 :     else if (stmt->identity_type == REPLICA_IDENTITY_FULL)
   16609             :     {
   16610         132 :         relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
   16611         132 :         return;
   16612             :     }
   16613         298 :     else if (stmt->identity_type == REPLICA_IDENTITY_NOTHING)
   16614             :     {
   16615          36 :         relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
   16616          36 :         return;
   16617             :     }
   16618         262 :     else if (stmt->identity_type == REPLICA_IDENTITY_INDEX)
   16619             :     {
   16620             :          /* fallthrough */ ;
   16621             :     }
   16622             :     else
   16623           0 :         elog(ERROR, "unexpected identity type %u", stmt->identity_type);
   16624             : 
   16625             :     /* Check that the index exists */
   16626         262 :     indexOid = get_relname_relid(stmt->name, rel->rd_rel->relnamespace);
   16627         262 :     if (!OidIsValid(indexOid))
   16628           0 :         ereport(ERROR,
   16629             :                 (errcode(ERRCODE_UNDEFINED_OBJECT),
   16630             :                  errmsg("index \"%s\" for table \"%s\" does not exist",
   16631             :                         stmt->name, RelationGetRelationName(rel))));
   16632             : 
   16633         262 :     indexRel = index_open(indexOid, ShareLock);
   16634             : 
   16635             :     /* Check that the index is on the relation we're altering. */
   16636         262 :     if (indexRel->rd_index == NULL ||
   16637         262 :         indexRel->rd_index->indrelid != RelationGetRelid(rel))
   16638           6 :         ereport(ERROR,
   16639             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   16640             :                  errmsg("\"%s\" is not an index for table \"%s\"",
   16641             :                         RelationGetRelationName(indexRel),
   16642             :                         RelationGetRelationName(rel))));
   16643             :     /* The AM must support uniqueness, and the index must in fact be unique. */
   16644         256 :     if (!indexRel->rd_indam->amcanunique ||
   16645         250 :         !indexRel->rd_index->indisunique)
   16646          12 :         ereport(ERROR,
   16647             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   16648             :                  errmsg("cannot use non-unique index \"%s\" as replica identity",
   16649             :                         RelationGetRelationName(indexRel))));
   16650             :     /* Deferred indexes are not guaranteed to be always unique. */
   16651         244 :     if (!indexRel->rd_index->indimmediate)
   16652          12 :         ereport(ERROR,
   16653             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   16654             :                  errmsg("cannot use non-immediate index \"%s\" as replica identity",
   16655             :                         RelationGetRelationName(indexRel))));
   16656             :     /* Expression indexes aren't supported. */
   16657         232 :     if (RelationGetIndexExpressions(indexRel) != NIL)
   16658           6 :         ereport(ERROR,
   16659             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   16660             :                  errmsg("cannot use expression index \"%s\" as replica identity",
   16661             :                         RelationGetRelationName(indexRel))));
   16662             :     /* Predicate indexes aren't supported. */
   16663         226 :     if (RelationGetIndexPredicate(indexRel) != NIL)
   16664           6 :         ereport(ERROR,
   16665             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   16666             :                  errmsg("cannot use partial index \"%s\" as replica identity",
   16667             :                         RelationGetRelationName(indexRel))));
   16668             : 
   16669             :     /* Check index for nullable columns. */
   16670         480 :     for (key = 0; key < IndexRelationGetNumberOfKeyAttributes(indexRel); key++)
   16671             :     {
   16672         266 :         int16       attno = indexRel->rd_index->indkey.values[key];
   16673             :         Form_pg_attribute attr;
   16674             : 
   16675             :         /*
   16676             :          * Reject any other system columns.  (Going forward, we'll disallow
   16677             :          * indexes containing such columns in the first place, but they might
   16678             :          * exist in older branches.)
   16679             :          */
   16680         266 :         if (attno <= 0)
   16681           0 :             ereport(ERROR,
   16682             :                     (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
   16683             :                      errmsg("index \"%s\" cannot be used as replica identity because column %d is a system column",
   16684             :                             RelationGetRelationName(indexRel), attno)));
   16685             : 
   16686         266 :         attr = TupleDescAttr(rel->rd_att, attno - 1);
   16687         266 :         if (!attr->attnotnull)
   16688           6 :             ereport(ERROR,
   16689             :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   16690             :                      errmsg("index \"%s\" cannot be used as replica identity because column \"%s\" is nullable",
   16691             :                             RelationGetRelationName(indexRel),
   16692             :                             NameStr(attr->attname))));
   16693             :     }
   16694             : 
   16695             :     /* This index is suitable for use as a replica identity. Mark it. */
   16696         214 :     relation_mark_replica_identity(rel, stmt->identity_type, indexOid, true);
   16697             : 
   16698         214 :     index_close(indexRel, NoLock);
   16699             : }
   16700             : 
   16701             : /*
   16702             :  * ALTER TABLE ENABLE/DISABLE ROW LEVEL SECURITY
   16703             :  */
   16704             : static void
   16705         294 : ATExecSetRowSecurity(Relation rel, bool rls)
   16706             : {
   16707             :     Relation    pg_class;
   16708             :     Oid         relid;
   16709             :     HeapTuple   tuple;
   16710             : 
   16711         294 :     relid = RelationGetRelid(rel);
   16712             : 
   16713             :     /* Pull the record for this relation and update it */
   16714         294 :     pg_class = table_open(RelationRelationId, RowExclusiveLock);
   16715             : 
   16716         294 :     tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
   16717             : 
   16718         294 :     if (!HeapTupleIsValid(tuple))
   16719           0 :         elog(ERROR, "cache lookup failed for relation %u", relid);
   16720             : 
   16721         294 :     ((Form_pg_class) GETSTRUCT(tuple))->relrowsecurity = rls;
   16722         294 :     CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
   16723             : 
   16724         294 :     InvokeObjectPostAlterHook(RelationRelationId,
   16725             :                               RelationGetRelid(rel), 0);
   16726             : 
   16727         294 :     table_close(pg_class, RowExclusiveLock);
   16728         294 :     heap_freetuple(tuple);
   16729         294 : }
   16730             : 
   16731             : /*
   16732             :  * ALTER TABLE FORCE/NO FORCE ROW LEVEL SECURITY
   16733             :  */
   16734             : static void
   16735         120 : ATExecForceNoForceRowSecurity(Relation rel, bool force_rls)
   16736             : {
   16737             :     Relation    pg_class;
   16738             :     Oid         relid;
   16739             :     HeapTuple   tuple;
   16740             : 
   16741         120 :     relid = RelationGetRelid(rel);
   16742             : 
   16743         120 :     pg_class = table_open(RelationRelationId, RowExclusiveLock);
   16744             : 
   16745         120 :     tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
   16746             : 
   16747         120 :     if (!HeapTupleIsValid(tuple))
   16748           0 :         elog(ERROR, "cache lookup failed for relation %u", relid);
   16749             : 
   16750         120 :     ((Form_pg_class) GETSTRUCT(tuple))->relforcerowsecurity = force_rls;
   16751         120 :     CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
   16752             : 
   16753         120 :     InvokeObjectPostAlterHook(RelationRelationId,
   16754             :                               RelationGetRelid(rel), 0);
   16755             : 
   16756         120 :     table_close(pg_class, RowExclusiveLock);
   16757         120 :     heap_freetuple(tuple);
   16758         120 : }
   16759             : 
   16760             : /*
   16761             :  * ALTER FOREIGN TABLE <name> OPTIONS (...)
   16762             :  */
   16763             : static void
   16764          50 : ATExecGenericOptions(Relation rel, List *options)
   16765             : {
   16766             :     Relation    ftrel;
   16767             :     ForeignServer *server;
   16768             :     ForeignDataWrapper *fdw;
   16769             :     HeapTuple   tuple;
   16770             :     bool        isnull;
   16771             :     Datum       repl_val[Natts_pg_foreign_table];
   16772             :     bool        repl_null[Natts_pg_foreign_table];
   16773             :     bool        repl_repl[Natts_pg_foreign_table];
   16774             :     Datum       datum;
   16775             :     Form_pg_foreign_table tableform;
   16776             : 
   16777          50 :     if (options == NIL)
   16778           0 :         return;
   16779             : 
   16780          50 :     ftrel = table_open(ForeignTableRelationId, RowExclusiveLock);
   16781             : 
   16782          50 :     tuple = SearchSysCacheCopy1(FOREIGNTABLEREL,
   16783             :                                 ObjectIdGetDatum(rel->rd_id));
   16784          50 :     if (!HeapTupleIsValid(tuple))
   16785           0 :         ereport(ERROR,
   16786             :                 (errcode(ERRCODE_UNDEFINED_OBJECT),
   16787             :                  errmsg("foreign table \"%s\" does not exist",
   16788             :                         RelationGetRelationName(rel))));
   16789          50 :     tableform = (Form_pg_foreign_table) GETSTRUCT(tuple);
   16790          50 :     server = GetForeignServer(tableform->ftserver);
   16791          50 :     fdw = GetForeignDataWrapper(server->fdwid);
   16792             : 
   16793          50 :     memset(repl_val, 0, sizeof(repl_val));
   16794          50 :     memset(repl_null, false, sizeof(repl_null));
   16795          50 :     memset(repl_repl, false, sizeof(repl_repl));
   16796             : 
   16797             :     /* Extract the current options */
   16798          50 :     datum = SysCacheGetAttr(FOREIGNTABLEREL,
   16799             :                             tuple,
   16800             :                             Anum_pg_foreign_table_ftoptions,
   16801             :                             &isnull);
   16802          50 :     if (isnull)
   16803           4 :         datum = PointerGetDatum(NULL);
   16804             : 
   16805             :     /* Transform the options */
   16806          50 :     datum = transformGenericOptions(ForeignTableRelationId,
   16807             :                                     datum,
   16808             :                                     options,
   16809             :                                     fdw->fdwvalidator);
   16810             : 
   16811          48 :     if (PointerIsValid(DatumGetPointer(datum)))
   16812          48 :         repl_val[Anum_pg_foreign_table_ftoptions - 1] = datum;
   16813             :     else
   16814           0 :         repl_null[Anum_pg_foreign_table_ftoptions - 1] = true;
   16815             : 
   16816          48 :     repl_repl[Anum_pg_foreign_table_ftoptions - 1] = true;
   16817             : 
   16818             :     /* Everything looks good - update the tuple */
   16819             : 
   16820          48 :     tuple = heap_modify_tuple(tuple, RelationGetDescr(ftrel),
   16821             :                               repl_val, repl_null, repl_repl);
   16822             : 
   16823          48 :     CatalogTupleUpdate(ftrel, &tuple->t_self, tuple);
   16824             : 
   16825             :     /*
   16826             :      * Invalidate relcache so that all sessions will refresh any cached plans
   16827             :      * that might depend on the old options.
   16828             :      */
   16829          48 :     CacheInvalidateRelcache(rel);
   16830             : 
   16831          48 :     InvokeObjectPostAlterHook(ForeignTableRelationId,
   16832             :                               RelationGetRelid(rel), 0);
   16833             : 
   16834          48 :     table_close(ftrel, RowExclusiveLock);
   16835             : 
   16836          48 :     heap_freetuple(tuple);
   16837             : }
   16838             : 
   16839             : /*
   16840             :  * ALTER TABLE ALTER COLUMN SET COMPRESSION
   16841             :  *
   16842             :  * Return value is the address of the modified column
   16843             :  */
   16844             : static ObjectAddress
   16845          68 : ATExecSetCompression(Relation rel,
   16846             :                      const char *column,
   16847             :                      Node *newValue,
   16848             :                      LOCKMODE lockmode)
   16849             : {
   16850             :     Relation    attrel;
   16851             :     HeapTuple   tuple;
   16852             :     Form_pg_attribute atttableform;
   16853             :     AttrNumber  attnum;
   16854             :     char       *compression;
   16855             :     char        cmethod;
   16856             :     ObjectAddress address;
   16857             : 
   16858          68 :     compression = strVal(newValue);
   16859             : 
   16860          68 :     attrel = table_open(AttributeRelationId, RowExclusiveLock);
   16861             : 
   16862             :     /* copy the cache entry so we can scribble on it below */
   16863          68 :     tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), column);
   16864          68 :     if (!HeapTupleIsValid(tuple))
   16865           0 :         ereport(ERROR,
   16866             :                 (errcode(ERRCODE_UNDEFINED_COLUMN),
   16867             :                  errmsg("column \"%s\" of relation \"%s\" does not exist",
   16868             :                         column, RelationGetRelationName(rel))));
   16869             : 
   16870             :     /* prevent them from altering a system attribute */
   16871          68 :     atttableform = (Form_pg_attribute) GETSTRUCT(tuple);
   16872          68 :     attnum = atttableform->attnum;
   16873          68 :     if (attnum <= 0)
   16874           0 :         ereport(ERROR,
   16875             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   16876             :                  errmsg("cannot alter system column \"%s\"", column)));
   16877             : 
   16878             :     /*
   16879             :      * Check that column type is compressible, then get the attribute
   16880             :      * compression method code
   16881             :      */
   16882          68 :     cmethod = GetAttributeCompression(atttableform->atttypid, compression);
   16883             : 
   16884             :     /* update pg_attribute entry */
   16885          62 :     atttableform->attcompression = cmethod;
   16886          62 :     CatalogTupleUpdate(attrel, &tuple->t_self, tuple);
   16887             : 
   16888          62 :     InvokeObjectPostAlterHook(RelationRelationId,
   16889             :                               RelationGetRelid(rel),
   16890             :                               attnum);
   16891             : 
   16892             :     /*
   16893             :      * Apply the change to indexes as well (only for simple index columns,
   16894             :      * matching behavior of index.c ConstructTupleDescriptor()).
   16895             :      */
   16896          62 :     SetIndexStorageProperties(rel, attrel, attnum,
   16897             :                               false, 0,
   16898             :                               true, cmethod,
   16899             :                               lockmode);
   16900             : 
   16901          62 :     heap_freetuple(tuple);
   16902             : 
   16903          62 :     table_close(attrel, RowExclusiveLock);
   16904             : 
   16905             :     /* make changes visible */
   16906          62 :     CommandCounterIncrement();
   16907             : 
   16908          62 :     ObjectAddressSubSet(address, RelationRelationId,
   16909             :                         RelationGetRelid(rel), attnum);
   16910          62 :     return address;
   16911             : }
   16912             : 
   16913             : 
   16914             : /*
   16915             :  * Preparation phase for SET LOGGED/UNLOGGED
   16916             :  *
   16917             :  * This verifies that we're not trying to change a temp table.  Also,
   16918             :  * existing foreign key constraints are checked to avoid ending up with
   16919             :  * permanent tables referencing unlogged tables.
   16920             :  *
   16921             :  * Return value is false if the operation is a no-op (in which case the
   16922             :  * checks are skipped), otherwise true.
   16923             :  */
   16924             : static bool
   16925          88 : ATPrepChangePersistence(Relation rel, bool toLogged)
   16926             : {
   16927             :     Relation    pg_constraint;
   16928             :     HeapTuple   tuple;
   16929             :     SysScanDesc scan;
   16930             :     ScanKeyData skey[1];
   16931             : 
   16932             :     /*
   16933             :      * Disallow changing status for a temp table.  Also verify whether we can
   16934             :      * get away with doing nothing; in such cases we don't need to run the
   16935             :      * checks below, either.
   16936             :      */
   16937          88 :     switch (rel->rd_rel->relpersistence)
   16938             :     {
   16939           0 :         case RELPERSISTENCE_TEMP:
   16940           0 :             ereport(ERROR,
   16941             :                     (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
   16942             :                      errmsg("cannot change logged status of table \"%s\" because it is temporary",
   16943             :                             RelationGetRelationName(rel)),
   16944             :                      errtable(rel)));
   16945             :             break;
   16946          50 :         case RELPERSISTENCE_PERMANENT:
   16947          50 :             if (toLogged)
   16948             :                 /* nothing to do */
   16949           6 :                 return false;
   16950          44 :             break;
   16951          38 :         case RELPERSISTENCE_UNLOGGED:
   16952          38 :             if (!toLogged)
   16953             :                 /* nothing to do */
   16954           6 :                 return false;
   16955          32 :             break;
   16956             :     }
   16957             : 
   16958             :     /*
   16959             :      * Check that the table is not part of any publication when changing to
   16960             :      * UNLOGGED, as UNLOGGED tables can't be published.
   16961             :      */
   16962         120 :     if (!toLogged &&
   16963          44 :         GetRelationPublications(RelationGetRelid(rel)) != NIL)
   16964           0 :         ereport(ERROR,
   16965             :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
   16966             :                  errmsg("cannot change table \"%s\" to unlogged because it is part of a publication",
   16967             :                         RelationGetRelationName(rel)),
   16968             :                  errdetail("Unlogged relations cannot be replicated.")));
   16969             : 
   16970             :     /*
   16971             :      * Check existing foreign key constraints to preserve the invariant that
   16972             :      * permanent tables cannot reference unlogged ones.  Self-referencing
   16973             :      * foreign keys can safely be ignored.
   16974             :      */
   16975          76 :     pg_constraint = table_open(ConstraintRelationId, AccessShareLock);
   16976             : 
   16977             :     /*
   16978             :      * Scan conrelid if changing to permanent, else confrelid.  This also
   16979             :      * determines whether a useful index exists.
   16980             :      */
   16981          76 :     ScanKeyInit(&skey[0],
   16982             :                 toLogged ? Anum_pg_constraint_conrelid :
   16983             :                 Anum_pg_constraint_confrelid,
   16984             :                 BTEqualStrategyNumber, F_OIDEQ,
   16985             :                 ObjectIdGetDatum(RelationGetRelid(rel)));
   16986          76 :     scan = systable_beginscan(pg_constraint,
   16987             :                               toLogged ? ConstraintRelidTypidNameIndexId : InvalidOid,
   16988             :                               true, NULL, 1, skey);
   16989             : 
   16990         106 :     while (HeapTupleIsValid(tuple = systable_getnext(scan)))
   16991             :     {
   16992          42 :         Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tuple);
   16993             : 
   16994          42 :         if (con->contype == CONSTRAINT_FOREIGN)
   16995             :         {
   16996             :             Oid         foreignrelid;
   16997             :             Relation    foreignrel;
   16998             : 
   16999             :             /* the opposite end of what we used as scankey */
   17000          30 :             foreignrelid = toLogged ? con->confrelid : con->conrelid;
   17001             : 
   17002             :             /* ignore if self-referencing */
   17003          30 :             if (RelationGetRelid(rel) == foreignrelid)
   17004          12 :                 continue;
   17005             : 
   17006          18 :             foreignrel = relation_open(foreignrelid, AccessShareLock);
   17007             : 
   17008          18 :             if (toLogged)
   17009             :             {
   17010           6 :                 if (!RelationIsPermanent(foreignrel))
   17011           6 :                     ereport(ERROR,
   17012             :                             (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
   17013             :                              errmsg("could not change table \"%s\" to logged because it references unlogged table \"%s\"",
   17014             :                                     RelationGetRelationName(rel),
   17015             :                                     RelationGetRelationName(foreignrel)),
   17016             :                              errtableconstraint(rel, NameStr(con->conname))));
   17017             :             }
   17018             :             else
   17019             :             {
   17020          12 :                 if (RelationIsPermanent(foreignrel))
   17021           6 :                     ereport(ERROR,
   17022             :                             (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
   17023             :                              errmsg("could not change table \"%s\" to unlogged because it references logged table \"%s\"",
   17024             :                                     RelationGetRelationName(rel),
   17025             :                                     RelationGetRelationName(foreignrel)),
   17026             :                              errtableconstraint(rel, NameStr(con->conname))));
   17027             :             }
   17028             : 
   17029           6 :             relation_close(foreignrel, AccessShareLock);
   17030             :         }
   17031             :     }
   17032             : 
   17033          64 :     systable_endscan(scan);
   17034             : 
   17035          64 :     table_close(pg_constraint, AccessShareLock);
   17036             : 
   17037          64 :     return true;
   17038             : }
   17039             : 
   17040             : /*
   17041             :  * Execute ALTER TABLE SET SCHEMA
   17042             :  */
   17043             : ObjectAddress
   17044         104 : AlterTableNamespace(AlterObjectSchemaStmt *stmt, Oid *oldschema)
   17045             : {
   17046             :     Relation    rel;
   17047             :     Oid         relid;
   17048             :     Oid         oldNspOid;
   17049             :     Oid         nspOid;
   17050             :     RangeVar   *newrv;
   17051             :     ObjectAddresses *objsMoved;
   17052             :     ObjectAddress myself;
   17053             : 
   17054         104 :     relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
   17055         104 :                                      stmt->missing_ok ? RVR_MISSING_OK : 0,
   17056             :                                      RangeVarCallbackForAlterRelation,
   17057             :                                      (void *) stmt);
   17058             : 
   17059         102 :     if (!OidIsValid(relid))
   17060             :     {
   17061          12 :         ereport(NOTICE,
   17062             :                 (errmsg("relation \"%s\" does not exist, skipping",
   17063             :                         stmt->relation->relname)));
   17064          12 :         return InvalidObjectAddress;
   17065             :     }
   17066             : 
   17067          90 :     rel = relation_open(relid, NoLock);
   17068             : 
   17069          90 :     oldNspOid = RelationGetNamespace(rel);
   17070             : 
   17071             :     /* If it's an owned sequence, disallow moving it by itself. */
   17072          90 :     if (rel->rd_rel->relkind == RELKIND_SEQUENCE)
   17073             :     {
   17074             :         Oid         tableId;
   17075             :         int32       colId;
   17076             : 
   17077          10 :         if (sequenceIsOwned(relid, DEPENDENCY_AUTO, &tableId, &colId) ||
   17078           2 :             sequenceIsOwned(relid, DEPENDENCY_INTERNAL, &tableId, &colId))
   17079           6 :             ereport(ERROR,
   17080             :                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   17081             :                      errmsg("cannot move an owned sequence into another schema"),
   17082             :                      errdetail("Sequence \"%s\" is linked to table \"%s\".",
   17083             :                                RelationGetRelationName(rel),
   17084             :                                get_rel_name(tableId))));
   17085             :     }
   17086             : 
   17087             :     /* Get and lock schema OID and check its permissions. */
   17088          84 :     newrv = makeRangeVar(stmt->newschema, RelationGetRelationName(rel), -1);
   17089          84 :     nspOid = RangeVarGetAndCheckCreationNamespace(newrv, NoLock, NULL);
   17090             : 
   17091             :     /* common checks on switching namespaces */
   17092          84 :     CheckSetNamespace(oldNspOid, nspOid);
   17093             : 
   17094          84 :     objsMoved = new_object_addresses();
   17095          84 :     AlterTableNamespaceInternal(rel, oldNspOid, nspOid, objsMoved);
   17096          84 :     free_object_addresses(objsMoved);
   17097             : 
   17098          84 :     ObjectAddressSet(myself, RelationRelationId, relid);
   17099             : 
   17100          84 :     if (oldschema)
   17101          84 :         *oldschema = oldNspOid;
   17102             : 
   17103             :     /* close rel, but keep lock until commit */
   17104          84 :     relation_close(rel, NoLock);
   17105             : 
   17106          84 :     return myself;
   17107             : }
   17108             : 
   17109             : /*
   17110             :  * The guts of relocating a table or materialized view to another namespace:
   17111             :  * besides moving the relation itself, its dependent objects are relocated to
   17112             :  * the new schema.
   17113             :  */
   17114             : void
   17115          86 : AlterTableNamespaceInternal(Relation rel, Oid oldNspOid, Oid nspOid,
   17116             :                             ObjectAddresses *objsMoved)
   17117             : {
   17118             :     Relation    classRel;
   17119             : 
   17120             :     Assert(objsMoved != NULL);
   17121             : 
   17122             :     /* OK, modify the pg_class row and pg_depend entry */
   17123          86 :     classRel = table_open(RelationRelationId, RowExclusiveLock);
   17124             : 
   17125          86 :     AlterRelationNamespaceInternal(classRel, RelationGetRelid(rel), oldNspOid,
   17126             :                                    nspOid, true, objsMoved);
   17127             : 
   17128             :     /* Fix the table's row type too, if it has one */
   17129          86 :     if (OidIsValid(rel->rd_rel->reltype))
   17130          84 :         AlterTypeNamespaceInternal(rel->rd_rel->reltype, nspOid,
   17131             :                                    false,   /* isImplicitArray */
   17132             :                                    false,   /* ignoreDependent */
   17133             :                                    false,   /* errorOnTableType */
   17134             :                                    objsMoved);
   17135             : 
   17136             :     /* Fix other dependent stuff */
   17137          86 :     AlterIndexNamespaces(classRel, rel, oldNspOid, nspOid, objsMoved);
   17138          86 :     AlterSeqNamespaces(classRel, rel, oldNspOid, nspOid,
   17139             :                        objsMoved, AccessExclusiveLock);
   17140          86 :     AlterConstraintNamespaces(RelationGetRelid(rel), oldNspOid, nspOid,
   17141             :                               false, objsMoved);
   17142             : 
   17143          86 :     table_close(classRel, RowExclusiveLock);
   17144          86 : }
   17145             : 
   17146             : /*
   17147             :  * The guts of relocating a relation to another namespace: fix the pg_class
   17148             :  * entry, and the pg_depend entry if any.  Caller must already have
   17149             :  * opened and write-locked pg_class.
   17150             :  */
   17151             : void
   17152         188 : AlterRelationNamespaceInternal(Relation classRel, Oid relOid,
   17153             :                                Oid oldNspOid, Oid newNspOid,
   17154             :                                bool hasDependEntry,
   17155             :                                ObjectAddresses *objsMoved)
   17156             : {
   17157             :     HeapTuple   classTup;
   17158             :     Form_pg_class classForm;
   17159             :     ObjectAddress thisobj;
   17160         188 :     bool        already_done = false;
   17161             : 
   17162         188 :     classTup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relOid));
   17163         188 :     if (!HeapTupleIsValid(classTup))
   17164           0 :         elog(ERROR, "cache lookup failed for relation %u", relOid);
   17165         188 :     classForm = (Form_pg_class) GETSTRUCT(classTup);
   17166             : 
   17167             :     Assert(classForm->relnamespace == oldNspOid);
   17168             : 
   17169         188 :     thisobj.classId = RelationRelationId;
   17170         188 :     thisobj.objectId = relOid;
   17171         188 :     thisobj.objectSubId = 0;
   17172             : 
   17173             :     /*
   17174             :      * If the object has already been moved, don't move it again.  If it's
   17175             :      * already in the right place, don't move it, but still fire the object
   17176             :      * access hook.
   17177             :      */
   17178         188 :     already_done = object_address_present(&thisobj, objsMoved);
   17179         188 :     if (!already_done && oldNspOid != newNspOid)
   17180             :     {
   17181             :         /* check for duplicate name (more friendly than unique-index failure) */
   17182         146 :         if (get_relname_relid(NameStr(classForm->relname),
   17183             :                               newNspOid) != InvalidOid)
   17184           0 :             ereport(ERROR,
   17185             :                     (errcode(ERRCODE_DUPLICATE_TABLE),
   17186             :                      errmsg("relation \"%s\" already exists in schema \"%s\"",
   17187             :                             NameStr(classForm->relname),
   17188             :                             get_namespace_name(newNspOid))));
   17189             : 
   17190             :         /* classTup is a copy, so OK to scribble on */
   17191         146 :         classForm->relnamespace = newNspOid;
   17192             : 
   17193         146 :         CatalogTupleUpdate(classRel, &classTup->t_self, classTup);
   17194             : 
   17195             :         /* Update dependency on schema if caller said so */
   17196         250 :         if (hasDependEntry &&
   17197         104 :             changeDependencyFor(RelationRelationId,
   17198             :                                 relOid,
   17199             :                                 NamespaceRelationId,
   17200             :                                 oldNspOid,
   17201             :                                 newNspOid) != 1)
   17202           0 :             elog(ERROR, "could not change schema dependency for relation \"%s\"",
   17203             :                  NameStr(classForm->relname));
   17204             :     }
   17205         188 :     if (!already_done)
   17206             :     {
   17207         188 :         add_exact_object_address(&thisobj, objsMoved);
   17208             : 
   17209         188 :         InvokeObjectPostAlterHook(RelationRelationId, relOid, 0);
   17210             :     }
   17211             : 
   17212         188 :     heap_freetuple(classTup);
   17213         188 : }
   17214             : 
   17215             : /*
   17216             :  * Move all indexes for the specified relation to another namespace.
   17217             :  *
   17218             :  * Note: we assume adequate permission checking was done by the caller,
   17219             :  * and that the caller has a suitable lock on the owning relation.
   17220             :  */
   17221             : static void
   17222          86 : AlterIndexNamespaces(Relation classRel, Relation rel,
   17223             :                      Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved)
   17224             : {
   17225             :     List       *indexList;
   17226             :     ListCell   *l;
   17227             : 
   17228          86 :     indexList = RelationGetIndexList(rel);
   17229             : 
   17230         132 :     foreach(l, indexList)
   17231             :     {
   17232          46 :         Oid         indexOid = lfirst_oid(l);
   17233             :         ObjectAddress thisobj;
   17234             : 
   17235          46 :         thisobj.classId = RelationRelationId;
   17236          46 :         thisobj.objectId = indexOid;
   17237          46 :         thisobj.objectSubId = 0;
   17238             : 
   17239             :         /*
   17240             :          * Note: currently, the index will not have its own dependency on the
   17241             :          * namespace, so we don't need to do changeDependencyFor(). There's no
   17242             :          * row type in pg_type, either.
   17243             :          *
   17244             :          * XXX this objsMoved test may be pointless -- surely we have a single
   17245             :          * dependency link from a relation to each index?
   17246             :          */
   17247          46 :         if (!object_address_present(&thisobj, objsMoved))
   17248             :         {
   17249          46 :             AlterRelationNamespaceInternal(classRel, indexOid,
   17250             :                                            oldNspOid, newNspOid,
   17251             :                                            false, objsMoved);
   17252          46 :             add_exact_object_address(&thisobj, objsMoved);
   17253             :         }
   17254             :     }
   17255             : 
   17256          86 :     list_free(indexList);
   17257          86 : }
   17258             : 
   17259             : /*
   17260             :  * Move all identity and SERIAL-column sequences of the specified relation to another
   17261             :  * namespace.
   17262             :  *
   17263             :  * Note: we assume adequate permission checking was done by the caller,
   17264             :  * and that the caller has a suitable lock on the owning relation.
   17265             :  */
   17266             : static void
   17267          86 : AlterSeqNamespaces(Relation classRel, Relation rel,
   17268             :                    Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved,
   17269             :                    LOCKMODE lockmode)
   17270             : {
   17271             :     Relation    depRel;
   17272             :     SysScanDesc scan;
   17273             :     ScanKeyData key[2];
   17274             :     HeapTuple   tup;
   17275             : 
   17276             :     /*
   17277             :      * SERIAL sequences are those having an auto dependency on one of the
   17278             :      * table's columns (we don't care *which* column, exactly).
   17279             :      */
   17280          86 :     depRel = table_open(DependRelationId, AccessShareLock);
   17281             : 
   17282          86 :     ScanKeyInit(&key[0],
   17283             :                 Anum_pg_depend_refclassid,
   17284             :                 BTEqualStrategyNumber, F_OIDEQ,
   17285             :                 ObjectIdGetDatum(RelationRelationId));
   17286          86 :     ScanKeyInit(&key[1],
   17287             :                 Anum_pg_depend_refobjid,
   17288             :                 BTEqualStrategyNumber, F_OIDEQ,
   17289             :                 ObjectIdGetDatum(RelationGetRelid(rel)));
   17290             :     /* we leave refobjsubid unspecified */
   17291             : 
   17292          86 :     scan = systable_beginscan(depRel, DependReferenceIndexId, true,
   17293             :                               NULL, 2, key);
   17294             : 
   17295         554 :     while (HeapTupleIsValid(tup = systable_getnext(scan)))
   17296             :     {
   17297         468 :         Form_pg_depend depForm = (Form_pg_depend) GETSTRUCT(tup);
   17298             :         Relation    seqRel;
   17299             : 
   17300             :         /* skip dependencies other than auto dependencies on columns */
   17301         468 :         if (depForm->refobjsubid == 0 ||
   17302         320 :             depForm->classid != RelationRelationId ||
   17303          42 :             depForm->objsubid != 0 ||
   17304          42 :             !(depForm->deptype == DEPENDENCY_AUTO || depForm->deptype == DEPENDENCY_INTERNAL))
   17305         426 :             continue;
   17306             : 
   17307             :         /* Use relation_open just in case it's an index */
   17308          42 :         seqRel = relation_open(depForm->objid, lockmode);
   17309             : 
   17310             :         /* skip non-sequence relations */
   17311          42 :         if (RelationGetForm(seqRel)->relkind != RELKIND_SEQUENCE)
   17312             :         {
   17313             :             /* No need to keep the lock */
   17314           0 :             relation_close(seqRel, lockmode);
   17315           0 :             continue;
   17316             :         }
   17317             : 
   17318             :         /* Fix the pg_class and pg_depend entries */
   17319          42 :         AlterRelationNamespaceInternal(classRel, depForm->objid,
   17320             :                                        oldNspOid, newNspOid,
   17321             :                                        true, objsMoved);
   17322             : 
   17323             :         /*
   17324             :          * Sequences used to have entries in pg_type, but no longer do.  If we
   17325             :          * ever re-instate that, we'll need to move the pg_type entry to the
   17326             :          * new namespace, too (using AlterTypeNamespaceInternal).
   17327             :          */
   17328             :         Assert(RelationGetForm(seqRel)->reltype == InvalidOid);
   17329             : 
   17330             :         /* Now we can close it.  Keep the lock till end of transaction. */
   17331          42 :         relation_close(seqRel, NoLock);
   17332             :     }
   17333             : 
   17334          86 :     systable_endscan(scan);
   17335             : 
   17336          86 :     relation_close(depRel, AccessShareLock);
   17337          86 : }
   17338             : 
   17339             : 
   17340             : /*
   17341             :  * This code supports
   17342             :  *  CREATE TEMP TABLE ... ON COMMIT { DROP | PRESERVE ROWS | DELETE ROWS }
   17343             :  *
   17344             :  * Because we only support this for TEMP tables, it's sufficient to remember
   17345             :  * the state in a backend-local data structure.
   17346             :  */
   17347             : 
   17348             : /*
   17349             :  * Register a newly-created relation's ON COMMIT action.
   17350             :  */
   17351             : void
   17352         166 : register_on_commit_action(Oid relid, OnCommitAction action)
   17353             : {
   17354             :     OnCommitItem *oc;
   17355             :     MemoryContext oldcxt;
   17356             : 
   17357             :     /*
   17358             :      * We needn't bother registering the relation unless there is an ON COMMIT
   17359             :      * action we need to take.
   17360             :      */
   17361         166 :     if (action == ONCOMMIT_NOOP || action == ONCOMMIT_PRESERVE_ROWS)
   17362          24 :         return;
   17363             : 
   17364         142 :     oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
   17365             : 
   17366         142 :     oc = (OnCommitItem *) palloc(sizeof(OnCommitItem));
   17367         142 :     oc->relid = relid;
   17368         142 :     oc->oncommit = action;
   17369         142 :     oc->creating_subid = GetCurrentSubTransactionId();
   17370         142 :     oc->deleting_subid = InvalidSubTransactionId;
   17371             : 
   17372             :     /*
   17373             :      * We use lcons() here so that ON COMMIT actions are processed in reverse
   17374             :      * order of registration.  That might not be essential but it seems
   17375             :      * reasonable.
   17376             :      */
   17377         142 :     on_commits = lcons(oc, on_commits);
   17378             : 
   17379         142 :     MemoryContextSwitchTo(oldcxt);
   17380             : }
   17381             : 
   17382             : /*
   17383             :  * Unregister any ON COMMIT action when a relation is deleted.
   17384             :  *
   17385             :  * Actually, we only mark the OnCommitItem entry as to be deleted after commit.
   17386             :  */
   17387             : void
   17388       42490 : remove_on_commit_action(Oid relid)
   17389             : {
   17390             :     ListCell   *l;
   17391             : 
   17392       42624 :     foreach(l, on_commits)
   17393             :     {
   17394         264 :         OnCommitItem *oc = (OnCommitItem *) lfirst(l);
   17395             : 
   17396         264 :         if (oc->relid == relid)
   17397             :         {
   17398         130 :             oc->deleting_subid = GetCurrentSubTransactionId();
   17399         130 :             break;
   17400             :         }
   17401             :     }
   17402       42490 : }
   17403             : 
   17404             : /*
   17405             :  * Perform ON COMMIT actions.
   17406             :  *
   17407             :  * This is invoked just before actually committing, since it's possible
   17408             :  * to encounter errors.
   17409             :  */
   17410             : void
   17411      516338 : PreCommit_on_commit_actions(void)
   17412             : {
   17413             :     ListCell   *l;
   17414      516338 :     List       *oids_to_truncate = NIL;
   17415      516338 :     List       *oids_to_drop = NIL;
   17416             : 
   17417      517056 :     foreach(l, on_commits)
   17418             :     {
   17419         718 :         OnCommitItem *oc = (OnCommitItem *) lfirst(l);
   17420             : 
   17421             :         /* Ignore entry if already dropped in this xact */
   17422         718 :         if (oc->deleting_subid != InvalidSubTransactionId)
   17423          68 :             continue;
   17424             : 
   17425         650 :         switch (oc->oncommit)
   17426             :         {
   17427           0 :             case ONCOMMIT_NOOP:
   17428             :             case ONCOMMIT_PRESERVE_ROWS:
   17429             :                 /* Do nothing (there shouldn't be such entries, actually) */
   17430           0 :                 break;
   17431         600 :             case ONCOMMIT_DELETE_ROWS:
   17432             : 
   17433             :                 /*
   17434             :                  * If this transaction hasn't accessed any temporary
   17435             :                  * relations, we can skip truncating ON COMMIT DELETE ROWS
   17436             :                  * tables, as they must still be empty.
   17437             :                  */
   17438         600 :                 if ((MyXactFlags & XACT_FLAGS_ACCESSEDTEMPNAMESPACE))
   17439         400 :                     oids_to_truncate = lappend_oid(oids_to_truncate, oc->relid);
   17440         600 :                 break;
   17441          50 :             case ONCOMMIT_DROP:
   17442          50 :                 oids_to_drop = lappend_oid(oids_to_drop, oc->relid);
   17443          50 :                 break;
   17444             :         }
   17445         718 :     }
   17446             : 
   17447             :     /*
   17448             :      * Truncate relations before dropping so that all dependencies between
   17449             :      * relations are removed after they are worked on.  Doing it like this
   17450             :      * might be a waste as it is possible that a relation being truncated will
   17451             :      * be dropped anyway due to its parent being dropped, but this makes the
   17452             :      * code more robust because of not having to re-check that the relation
   17453             :      * exists at truncation time.
   17454             :      */
   17455      516338 :     if (oids_to_truncate != NIL)
   17456         334 :         heap_truncate(oids_to_truncate);
   17457             : 
   17458      516332 :     if (oids_to_drop != NIL)
   17459             :     {
   17460          44 :         ObjectAddresses *targetObjects = new_object_addresses();
   17461             : 
   17462          94 :         foreach(l, oids_to_drop)
   17463             :         {
   17464             :             ObjectAddress object;
   17465             : 
   17466          50 :             object.classId = RelationRelationId;
   17467          50 :             object.objectId = lfirst_oid(l);
   17468          50 :             object.objectSubId = 0;
   17469             : 
   17470             :             Assert(!object_address_present(&object, targetObjects));
   17471             : 
   17472          50 :             add_exact_object_address(&object, targetObjects);
   17473             :         }
   17474             : 
   17475             :         /*
   17476             :          * Object deletion might involve toast table access (to clean up
   17477             :          * toasted catalog entries), so ensure we have a valid snapshot.
   17478             :          */
   17479          44 :         PushActiveSnapshot(GetTransactionSnapshot());
   17480             : 
   17481             :         /*
   17482             :          * Since this is an automatic drop, rather than one directly initiated
   17483             :          * by the user, we pass the PERFORM_DELETION_INTERNAL flag.
   17484             :          */
   17485          44 :         performMultipleDeletions(targetObjects, DROP_CASCADE,
   17486             :                                  PERFORM_DELETION_INTERNAL | PERFORM_DELETION_QUIETLY);
   17487             : 
   17488          44 :         PopActiveSnapshot();
   17489             : 
   17490             : #ifdef USE_ASSERT_CHECKING
   17491             : 
   17492             :         /*
   17493             :          * Note that table deletion will call remove_on_commit_action, so the
   17494             :          * entry should get marked as deleted.
   17495             :          */
   17496             :         foreach(l, on_commits)
   17497             :         {
   17498             :             OnCommitItem *oc = (OnCommitItem *) lfirst(l);
   17499             : 
   17500             :             if (oc->oncommit != ONCOMMIT_DROP)
   17501             :                 continue;
   17502             : 
   17503             :             Assert(oc->deleting_subid != InvalidSubTransactionId);
   17504             :         }
   17505             : #endif
   17506             :     }
   17507      516332 : }
   17508             : 
   17509             : /*
   17510             :  * Post-commit or post-abort cleanup for ON COMMIT management.
   17511             :  *
   17512             :  * All we do here is remove no-longer-needed OnCommitItem entries.
   17513             :  *
   17514             :  * During commit, remove entries that were deleted during this transaction;
   17515             :  * during abort, remove those created during this transaction.
   17516             :  */
   17517             : void
   17518      560902 : AtEOXact_on_commit_actions(bool isCommit)
   17519             : {
   17520             :     ListCell   *cur_item;
   17521             : 
   17522      561650 :     foreach(cur_item, on_commits)
   17523             :     {
   17524         748 :         OnCommitItem *oc = (OnCommitItem *) lfirst(cur_item);
   17525             : 
   17526         850 :         if (isCommit ? oc->deleting_subid != InvalidSubTransactionId :
   17527         102 :             oc->creating_subid != InvalidSubTransactionId)
   17528             :         {
   17529             :             /* cur_item must be removed */
   17530         142 :             on_commits = foreach_delete_current(on_commits, cur_item);
   17531         142 :             pfree(oc);
   17532             :         }
   17533             :         else
   17534             :         {
   17535             :             /* cur_item must be preserved */
   17536         606 :             oc->creating_subid = InvalidSubTransactionId;
   17537         606 :             oc->deleting_subid = InvalidSubTransactionId;
   17538             :         }
   17539             :     }
   17540      560902 : }
   17541             : 
   17542             : /*
   17543             :  * Post-subcommit or post-subabort cleanup for ON COMMIT management.
   17544             :  *
   17545             :  * During subabort, we can immediately remove entries created during this
   17546             :  * subtransaction.  During subcommit, just relabel entries marked during
   17547             :  * this subtransaction as being the parent's responsibility.
   17548             :  */
   17549             : void
   17550       18086 : AtEOSubXact_on_commit_actions(bool isCommit, SubTransactionId mySubid,
   17551             :                               SubTransactionId parentSubid)
   17552             : {
   17553             :     ListCell   *cur_item;
   17554             : 
   17555       18086 :     foreach(cur_item, on_commits)
   17556             :     {
   17557           0 :         OnCommitItem *oc = (OnCommitItem *) lfirst(cur_item);
   17558             : 
   17559           0 :         if (!isCommit && oc->creating_subid == mySubid)
   17560             :         {
   17561             :             /* cur_item must be removed */
   17562           0 :             on_commits = foreach_delete_current(on_commits, cur_item);
   17563           0 :             pfree(oc);
   17564             :         }
   17565             :         else
   17566             :         {
   17567             :             /* cur_item must be preserved */
   17568           0 :             if (oc->creating_subid == mySubid)
   17569           0 :                 oc->creating_subid = parentSubid;
   17570           0 :             if (oc->deleting_subid == mySubid)
   17571           0 :                 oc->deleting_subid = isCommit ? parentSubid : InvalidSubTransactionId;
   17572             :         }
   17573             :     }
   17574       18086 : }
   17575             : 
   17576             : /*
   17577             :  * This is intended as a callback for RangeVarGetRelidExtended().  It allows
   17578             :  * the relation to be locked only if (1) it's a plain or partitioned table,
   17579             :  * materialized view, or TOAST table and (2) the current user is the owner (or
   17580             :  * the superuser) or has been granted MAINTAIN.  This meets the
   17581             :  * permission-checking needs of CLUSTER, REINDEX TABLE, and REFRESH
   17582             :  * MATERIALIZED VIEW; we expose it here so that it can be used by all.
   17583             :  */
   17584             : void
   17585         976 : RangeVarCallbackMaintainsTable(const RangeVar *relation,
   17586             :                                Oid relId, Oid oldRelId, void *arg)
   17587             : {
   17588             :     char        relkind;
   17589             :     AclResult   aclresult;
   17590             : 
   17591             :     /* Nothing to do if the relation was not found. */
   17592         976 :     if (!OidIsValid(relId))
   17593           6 :         return;
   17594             : 
   17595             :     /*
   17596             :      * If the relation does exist, check whether it's an index.  But note that
   17597             :      * the relation might have been dropped between the time we did the name
   17598             :      * lookup and now.  In that case, there's nothing to do.
   17599             :      */
   17600         970 :     relkind = get_rel_relkind(relId);
   17601         970 :     if (!relkind)
   17602           0 :         return;
   17603         970 :     if (relkind != RELKIND_RELATION && relkind != RELKIND_TOASTVALUE &&
   17604         138 :         relkind != RELKIND_MATVIEW && relkind != RELKIND_PARTITIONED_TABLE)
   17605          28 :         ereport(ERROR,
   17606             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   17607             :                  errmsg("\"%s\" is not a table or materialized view", relation->relname)));
   17608             : 
   17609             :     /* Check permissions */
   17610         942 :     aclresult = pg_class_aclcheck(relId, GetUserId(), ACL_MAINTAIN);
   17611         942 :     if (aclresult != ACLCHECK_OK)
   17612          30 :         aclcheck_error(aclresult,
   17613          30 :                        get_relkind_objtype(get_rel_relkind(relId)),
   17614          30 :                        relation->relname);
   17615             : }
   17616             : 
   17617             : /*
   17618             :  * Callback to RangeVarGetRelidExtended() for TRUNCATE processing.
   17619             :  */
   17620             : static void
   17621        1638 : RangeVarCallbackForTruncate(const RangeVar *relation,
   17622             :                             Oid relId, Oid oldRelId, void *arg)
   17623             : {
   17624             :     HeapTuple   tuple;
   17625             : 
   17626             :     /* Nothing to do if the relation was not found. */
   17627        1638 :     if (!OidIsValid(relId))
   17628           0 :         return;
   17629             : 
   17630        1638 :     tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relId));
   17631        1638 :     if (!HeapTupleIsValid(tuple))   /* should not happen */
   17632           0 :         elog(ERROR, "cache lookup failed for relation %u", relId);
   17633             : 
   17634        1638 :     truncate_check_rel(relId, (Form_pg_class) GETSTRUCT(tuple));
   17635        1634 :     truncate_check_perms(relId, (Form_pg_class) GETSTRUCT(tuple));
   17636             : 
   17637        1602 :     ReleaseSysCache(tuple);
   17638             : }
   17639             : 
   17640             : /*
   17641             :  * Callback for RangeVarGetRelidExtended().  Checks that the current user is
   17642             :  * the owner of the relation, or superuser.
   17643             :  */
   17644             : void
   17645       14426 : RangeVarCallbackOwnsRelation(const RangeVar *relation,
   17646             :                              Oid relId, Oid oldRelId, void *arg)
   17647             : {
   17648             :     HeapTuple   tuple;
   17649             : 
   17650             :     /* Nothing to do if the relation was not found. */
   17651       14426 :     if (!OidIsValid(relId))
   17652          14 :         return;
   17653             : 
   17654       14412 :     tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relId));
   17655       14412 :     if (!HeapTupleIsValid(tuple))   /* should not happen */
   17656           0 :         elog(ERROR, "cache lookup failed for relation %u", relId);
   17657             : 
   17658       14412 :     if (!object_ownercheck(RelationRelationId, relId, GetUserId()))
   17659           6 :         aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relId)),
   17660           6 :                        relation->relname);
   17661             : 
   17662       28692 :     if (!allowSystemTableMods &&
   17663       14286 :         IsSystemClass(relId, (Form_pg_class) GETSTRUCT(tuple)))
   17664           2 :         ereport(ERROR,
   17665             :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
   17666             :                  errmsg("permission denied: \"%s\" is a system catalog",
   17667             :                         relation->relname)));
   17668             : 
   17669       14404 :     ReleaseSysCache(tuple);
   17670             : }
   17671             : 
   17672             : /*
   17673             :  * Common RangeVarGetRelid callback for rename, set schema, and alter table
   17674             :  * processing.
   17675             :  */
   17676             : static void
   17677       28484 : RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid, Oid oldrelid,
   17678             :                                  void *arg)
   17679             : {
   17680       28484 :     Node       *stmt = (Node *) arg;
   17681             :     ObjectType  reltype;
   17682             :     HeapTuple   tuple;
   17683             :     Form_pg_class classform;
   17684             :     AclResult   aclresult;
   17685             :     char        relkind;
   17686             : 
   17687       28484 :     tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
   17688       28484 :     if (!HeapTupleIsValid(tuple))
   17689         232 :         return;                 /* concurrently dropped */
   17690       28252 :     classform = (Form_pg_class) GETSTRUCT(tuple);
   17691       28252 :     relkind = classform->relkind;
   17692             : 
   17693             :     /* Must own relation. */
   17694       28252 :     if (!object_ownercheck(RelationRelationId, relid, GetUserId()))
   17695          72 :         aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relid)), rv->relname);
   17696             : 
   17697             :     /* No system table modifications unless explicitly allowed. */
   17698       28180 :     if (!allowSystemTableMods && IsSystemClass(relid, classform))
   17699          28 :         ereport(ERROR,
   17700             :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
   17701             :                  errmsg("permission denied: \"%s\" is a system catalog",
   17702             :                         rv->relname)));
   17703             : 
   17704             :     /*
   17705             :      * Extract the specified relation type from the statement parse tree.
   17706             :      *
   17707             :      * Also, for ALTER .. RENAME, check permissions: the user must (still)
   17708             :      * have CREATE rights on the containing namespace.
   17709             :      */
   17710       28152 :     if (IsA(stmt, RenameStmt))
   17711             :     {
   17712         486 :         aclresult = object_aclcheck(NamespaceRelationId, classform->relnamespace,
   17713             :                                     GetUserId(), ACL_CREATE);
   17714         486 :         if (aclresult != ACLCHECK_OK)
   17715           0 :             aclcheck_error(aclresult, OBJECT_SCHEMA,
   17716           0 :                            get_namespace_name(classform->relnamespace));
   17717         486 :         reltype = ((RenameStmt *) stmt)->renameType;
   17718             :     }
   17719       27666 :     else if (IsA(stmt, AlterObjectSchemaStmt))
   17720          90 :         reltype = ((AlterObjectSchemaStmt *) stmt)->objectType;
   17721             : 
   17722       27576 :     else if (IsA(stmt, AlterTableStmt))
   17723       27576 :         reltype = ((AlterTableStmt *) stmt)->objtype;
   17724             :     else
   17725             :     {
   17726           0 :         elog(ERROR, "unrecognized node type: %d", (int) nodeTag(stmt));
   17727             :         reltype = OBJECT_TABLE; /* placate compiler */
   17728             :     }
   17729             : 
   17730             :     /*
   17731             :      * For compatibility with prior releases, we allow ALTER TABLE to be used
   17732             :      * with most other types of relations (but not composite types). We allow
   17733             :      * similar flexibility for ALTER INDEX in the case of RENAME, but not
   17734             :      * otherwise.  Otherwise, the user must select the correct form of the
   17735             :      * command for the relation at issue.
   17736             :      */
   17737       28152 :     if (reltype == OBJECT_SEQUENCE && relkind != RELKIND_SEQUENCE)
   17738           0 :         ereport(ERROR,
   17739             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   17740             :                  errmsg("\"%s\" is not a sequence", rv->relname)));
   17741             : 
   17742       28152 :     if (reltype == OBJECT_VIEW && relkind != RELKIND_VIEW)
   17743           0 :         ereport(ERROR,
   17744             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   17745             :                  errmsg("\"%s\" is not a view", rv->relname)));
   17746             : 
   17747       28152 :     if (reltype == OBJECT_MATVIEW && relkind != RELKIND_MATVIEW)
   17748           0 :         ereport(ERROR,
   17749             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   17750             :                  errmsg("\"%s\" is not a materialized view", rv->relname)));
   17751             : 
   17752       28152 :     if (reltype == OBJECT_FOREIGN_TABLE && relkind != RELKIND_FOREIGN_TABLE)
   17753           0 :         ereport(ERROR,
   17754             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   17755             :                  errmsg("\"%s\" is not a foreign table", rv->relname)));
   17756             : 
   17757       28152 :     if (reltype == OBJECT_TYPE && relkind != RELKIND_COMPOSITE_TYPE)
   17758           0 :         ereport(ERROR,
   17759             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   17760             :                  errmsg("\"%s\" is not a composite type", rv->relname)));
   17761             : 
   17762       28152 :     if (reltype == OBJECT_INDEX && relkind != RELKIND_INDEX &&
   17763             :         relkind != RELKIND_PARTITIONED_INDEX
   17764          32 :         && !IsA(stmt, RenameStmt))
   17765           6 :         ereport(ERROR,
   17766             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   17767             :                  errmsg("\"%s\" is not an index", rv->relname)));
   17768             : 
   17769             :     /*
   17770             :      * Don't allow ALTER TABLE on composite types. We want people to use ALTER
   17771             :      * TYPE for that.
   17772             :      */
   17773       28146 :     if (reltype != OBJECT_TYPE && relkind == RELKIND_COMPOSITE_TYPE)
   17774           0 :         ereport(ERROR,
   17775             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   17776             :                  errmsg("\"%s\" is a composite type", rv->relname),
   17777             :         /* translator: %s is an SQL ALTER command */
   17778             :                  errhint("Use %s instead.",
   17779             :                          "ALTER TYPE")));
   17780             : 
   17781             :     /*
   17782             :      * Don't allow ALTER TABLE .. SET SCHEMA on relations that can't be moved
   17783             :      * to a different schema, such as indexes and TOAST tables.
   17784             :      */
   17785       28146 :     if (IsA(stmt, AlterObjectSchemaStmt))
   17786             :     {
   17787          90 :         if (relkind == RELKIND_INDEX || relkind == RELKIND_PARTITIONED_INDEX)
   17788           0 :             ereport(ERROR,
   17789             :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   17790             :                      errmsg("cannot change schema of index \"%s\"",
   17791             :                             rv->relname),
   17792             :                      errhint("Change the schema of the table instead.")));
   17793          90 :         else if (relkind == RELKIND_COMPOSITE_TYPE)
   17794           0 :             ereport(ERROR,
   17795             :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   17796             :                      errmsg("cannot change schema of composite type \"%s\"",
   17797             :                             rv->relname),
   17798             :             /* translator: %s is an SQL ALTER command */
   17799             :                      errhint("Use %s instead.",
   17800             :                              "ALTER TYPE")));
   17801          90 :         else if (relkind == RELKIND_TOASTVALUE)
   17802           0 :             ereport(ERROR,
   17803             :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   17804             :                      errmsg("cannot change schema of TOAST table \"%s\"",
   17805             :                             rv->relname),
   17806             :                      errhint("Change the schema of the table instead.")));
   17807             :     }
   17808             : 
   17809       28146 :     ReleaseSysCache(tuple);
   17810             : }
   17811             : 
   17812             : /*
   17813             :  * Transform any expressions present in the partition key
   17814             :  *
   17815             :  * Returns a transformed PartitionSpec.
   17816             :  */
   17817             : static PartitionSpec *
   17818        4924 : transformPartitionSpec(Relation rel, PartitionSpec *partspec)
   17819             : {
   17820             :     PartitionSpec *newspec;
   17821             :     ParseState *pstate;
   17822             :     ParseNamespaceItem *nsitem;
   17823             :     ListCell   *l;
   17824             : 
   17825        4924 :     newspec = makeNode(PartitionSpec);
   17826             : 
   17827        4924 :     newspec->strategy = partspec->strategy;
   17828        4924 :     newspec->partParams = NIL;
   17829        4924 :     newspec->location = partspec->location;
   17830             : 
   17831             :     /* Check valid number of columns for strategy */
   17832        7248 :     if (partspec->strategy == PARTITION_STRATEGY_LIST &&
   17833        2324 :         list_length(partspec->partParams) != 1)
   17834           6 :         ereport(ERROR,
   17835             :                 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
   17836             :                  errmsg("cannot use \"list\" partition strategy with more than one column")));
   17837             : 
   17838             :     /*
   17839             :      * Create a dummy ParseState and insert the target relation as its sole
   17840             :      * rangetable entry.  We need a ParseState for transformExpr.
   17841             :      */
   17842        4918 :     pstate = make_parsestate(NULL);
   17843        4918 :     nsitem = addRangeTableEntryForRelation(pstate, rel, AccessShareLock,
   17844             :                                            NULL, false, true);
   17845        4918 :     addNSItemToQuery(pstate, nsitem, true, true, true);
   17846             : 
   17847             :     /* take care of any partition expressions */
   17848       10274 :     foreach(l, partspec->partParams)
   17849             :     {
   17850        5380 :         PartitionElem *pelem = lfirst_node(PartitionElem, l);
   17851             : 
   17852        5380 :         if (pelem->expr)
   17853             :         {
   17854             :             /* Copy, to avoid scribbling on the input */
   17855         298 :             pelem = copyObject(pelem);
   17856             : 
   17857             :             /* Now do parse transformation of the expression */
   17858         298 :             pelem->expr = transformExpr(pstate, pelem->expr,
   17859             :                                         EXPR_KIND_PARTITION_EXPRESSION);
   17860             : 
   17861             :             /* we have to fix its collations too */
   17862         274 :             assign_expr_collations(pstate, pelem->expr);
   17863             :         }
   17864             : 
   17865        5356 :         newspec->partParams = lappend(newspec->partParams, pelem);
   17866             :     }
   17867             : 
   17868        4894 :     return newspec;
   17869             : }
   17870             : 
   17871             : /*
   17872             :  * Compute per-partition-column information from a list of PartitionElems.
   17873             :  * Expressions in the PartitionElems must be parse-analyzed already.
   17874             :  */
   17875             : static void
   17876        4894 : ComputePartitionAttrs(ParseState *pstate, Relation rel, List *partParams, AttrNumber *partattrs,
   17877             :                       List **partexprs, Oid *partopclass, Oid *partcollation,
   17878             :                       PartitionStrategy strategy)
   17879             : {
   17880             :     int         attn;
   17881             :     ListCell   *lc;
   17882             :     Oid         am_oid;
   17883             : 
   17884        4894 :     attn = 0;
   17885       10166 :     foreach(lc, partParams)
   17886             :     {
   17887        5356 :         PartitionElem *pelem = lfirst_node(PartitionElem, lc);
   17888             :         Oid         atttype;
   17889             :         Oid         attcollation;
   17890             : 
   17891        5356 :         if (pelem->name != NULL)
   17892             :         {
   17893             :             /* Simple attribute reference */
   17894             :             HeapTuple   atttuple;
   17895             :             Form_pg_attribute attform;
   17896             : 
   17897        5082 :             atttuple = SearchSysCacheAttName(RelationGetRelid(rel),
   17898        5082 :                                              pelem->name);
   17899        5082 :             if (!HeapTupleIsValid(atttuple))
   17900          12 :                 ereport(ERROR,
   17901             :                         (errcode(ERRCODE_UNDEFINED_COLUMN),
   17902             :                          errmsg("column \"%s\" named in partition key does not exist",
   17903             :                                 pelem->name),
   17904             :                          parser_errposition(pstate, pelem->location)));
   17905        5070 :             attform = (Form_pg_attribute) GETSTRUCT(atttuple);
   17906             : 
   17907        5070 :             if (attform->attnum <= 0)
   17908           6 :                 ereport(ERROR,
   17909             :                         (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
   17910             :                          errmsg("cannot use system column \"%s\" in partition key",
   17911             :                                 pelem->name),
   17912             :                          parser_errposition(pstate, pelem->location)));
   17913             : 
   17914             :             /*
   17915             :              * Generated columns cannot work: They are computed after BEFORE
   17916             :              * triggers, but partition routing is done before all triggers.
   17917             :              */
   17918        5064 :             if (attform->attgenerated)
   17919           6 :                 ereport(ERROR,
   17920             :                         (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
   17921             :                          errmsg("cannot use generated column in partition key"),
   17922             :                          errdetail("Column \"%s\" is a generated column.",
   17923             :                                    pelem->name),
   17924             :                          parser_errposition(pstate, pelem->location)));
   17925             : 
   17926        5058 :             partattrs[attn] = attform->attnum;
   17927        5058 :             atttype = attform->atttypid;
   17928        5058 :             attcollation = attform->attcollation;
   17929        5058 :             ReleaseSysCache(atttuple);
   17930             :         }
   17931             :         else
   17932             :         {
   17933             :             /* Expression */
   17934         274 :             Node       *expr = pelem->expr;
   17935             :             char        partattname[16];
   17936             : 
   17937             :             Assert(expr != NULL);
   17938         274 :             atttype = exprType(expr);
   17939         274 :             attcollation = exprCollation(expr);
   17940             : 
   17941             :             /*
   17942             :              * The expression must be of a storable type (e.g., not RECORD).
   17943             :              * The test is the same as for whether a table column is of a safe
   17944             :              * type (which is why we needn't check for the non-expression
   17945             :              * case).
   17946             :              */
   17947         274 :             snprintf(partattname, sizeof(partattname), "%d", attn + 1);
   17948         274 :             CheckAttributeType(partattname,
   17949             :                                atttype, attcollation,
   17950             :                                NIL, CHKATYPE_IS_PARTKEY);
   17951             : 
   17952             :             /*
   17953             :              * Strip any top-level COLLATE clause.  This ensures that we treat
   17954             :              * "x COLLATE y" and "(x COLLATE y)" alike.
   17955             :              */
   17956         262 :             while (IsA(expr, CollateExpr))
   17957           0 :                 expr = (Node *) ((CollateExpr *) expr)->arg;
   17958             : 
   17959         262 :             if (IsA(expr, Var) &&
   17960          12 :                 ((Var *) expr)->varattno > 0)
   17961             :             {
   17962             :                 /*
   17963             :                  * User wrote "(column)" or "(column COLLATE something)".
   17964             :                  * Treat it like simple attribute anyway.
   17965             :                  */
   17966           6 :                 partattrs[attn] = ((Var *) expr)->varattno;
   17967             :             }
   17968             :             else
   17969             :             {
   17970         256 :                 Bitmapset  *expr_attrs = NULL;
   17971             :                 int         i;
   17972             : 
   17973         256 :                 partattrs[attn] = 0;    /* marks the column as expression */
   17974         256 :                 *partexprs = lappend(*partexprs, expr);
   17975             : 
   17976             :                 /*
   17977             :                  * transformPartitionSpec() should have already rejected
   17978             :                  * subqueries, aggregates, window functions, and SRFs, based
   17979             :                  * on the EXPR_KIND_ for partition expressions.
   17980             :                  */
   17981             : 
   17982             :                 /*
   17983             :                  * Cannot allow system column references, since that would
   17984             :                  * make partition routing impossible: their values won't be
   17985             :                  * known yet when we need to do that.
   17986             :                  */
   17987         256 :                 pull_varattnos(expr, 1, &expr_attrs);
   17988        2048 :                 for (i = FirstLowInvalidHeapAttributeNumber; i < 0; i++)
   17989             :                 {
   17990        1792 :                     if (bms_is_member(i - FirstLowInvalidHeapAttributeNumber,
   17991             :                                       expr_attrs))
   17992           0 :                         ereport(ERROR,
   17993             :                                 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
   17994             :                                  errmsg("partition key expressions cannot contain system column references")));
   17995             :                 }
   17996             : 
   17997             :                 /*
   17998             :                  * Generated columns cannot work: They are computed after
   17999             :                  * BEFORE triggers, but partition routing is done before all
   18000             :                  * triggers.
   18001             :                  */
   18002         256 :                 i = -1;
   18003         564 :                 while ((i = bms_next_member(expr_attrs, i)) >= 0)
   18004             :                 {
   18005         314 :                     AttrNumber  attno = i + FirstLowInvalidHeapAttributeNumber;
   18006             : 
   18007         314 :                     if (attno > 0 &&
   18008         308 :                         TupleDescAttr(RelationGetDescr(rel), attno - 1)->attgenerated)
   18009           6 :                         ereport(ERROR,
   18010             :                                 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
   18011             :                                  errmsg("cannot use generated column in partition key"),
   18012             :                                  errdetail("Column \"%s\" is a generated column.",
   18013             :                                            get_attname(RelationGetRelid(rel), attno, false)),
   18014             :                                  parser_errposition(pstate, pelem->location)));
   18015             :                 }
   18016             : 
   18017             :                 /*
   18018             :                  * Preprocess the expression before checking for mutability.
   18019             :                  * This is essential for the reasons described in
   18020             :                  * contain_mutable_functions_after_planning.  However, we call
   18021             :                  * expression_planner for ourselves rather than using that
   18022             :                  * function, because if constant-folding reduces the
   18023             :                  * expression to a constant, we'd like to know that so we can
   18024             :                  * complain below.
   18025             :                  *
   18026             :                  * Like contain_mutable_functions_after_planning, assume that
   18027             :                  * expression_planner won't scribble on its input, so this
   18028             :                  * won't affect the partexprs entry we saved above.
   18029             :                  */
   18030         250 :                 expr = (Node *) expression_planner((Expr *) expr);
   18031             : 
   18032             :                 /*
   18033             :                  * Partition expressions cannot contain mutable functions,
   18034             :                  * because a given row must always map to the same partition
   18035             :                  * as long as there is no change in the partition boundary
   18036             :                  * structure.
   18037             :                  */
   18038         250 :                 if (contain_mutable_functions(expr))
   18039           6 :                     ereport(ERROR,
   18040             :                             (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
   18041             :                              errmsg("functions in partition key expression must be marked IMMUTABLE")));
   18042             : 
   18043             :                 /*
   18044             :                  * While it is not exactly *wrong* for a partition expression
   18045             :                  * to be a constant, it seems better to reject such keys.
   18046             :                  */
   18047         244 :                 if (IsA(expr, Const))
   18048          12 :                     ereport(ERROR,
   18049             :                             (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
   18050             :                              errmsg("cannot use constant expression as partition key")));
   18051             :             }
   18052             :         }
   18053             : 
   18054             :         /*
   18055             :          * Apply collation override if any
   18056             :          */
   18057        5296 :         if (pelem->collation)
   18058          30 :             attcollation = get_collation_oid(pelem->collation, false);
   18059             : 
   18060             :         /*
   18061             :          * Check we have a collation iff it's a collatable type.  The only
   18062             :          * expected failures here are (1) COLLATE applied to a noncollatable
   18063             :          * type, or (2) partition expression had an unresolved collation. But
   18064             :          * we might as well code this to be a complete consistency check.
   18065             :          */
   18066        5296 :         if (type_is_collatable(atttype))
   18067             :         {
   18068         626 :             if (!OidIsValid(attcollation))
   18069           0 :                 ereport(ERROR,
   18070             :                         (errcode(ERRCODE_INDETERMINATE_COLLATION),
   18071             :                          errmsg("could not determine which collation to use for partition expression"),
   18072             :                          errhint("Use the COLLATE clause to set the collation explicitly.")));
   18073             :         }
   18074             :         else
   18075             :         {
   18076        4670 :             if (OidIsValid(attcollation))
   18077           0 :                 ereport(ERROR,
   18078             :                         (errcode(ERRCODE_DATATYPE_MISMATCH),
   18079             :                          errmsg("collations are not supported by type %s",
   18080             :                                 format_type_be(atttype))));
   18081             :         }
   18082             : 
   18083        5296 :         partcollation[attn] = attcollation;
   18084             : 
   18085             :         /*
   18086             :          * Identify the appropriate operator class.  For list and range
   18087             :          * partitioning, we use a btree operator class; hash partitioning uses
   18088             :          * a hash operator class.
   18089             :          */
   18090        5296 :         if (strategy == PARTITION_STRATEGY_HASH)
   18091         288 :             am_oid = HASH_AM_OID;
   18092             :         else
   18093        5008 :             am_oid = BTREE_AM_OID;
   18094             : 
   18095        5296 :         if (!pelem->opclass)
   18096             :         {
   18097        5164 :             partopclass[attn] = GetDefaultOpClass(atttype, am_oid);
   18098             : 
   18099        5164 :             if (!OidIsValid(partopclass[attn]))
   18100             :             {
   18101          12 :                 if (strategy == PARTITION_STRATEGY_HASH)
   18102           0 :                     ereport(ERROR,
   18103             :                             (errcode(ERRCODE_UNDEFINED_OBJECT),
   18104             :                              errmsg("data type %s has no default operator class for access method \"%s\"",
   18105             :                                     format_type_be(atttype), "hash"),
   18106             :                              errhint("You must specify a hash operator class or define a default hash operator class for the data type.")));
   18107             :                 else
   18108          12 :                     ereport(ERROR,
   18109             :                             (errcode(ERRCODE_UNDEFINED_OBJECT),
   18110             :                              errmsg("data type %s has no default operator class for access method \"%s\"",
   18111             :                                     format_type_be(atttype), "btree"),
   18112             :                              errhint("You must specify a btree operator class or define a default btree operator class for the data type.")));
   18113             :             }
   18114             :         }
   18115             :         else
   18116         132 :             partopclass[attn] = ResolveOpClass(pelem->opclass,
   18117             :                                                atttype,
   18118             :                                                am_oid == HASH_AM_OID ? "hash" : "btree",
   18119             :                                                am_oid);
   18120             : 
   18121        5272 :         attn++;
   18122             :     }
   18123        4810 : }
   18124             : 
   18125             : /*
   18126             :  * PartConstraintImpliedByRelConstraint
   18127             :  *      Do scanrel's existing constraints imply the partition constraint?
   18128             :  *
   18129             :  * "Existing constraints" include its check constraints and column-level
   18130             :  * not-null constraints.  partConstraint describes the partition constraint,
   18131             :  * in implicit-AND form.
   18132             :  */
   18133             : bool
   18134        3060 : PartConstraintImpliedByRelConstraint(Relation scanrel,
   18135             :                                      List *partConstraint)
   18136             : {
   18137        3060 :     List       *existConstraint = NIL;
   18138        3060 :     TupleConstr *constr = RelationGetDescr(scanrel)->constr;
   18139             :     int         i;
   18140             : 
   18141        3060 :     if (constr && constr->has_not_null)
   18142             :     {
   18143         740 :         int         natts = scanrel->rd_att->natts;
   18144             : 
   18145        2374 :         for (i = 1; i <= natts; i++)
   18146             :         {
   18147        1634 :             Form_pg_attribute att = TupleDescAttr(scanrel->rd_att, i - 1);
   18148             : 
   18149        1634 :             if (att->attnotnull && !att->attisdropped)
   18150             :             {
   18151         964 :                 NullTest   *ntest = makeNode(NullTest);
   18152             : 
   18153         964 :                 ntest->arg = (Expr *) makeVar(1,
   18154             :                                               i,
   18155             :                                               att->atttypid,
   18156             :                                               att->atttypmod,
   18157             :                                               att->attcollation,
   18158             :                                               0);
   18159         964 :                 ntest->nulltesttype = IS_NOT_NULL;
   18160             : 
   18161             :                 /*
   18162             :                  * argisrow=false is correct even for a composite column,
   18163             :                  * because attnotnull does not represent a SQL-spec IS NOT
   18164             :                  * NULL test in such a case, just IS DISTINCT FROM NULL.
   18165             :                  */
   18166         964 :                 ntest->argisrow = false;
   18167         964 :                 ntest->location = -1;
   18168         964 :                 existConstraint = lappend(existConstraint, ntest);
   18169             :             }
   18170             :         }
   18171             :     }
   18172             : 
   18173        3060 :     return ConstraintImpliedByRelConstraint(scanrel, partConstraint, existConstraint);
   18174             : }
   18175             : 
   18176             : /*
   18177             :  * ConstraintImpliedByRelConstraint
   18178             :  *      Do scanrel's existing constraints imply the given constraint?
   18179             :  *
   18180             :  * testConstraint is the constraint to validate. provenConstraint is a
   18181             :  * caller-provided list of conditions which this function may assume
   18182             :  * to be true. Both provenConstraint and testConstraint must be in
   18183             :  * implicit-AND form, must only contain immutable clauses, and must
   18184             :  * contain only Vars with varno = 1.
   18185             :  */
   18186             : bool
   18187        3896 : ConstraintImpliedByRelConstraint(Relation scanrel, List *testConstraint, List *provenConstraint)
   18188             : {
   18189        3896 :     List       *existConstraint = list_copy(provenConstraint);
   18190        3896 :     TupleConstr *constr = RelationGetDescr(scanrel)->constr;
   18191             :     int         num_check,
   18192             :                 i;
   18193             : 
   18194        3896 :     num_check = (constr != NULL) ? constr->num_check : 0;
   18195        4384 :     for (i = 0; i < num_check; i++)
   18196             :     {
   18197             :         Node       *cexpr;
   18198             : 
   18199             :         /*
   18200             :          * If this constraint hasn't been fully validated yet, we must ignore
   18201             :          * it here.
   18202             :          */
   18203         488 :         if (!constr->check[i].ccvalid)
   18204           6 :             continue;
   18205             : 
   18206         482 :         cexpr = stringToNode(constr->check[i].ccbin);
   18207             : 
   18208             :         /*
   18209             :          * Run each expression through const-simplification and
   18210             :          * canonicalization.  It is necessary, because we will be comparing it
   18211             :          * to similarly-processed partition constraint expressions, and may
   18212             :          * fail to detect valid matches without this.
   18213             :          */
   18214         482 :         cexpr = eval_const_expressions(NULL, cexpr);
   18215         482 :         cexpr = (Node *) canonicalize_qual((Expr *) cexpr, true);
   18216             : 
   18217         482 :         existConstraint = list_concat(existConstraint,
   18218         482 :                                       make_ands_implicit((Expr *) cexpr));
   18219             :     }
   18220             : 
   18221             :     /*
   18222             :      * Try to make the proof.  Since we are comparing CHECK constraints, we
   18223             :      * need to use weak implication, i.e., we assume existConstraint is
   18224             :      * not-false and try to prove the same for testConstraint.
   18225             :      *
   18226             :      * Note that predicate_implied_by assumes its first argument is known
   18227             :      * immutable.  That should always be true for both NOT NULL and partition
   18228             :      * constraints, so we don't test it here.
   18229             :      */
   18230        3896 :     return predicate_implied_by(testConstraint, existConstraint, true);
   18231             : }
   18232             : 
   18233             : /*
   18234             :  * QueuePartitionConstraintValidation
   18235             :  *
   18236             :  * Add an entry to wqueue to have the given partition constraint validated by
   18237             :  * Phase 3, for the given relation, and all its children.
   18238             :  *
   18239             :  * We first verify whether the given constraint is implied by pre-existing
   18240             :  * relation constraints; if it is, there's no need to scan the table to
   18241             :  * validate, so don't queue in that case.
   18242             :  */
   18243             : static void
   18244        2430 : QueuePartitionConstraintValidation(List **wqueue, Relation scanrel,
   18245             :                                    List *partConstraint,
   18246             :                                    bool validate_default)
   18247             : {
   18248             :     /*
   18249             :      * Based on the table's existing constraints, determine whether or not we
   18250             :      * may skip scanning the table.
   18251             :      */
   18252        2430 :     if (PartConstraintImpliedByRelConstraint(scanrel, partConstraint))
   18253             :     {
   18254          90 :         if (!validate_default)
   18255          68 :             ereport(DEBUG1,
   18256             :                     (errmsg_internal("partition constraint for table \"%s\" is implied by existing constraints",
   18257             :                                      RelationGetRelationName(scanrel))));
   18258             :         else
   18259          22 :             ereport(DEBUG1,
   18260             :                     (errmsg_internal("updated partition constraint for default partition \"%s\" is implied by existing constraints",
   18261             :                                      RelationGetRelationName(scanrel))));
   18262          90 :         return;
   18263             :     }
   18264             : 
   18265             :     /*
   18266             :      * Constraints proved insufficient. For plain relations, queue a
   18267             :      * validation item now; for partitioned tables, recurse to process each
   18268             :      * partition.
   18269             :      */
   18270        2340 :     if (scanrel->rd_rel->relkind == RELKIND_RELATION)
   18271             :     {
   18272             :         AlteredTableInfo *tab;
   18273             : 
   18274             :         /* Grab a work queue entry. */
   18275        1946 :         tab = ATGetQueueEntry(wqueue, scanrel);
   18276             :         Assert(tab->partition_constraint == NULL);
   18277        1946 :         tab->partition_constraint = (Expr *) linitial(partConstraint);
   18278        1946 :         tab->validate_default = validate_default;
   18279             :     }
   18280         394 :     else if (scanrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
   18281             :     {
   18282         346 :         PartitionDesc partdesc = RelationGetPartitionDesc(scanrel, true);
   18283             :         int         i;
   18284             : 
   18285         740 :         for (i = 0; i < partdesc->nparts; i++)
   18286             :         {
   18287             :             Relation    part_rel;
   18288             :             List       *thisPartConstraint;
   18289             : 
   18290             :             /*
   18291             :              * This is the minimum lock we need to prevent deadlocks.
   18292             :              */
   18293         394 :             part_rel = table_open(partdesc->oids[i], AccessExclusiveLock);
   18294             : 
   18295             :             /*
   18296             :              * Adjust the constraint for scanrel so that it matches this
   18297             :              * partition's attribute numbers.
   18298             :              */
   18299             :             thisPartConstraint =
   18300         394 :                 map_partition_varattnos(partConstraint, 1,
   18301             :                                         part_rel, scanrel);
   18302             : 
   18303         394 :             QueuePartitionConstraintValidation(wqueue, part_rel,
   18304             :                                                thisPartConstraint,
   18305             :                                                validate_default);
   18306         394 :             table_close(part_rel, NoLock);  /* keep lock till commit */
   18307             :         }
   18308             :     }
   18309             : }
   18310             : 
   18311             : /*
   18312             :  * attachPartitionTable: attach a new partition to the partitioned table
   18313             :  *
   18314             :  * wqueue: the ALTER TABLE work queue; can be NULL when not running as part
   18315             :  *   of an ALTER TABLE sequence.
   18316             :  * rel: partitioned relation;
   18317             :  * attachrel: relation of attached partition;
   18318             :  * bound: bounds of attached relation.
   18319             :  */
   18320             : static void
   18321        2574 : attachPartitionTable(List **wqueue, Relation rel, Relation attachrel, PartitionBoundSpec *bound)
   18322             : {
   18323             :     /* OK to create inheritance.  Rest of the checks performed there */
   18324        2574 :     CreateInheritance(attachrel, rel, true);
   18325             : 
   18326             :     /* Update the pg_class entry. */
   18327        2508 :     StorePartitionBound(attachrel, rel, bound);
   18328             : 
   18329             :     /* Ensure there exists a correct set of indexes in the partition. */
   18330        2508 :     AttachPartitionEnsureIndexes(wqueue, rel, attachrel);
   18331             : 
   18332             :     /* and triggers */
   18333        2478 :     CloneRowTriggersToPartition(rel, attachrel);
   18334             : 
   18335             :     /*
   18336             :      * Clone foreign key constraints.  Callee is responsible for setting up
   18337             :      * for phase 3 constraint verification.
   18338             :      */
   18339        2472 :     CloneForeignKeyConstraints(wqueue, rel, attachrel);
   18340        2472 : }
   18341             : 
   18342             : /*
   18343             :  * ALTER TABLE <name> ATTACH PARTITION <partition-name> FOR VALUES
   18344             :  *
   18345             :  * Return the address of the newly attached partition.
   18346             :  */
   18347             : static ObjectAddress
   18348        2190 : ATExecAttachPartition(List **wqueue, Relation rel, PartitionCmd *cmd,
   18349             :                       AlterTableUtilityContext *context)
   18350             : {
   18351             :     Relation    attachrel,
   18352             :                 catalog;
   18353             :     List       *attachrel_children;
   18354             :     List       *partConstraint;
   18355             :     SysScanDesc scan;
   18356             :     ScanKeyData skey;
   18357             :     AttrNumber  attno;
   18358             :     int         natts;
   18359             :     TupleDesc   tupleDesc;
   18360             :     ObjectAddress address;
   18361             :     const char *trigger_name;
   18362             :     Oid         defaultPartOid;
   18363             :     List       *partBoundConstraint;
   18364        2190 :     ParseState *pstate = make_parsestate(NULL);
   18365             : 
   18366        2190 :     pstate->p_sourcetext = context->queryString;
   18367             : 
   18368             :     /*
   18369             :      * We must lock the default partition if one exists, because attaching a
   18370             :      * new partition will change its partition constraint.
   18371             :      */
   18372             :     defaultPartOid =
   18373        2190 :         get_default_oid_from_partdesc(RelationGetPartitionDesc(rel, true));
   18374        2190 :     if (OidIsValid(defaultPartOid))
   18375         182 :         LockRelationOid(defaultPartOid, AccessExclusiveLock);
   18376             : 
   18377        2190 :     attachrel = table_openrv(cmd->name, AccessExclusiveLock);
   18378             : 
   18379             :     /*
   18380             :      * XXX I think it'd be a good idea to grab locks on all tables referenced
   18381             :      * by FKs at this point also.
   18382             :      */
   18383             : 
   18384             :     /*
   18385             :      * Must be owner of both parent and source table -- parent was checked by
   18386             :      * ATSimplePermissions call in ATPrepCmd
   18387             :      */
   18388        2184 :     ATSimplePermissions(AT_AttachPartition, attachrel, ATT_TABLE | ATT_FOREIGN_TABLE);
   18389             : 
   18390             :     /* A partition can only have one parent */
   18391        2178 :     if (attachrel->rd_rel->relispartition)
   18392           6 :         ereport(ERROR,
   18393             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   18394             :                  errmsg("\"%s\" is already a partition",
   18395             :                         RelationGetRelationName(attachrel))));
   18396             : 
   18397        2172 :     if (OidIsValid(attachrel->rd_rel->reloftype))
   18398           6 :         ereport(ERROR,
   18399             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   18400             :                  errmsg("cannot attach a typed table as partition")));
   18401             : 
   18402             :     /*
   18403             :      * Table being attached should not already be part of inheritance; either
   18404             :      * as a child table...
   18405             :      */
   18406        2166 :     catalog = table_open(InheritsRelationId, AccessShareLock);
   18407        2166 :     ScanKeyInit(&skey,
   18408             :                 Anum_pg_inherits_inhrelid,
   18409             :                 BTEqualStrategyNumber, F_OIDEQ,
   18410             :                 ObjectIdGetDatum(RelationGetRelid(attachrel)));
   18411        2166 :     scan = systable_beginscan(catalog, InheritsRelidSeqnoIndexId, true,
   18412             :                               NULL, 1, &skey);
   18413        2166 :     if (HeapTupleIsValid(systable_getnext(scan)))
   18414           6 :         ereport(ERROR,
   18415             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   18416             :                  errmsg("cannot attach inheritance child as partition")));
   18417        2160 :     systable_endscan(scan);
   18418             : 
   18419             :     /* ...or as a parent table (except the case when it is partitioned) */
   18420        2160 :     ScanKeyInit(&skey,
   18421             :                 Anum_pg_inherits_inhparent,
   18422             :                 BTEqualStrategyNumber, F_OIDEQ,
   18423             :                 ObjectIdGetDatum(RelationGetRelid(attachrel)));
   18424        2160 :     scan = systable_beginscan(catalog, InheritsParentIndexId, true, NULL,
   18425             :                               1, &skey);
   18426        2160 :     if (HeapTupleIsValid(systable_getnext(scan)) &&
   18427         248 :         attachrel->rd_rel->relkind == RELKIND_RELATION)
   18428           6 :         ereport(ERROR,
   18429             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   18430             :                  errmsg("cannot attach inheritance parent as partition")));
   18431        2154 :     systable_endscan(scan);
   18432        2154 :     table_close(catalog, AccessShareLock);
   18433             : 
   18434             :     /*
   18435             :      * Prevent circularity by seeing if rel is a partition of attachrel. (In
   18436             :      * particular, this disallows making a rel a partition of itself.)
   18437             :      *
   18438             :      * We do that by checking if rel is a member of the list of attachrel's
   18439             :      * partitions provided the latter is partitioned at all.  We want to avoid
   18440             :      * having to construct this list again, so we request the strongest lock
   18441             :      * on all partitions.  We need the strongest lock, because we may decide
   18442             :      * to scan them if we find out that the table being attached (or its leaf
   18443             :      * partitions) may contain rows that violate the partition constraint. If
   18444             :      * the table has a constraint that would prevent such rows, which by
   18445             :      * definition is present in all the partitions, we need not scan the
   18446             :      * table, nor its partitions.  But we cannot risk a deadlock by taking a
   18447             :      * weaker lock now and the stronger one only when needed.
   18448             :      */
   18449        2154 :     attachrel_children = find_all_inheritors(RelationGetRelid(attachrel),
   18450             :                                              AccessExclusiveLock, NULL);
   18451        2154 :     if (list_member_oid(attachrel_children, RelationGetRelid(rel)))
   18452          12 :         ereport(ERROR,
   18453             :                 (errcode(ERRCODE_DUPLICATE_TABLE),
   18454             :                  errmsg("circular inheritance not allowed"),
   18455             :                  errdetail("\"%s\" is already a child of \"%s\".",
   18456             :                            RelationGetRelationName(rel),
   18457             :                            RelationGetRelationName(attachrel))));
   18458             : 
   18459             :     /* If the parent is permanent, so must be all of its partitions. */
   18460        2142 :     if (rel->rd_rel->relpersistence != RELPERSISTENCE_TEMP &&
   18461        2100 :         attachrel->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
   18462           6 :         ereport(ERROR,
   18463             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   18464             :                  errmsg("cannot attach a temporary relation as partition of permanent relation \"%s\"",
   18465             :                         RelationGetRelationName(rel))));
   18466             : 
   18467             :     /* Temp parent cannot have a partition that is itself not a temp */
   18468        2136 :     if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
   18469          42 :         attachrel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
   18470          18 :         ereport(ERROR,
   18471             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   18472             :                  errmsg("cannot attach a permanent relation as partition of temporary relation \"%s\"",
   18473             :                         RelationGetRelationName(rel))));
   18474             : 
   18475             :     /* If the parent is temp, it must belong to this session */
   18476        2118 :     if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
   18477          24 :         !rel->rd_islocaltemp)
   18478           0 :         ereport(ERROR,
   18479             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   18480             :                  errmsg("cannot attach as partition of temporary relation of another session")));
   18481             : 
   18482             :     /* Ditto for the partition */
   18483        2118 :     if (attachrel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
   18484          24 :         !attachrel->rd_islocaltemp)
   18485           0 :         ereport(ERROR,
   18486             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   18487             :                  errmsg("cannot attach temporary relation of another session as partition")));
   18488             : 
   18489             :     /*
   18490             :      * Check if attachrel has any identity columns or any columns that aren't
   18491             :      * in the parent.
   18492             :      */
   18493        2118 :     tupleDesc = RelationGetDescr(attachrel);
   18494        2118 :     natts = tupleDesc->natts;
   18495        7284 :     for (attno = 1; attno <= natts; attno++)
   18496             :     {
   18497        5202 :         Form_pg_attribute attribute = TupleDescAttr(tupleDesc, attno - 1);
   18498        5202 :         char       *attributeName = NameStr(attribute->attname);
   18499             : 
   18500             :         /* Ignore dropped */
   18501        5202 :         if (attribute->attisdropped)
   18502         592 :             continue;
   18503             : 
   18504        4610 :         if (attribute->attidentity)
   18505          18 :             ereport(ERROR,
   18506             :                     errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
   18507             :                     errmsg("table \"%s\" being attached contains an identity column \"%s\"",
   18508             :                            RelationGetRelationName(attachrel), attributeName),
   18509             :                     errdetail("The new partition may not contain an identity column."));
   18510             : 
   18511             :         /* Try to find the column in parent (matching on column name) */
   18512        4592 :         if (!SearchSysCacheExists2(ATTNAME,
   18513             :                                    ObjectIdGetDatum(RelationGetRelid(rel)),
   18514             :                                    CStringGetDatum(attributeName)))
   18515          18 :             ereport(ERROR,
   18516             :                     (errcode(ERRCODE_DATATYPE_MISMATCH),
   18517             :                      errmsg("table \"%s\" contains column \"%s\" not found in parent \"%s\"",
   18518             :                             RelationGetRelationName(attachrel), attributeName,
   18519             :                             RelationGetRelationName(rel)),
   18520             :                      errdetail("The new partition may contain only the columns present in parent.")));
   18521             :     }
   18522             : 
   18523             :     /*
   18524             :      * If child_rel has row-level triggers with transition tables, we
   18525             :      * currently don't allow it to become a partition.  See also prohibitions
   18526             :      * in ATExecAddInherit() and CreateTrigger().
   18527             :      */
   18528        2082 :     trigger_name = FindTriggerIncompatibleWithInheritance(attachrel->trigdesc);
   18529        2082 :     if (trigger_name != NULL)
   18530           6 :         ereport(ERROR,
   18531             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   18532             :                  errmsg("trigger \"%s\" prevents table \"%s\" from becoming a partition",
   18533             :                         trigger_name, RelationGetRelationName(attachrel)),
   18534             :                  errdetail("ROW triggers with transition tables are not supported on partitions.")));
   18535             : 
   18536             :     /*
   18537             :      * Check that the new partition's bound is valid and does not overlap any
   18538             :      * of existing partitions of the parent - note that it does not return on
   18539             :      * error.
   18540             :      */
   18541        2076 :     check_new_partition_bound(RelationGetRelationName(attachrel), rel,
   18542             :                               cmd->bound, pstate);
   18543             : 
   18544             :     /* Attach a new partition to the partitioned table. */
   18545        2040 :     attachPartitionTable(wqueue, rel, attachrel, cmd->bound);
   18546             : 
   18547             :     /*
   18548             :      * Generate partition constraint from the partition bound specification.
   18549             :      * If the parent itself is a partition, make sure to include its
   18550             :      * constraint as well.
   18551             :      */
   18552        1938 :     partBoundConstraint = get_qual_from_partbound(rel, cmd->bound);
   18553        1938 :     partConstraint = list_concat(partBoundConstraint,
   18554        1938 :                                  RelationGetPartitionQual(rel));
   18555             : 
   18556             :     /* Skip validation if there are no constraints to validate. */
   18557        1938 :     if (partConstraint)
   18558             :     {
   18559             :         /*
   18560             :          * Run the partition quals through const-simplification similar to
   18561             :          * check constraints.  We skip canonicalize_qual, though, because
   18562             :          * partition quals should be in canonical form already.
   18563             :          */
   18564             :         partConstraint =
   18565        1890 :             (List *) eval_const_expressions(NULL,
   18566             :                                             (Node *) partConstraint);
   18567             : 
   18568             :         /* XXX this sure looks wrong */
   18569        1890 :         partConstraint = list_make1(make_ands_explicit(partConstraint));
   18570             : 
   18571             :         /*
   18572             :          * Adjust the generated constraint to match this partition's attribute
   18573             :          * numbers.
   18574             :          */
   18575        1890 :         partConstraint = map_partition_varattnos(partConstraint, 1, attachrel,
   18576             :                                                  rel);
   18577             : 
   18578             :         /* Validate partition constraints against the table being attached. */
   18579        1890 :         QueuePartitionConstraintValidation(wqueue, attachrel, partConstraint,
   18580             :                                            false);
   18581             :     }
   18582             : 
   18583             :     /*
   18584             :      * If we're attaching a partition other than the default partition and a
   18585             :      * default one exists, then that partition's partition constraint changes,
   18586             :      * so add an entry to the work queue to validate it, too.  (We must not do
   18587             :      * this when the partition being attached is the default one; we already
   18588             :      * did it above!)
   18589             :      */
   18590        1938 :     if (OidIsValid(defaultPartOid))
   18591             :     {
   18592             :         Relation    defaultrel;
   18593             :         List       *defPartConstraint;
   18594             : 
   18595             :         Assert(!cmd->bound->is_default);
   18596             : 
   18597             :         /* we already hold a lock on the default partition */
   18598         146 :         defaultrel = table_open(defaultPartOid, NoLock);
   18599             :         defPartConstraint =
   18600         146 :             get_proposed_default_constraint(partBoundConstraint);
   18601             : 
   18602             :         /*
   18603             :          * Map the Vars in the constraint expression from rel's attnos to
   18604             :          * defaultrel's.
   18605             :          */
   18606             :         defPartConstraint =
   18607         146 :             map_partition_varattnos(defPartConstraint,
   18608             :                                     1, defaultrel, rel);
   18609         146 :         QueuePartitionConstraintValidation(wqueue, defaultrel,
   18610             :                                            defPartConstraint, true);
   18611             : 
   18612             :         /* keep our lock until commit. */
   18613         146 :         table_close(defaultrel, NoLock);
   18614             :     }
   18615             : 
   18616        1938 :     ObjectAddressSet(address, RelationRelationId, RelationGetRelid(attachrel));
   18617             : 
   18618             :     /*
   18619             :      * If the partition we just attached is partitioned itself, invalidate
   18620             :      * relcache for all descendent partitions too to ensure that their
   18621             :      * rd_partcheck expression trees are rebuilt; partitions already locked at
   18622             :      * the beginning of this function.
   18623             :      */
   18624        1938 :     if (attachrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
   18625             :     {
   18626             :         ListCell   *l;
   18627             : 
   18628         996 :         foreach(l, attachrel_children)
   18629             :         {
   18630         674 :             CacheInvalidateRelcacheByRelid(lfirst_oid(l));
   18631             :         }
   18632             :     }
   18633             : 
   18634             :     /* keep our lock until commit */
   18635        1938 :     table_close(attachrel, NoLock);
   18636             : 
   18637        1938 :     return address;
   18638             : }
   18639             : 
   18640             : /*
   18641             :  * AttachPartitionEnsureIndexes
   18642             :  *      subroutine for ATExecAttachPartition to create/match indexes
   18643             :  *
   18644             :  * Enforce the indexing rule for partitioned tables during ALTER TABLE / ATTACH
   18645             :  * PARTITION: every partition must have an index attached to each index on the
   18646             :  * partitioned table.
   18647             :  */
   18648             : static void
   18649        2508 : AttachPartitionEnsureIndexes(List **wqueue, Relation rel, Relation attachrel)
   18650             : {
   18651             :     List       *idxes;
   18652             :     List       *attachRelIdxs;
   18653             :     Relation   *attachrelIdxRels;
   18654             :     IndexInfo **attachInfos;
   18655             :     ListCell   *cell;
   18656             :     MemoryContext cxt;
   18657             :     MemoryContext oldcxt;
   18658             : 
   18659        2508 :     cxt = AllocSetContextCreate(CurrentMemoryContext,
   18660             :                                 "AttachPartitionEnsureIndexes",
   18661             :                                 ALLOCSET_DEFAULT_SIZES);
   18662        2508 :     oldcxt = MemoryContextSwitchTo(cxt);
   18663             : 
   18664        2508 :     idxes = RelationGetIndexList(rel);
   18665        2508 :     attachRelIdxs = RelationGetIndexList(attachrel);
   18666        2508 :     attachrelIdxRels = palloc(sizeof(Relation) * list_length(attachRelIdxs));
   18667        2508 :     attachInfos = palloc(sizeof(IndexInfo *) * list_length(attachRelIdxs));
   18668             : 
   18669             :     /* Build arrays of all existing indexes and their IndexInfos */
   18670        5368 :     foreach_oid(cldIdxId, attachRelIdxs)
   18671             :     {
   18672         352 :         int         i = foreach_current_index(cldIdxId);
   18673             : 
   18674         352 :         attachrelIdxRels[i] = index_open(cldIdxId, AccessShareLock);
   18675         352 :         attachInfos[i] = BuildIndexInfo(attachrelIdxRels[i]);
   18676             :     }
   18677             : 
   18678             :     /*
   18679             :      * If we're attaching a foreign table, we must fail if any of the indexes
   18680             :      * is a constraint index; otherwise, there's nothing to do here.  Do this
   18681             :      * before starting work, to avoid wasting the effort of building a few
   18682             :      * non-unique indexes before coming across a unique one.
   18683             :      */
   18684        2508 :     if (attachrel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
   18685             :     {
   18686          86 :         foreach(cell, idxes)
   18687             :         {
   18688          36 :             Oid         idx = lfirst_oid(cell);
   18689          36 :             Relation    idxRel = index_open(idx, AccessShareLock);
   18690             : 
   18691          36 :             if (idxRel->rd_index->indisunique ||
   18692          24 :                 idxRel->rd_index->indisprimary)
   18693          12 :                 ereport(ERROR,
   18694             :                         (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   18695             :                          errmsg("cannot attach foreign table \"%s\" as partition of partitioned table \"%s\"",
   18696             :                                 RelationGetRelationName(attachrel),
   18697             :                                 RelationGetRelationName(rel)),
   18698             :                          errdetail("Partitioned table \"%s\" contains unique indexes.",
   18699             :                                    RelationGetRelationName(rel))));
   18700          24 :             index_close(idxRel, AccessShareLock);
   18701             :         }
   18702             : 
   18703          50 :         goto out;
   18704             :     }
   18705             : 
   18706             :     /*
   18707             :      * For each index on the partitioned table, find a matching one in the
   18708             :      * partition-to-be; if one is not found, create one.
   18709             :      */
   18710        3084 :     foreach(cell, idxes)
   18711             :     {
   18712         656 :         Oid         idx = lfirst_oid(cell);
   18713         656 :         Relation    idxRel = index_open(idx, AccessShareLock);
   18714             :         IndexInfo  *info;
   18715             :         AttrMap    *attmap;
   18716         656 :         bool        found = false;
   18717             :         Oid         constraintOid;
   18718             : 
   18719             :         /*
   18720             :          * Ignore indexes in the partitioned table other than partitioned
   18721             :          * indexes.
   18722             :          */
   18723         656 :         if (idxRel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
   18724             :         {
   18725           0 :             index_close(idxRel, AccessShareLock);
   18726           0 :             continue;
   18727             :         }
   18728             : 
   18729             :         /* construct an indexinfo to compare existing indexes against */
   18730         656 :         info = BuildIndexInfo(idxRel);
   18731         656 :         attmap = build_attrmap_by_name(RelationGetDescr(attachrel),
   18732             :                                        RelationGetDescr(rel),
   18733             :                                        false);
   18734         656 :         constraintOid = get_relation_idx_constraint_oid(RelationGetRelid(rel), idx);
   18735             : 
   18736             :         /*
   18737             :          * Scan the list of existing indexes in the partition-to-be, and mark
   18738             :          * the first matching, valid, unattached one we find, if any, as
   18739             :          * partition of the parent index.  If we find one, we're done.
   18740             :          */
   18741         716 :         for (int i = 0; i < list_length(attachRelIdxs); i++)
   18742             :         {
   18743         262 :             Oid         cldIdxId = RelationGetRelid(attachrelIdxRels[i]);
   18744         262 :             Oid         cldConstrOid = InvalidOid;
   18745             : 
   18746             :             /* does this index have a parent?  if so, can't use it */
   18747         262 :             if (attachrelIdxRels[i]->rd_rel->relispartition)
   18748          12 :                 continue;
   18749             : 
   18750             :             /* If this index is invalid, can't use it */
   18751         250 :             if (!attachrelIdxRels[i]->rd_index->indisvalid)
   18752           6 :                 continue;
   18753             : 
   18754         244 :             if (CompareIndexInfo(attachInfos[i], info,
   18755         244 :                                  attachrelIdxRels[i]->rd_indcollation,
   18756         244 :                                  idxRel->rd_indcollation,
   18757         244 :                                  attachrelIdxRels[i]->rd_opfamily,
   18758         244 :                                  idxRel->rd_opfamily,
   18759             :                                  attmap))
   18760             :             {
   18761             :                 /*
   18762             :                  * If this index is being created in the parent because of a
   18763             :                  * constraint, then the child needs to have a constraint also,
   18764             :                  * so look for one.  If there is no such constraint, this
   18765             :                  * index is no good, so keep looking.
   18766             :                  */
   18767         208 :                 if (OidIsValid(constraintOid))
   18768             :                 {
   18769             :                     cldConstrOid =
   18770         110 :                         get_relation_idx_constraint_oid(RelationGetRelid(attachrel),
   18771             :                                                         cldIdxId);
   18772             :                     /* no dice */
   18773         110 :                     if (!OidIsValid(cldConstrOid))
   18774           6 :                         continue;
   18775             : 
   18776             :                     /* Ensure they're both the same type of constraint */
   18777         208 :                     if (get_constraint_type(constraintOid) !=
   18778         104 :                         get_constraint_type(cldConstrOid))
   18779           0 :                         continue;
   18780             :                 }
   18781             : 
   18782             :                 /* bingo. */
   18783         202 :                 IndexSetParentIndex(attachrelIdxRels[i], idx);
   18784         202 :                 if (OidIsValid(constraintOid))
   18785         104 :                     ConstraintSetParentConstraint(cldConstrOid, constraintOid,
   18786             :                                                   RelationGetRelid(attachrel));
   18787         202 :                 found = true;
   18788             : 
   18789         202 :                 CommandCounterIncrement();
   18790         202 :                 break;
   18791             :             }
   18792             :         }
   18793             : 
   18794             :         /*
   18795             :          * If no suitable index was found in the partition-to-be, create one
   18796             :          * now.
   18797             :          */
   18798         656 :         if (!found)
   18799             :         {
   18800             :             IndexStmt  *stmt;
   18801             :             Oid         conOid;
   18802             : 
   18803         454 :             stmt = generateClonedIndexStmt(NULL,
   18804             :                                            idxRel, attmap,
   18805             :                                            &conOid);
   18806         454 :             DefineIndex(RelationGetRelid(attachrel), stmt, InvalidOid,
   18807             :                         RelationGetRelid(idxRel),
   18808             :                         conOid,
   18809             :                         -1,
   18810             :                         true, false, false, false, false);
   18811             :         }
   18812             : 
   18813         638 :         index_close(idxRel, AccessShareLock);
   18814             :     }
   18815             : 
   18816        2478 : out:
   18817             :     /* Clean up. */
   18818        2818 :     for (int i = 0; i < list_length(attachRelIdxs); i++)
   18819         340 :         index_close(attachrelIdxRels[i], AccessShareLock);
   18820        2478 :     MemoryContextSwitchTo(oldcxt);
   18821        2478 :     MemoryContextDelete(cxt);
   18822        2478 : }
   18823             : 
   18824             : /*
   18825             :  * CloneRowTriggersToPartition
   18826             :  *      subroutine for ATExecAttachPartition/DefineRelation to create row
   18827             :  *      triggers on partitions
   18828             :  */
   18829             : static void
   18830        2892 : CloneRowTriggersToPartition(Relation parent, Relation partition)
   18831             : {
   18832             :     Relation    pg_trigger;
   18833             :     ScanKeyData key;
   18834             :     SysScanDesc scan;
   18835             :     HeapTuple   tuple;
   18836             :     MemoryContext perTupCxt;
   18837             : 
   18838        2892 :     ScanKeyInit(&key, Anum_pg_trigger_tgrelid, BTEqualStrategyNumber,
   18839             :                 F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(parent)));
   18840        2892 :     pg_trigger = table_open(TriggerRelationId, RowExclusiveLock);
   18841        2892 :     scan = systable_beginscan(pg_trigger, TriggerRelidNameIndexId,
   18842             :                               true, NULL, 1, &key);
   18843             : 
   18844        2892 :     perTupCxt = AllocSetContextCreate(CurrentMemoryContext,
   18845             :                                       "clone trig", ALLOCSET_SMALL_SIZES);
   18846             : 
   18847        4448 :     while (HeapTupleIsValid(tuple = systable_getnext(scan)))
   18848             :     {
   18849        1562 :         Form_pg_trigger trigForm = (Form_pg_trigger) GETSTRUCT(tuple);
   18850             :         CreateTrigStmt *trigStmt;
   18851        1562 :         Node       *qual = NULL;
   18852             :         Datum       value;
   18853             :         bool        isnull;
   18854        1562 :         List       *cols = NIL;
   18855        1562 :         List       *trigargs = NIL;
   18856             :         MemoryContext oldcxt;
   18857             : 
   18858             :         /*
   18859             :          * Ignore statement-level triggers; those are not cloned.
   18860             :          */
   18861        1562 :         if (!TRIGGER_FOR_ROW(trigForm->tgtype))
   18862        1382 :             continue;
   18863             : 
   18864             :         /*
   18865             :          * Don't clone internal triggers, because the constraint cloning code
   18866             :          * will.
   18867             :          */
   18868        1538 :         if (trigForm->tgisinternal)
   18869        1358 :             continue;
   18870             : 
   18871             :         /*
   18872             :          * Complain if we find an unexpected trigger type.
   18873             :          */
   18874         180 :         if (!TRIGGER_FOR_BEFORE(trigForm->tgtype) &&
   18875         162 :             !TRIGGER_FOR_AFTER(trigForm->tgtype))
   18876           0 :             elog(ERROR, "unexpected trigger \"%s\" found",
   18877             :                  NameStr(trigForm->tgname));
   18878             : 
   18879             :         /* Use short-lived context for CREATE TRIGGER */
   18880         180 :         oldcxt = MemoryContextSwitchTo(perTupCxt);
   18881             : 
   18882             :         /*
   18883             :          * If there is a WHEN clause, generate a 'cooked' version of it that's
   18884             :          * appropriate for the partition.
   18885             :          */
   18886         180 :         value = heap_getattr(tuple, Anum_pg_trigger_tgqual,
   18887             :                              RelationGetDescr(pg_trigger), &isnull);
   18888         180 :         if (!isnull)
   18889             :         {
   18890           6 :             qual = stringToNode(TextDatumGetCString(value));
   18891           6 :             qual = (Node *) map_partition_varattnos((List *) qual, PRS2_OLD_VARNO,
   18892             :                                                     partition, parent);
   18893           6 :             qual = (Node *) map_partition_varattnos((List *) qual, PRS2_NEW_VARNO,
   18894             :                                                     partition, parent);
   18895             :         }
   18896             : 
   18897             :         /*
   18898             :          * If there is a column list, transform it to a list of column names.
   18899             :          * Note we don't need to map this list in any way ...
   18900             :          */
   18901         180 :         if (trigForm->tgattr.dim1 > 0)
   18902             :         {
   18903             :             int         i;
   18904             : 
   18905          12 :             for (i = 0; i < trigForm->tgattr.dim1; i++)
   18906             :             {
   18907             :                 Form_pg_attribute col;
   18908             : 
   18909           6 :                 col = TupleDescAttr(parent->rd_att,
   18910             :                                     trigForm->tgattr.values[i] - 1);
   18911           6 :                 cols = lappend(cols,
   18912           6 :                                makeString(pstrdup(NameStr(col->attname))));
   18913             :             }
   18914             :         }
   18915             : 
   18916             :         /* Reconstruct trigger arguments list. */
   18917         180 :         if (trigForm->tgnargs > 0)
   18918             :         {
   18919             :             char       *p;
   18920             : 
   18921          36 :             value = heap_getattr(tuple, Anum_pg_trigger_tgargs,
   18922             :                                  RelationGetDescr(pg_trigger), &isnull);
   18923          36 :             if (isnull)
   18924           0 :                 elog(ERROR, "tgargs is null for trigger \"%s\" in partition \"%s\"",
   18925             :                      NameStr(trigForm->tgname), RelationGetRelationName(partition));
   18926             : 
   18927          36 :             p = (char *) VARDATA_ANY(DatumGetByteaPP(value));
   18928             : 
   18929          84 :             for (int i = 0; i < trigForm->tgnargs; i++)
   18930             :             {
   18931          48 :                 trigargs = lappend(trigargs, makeString(pstrdup(p)));
   18932          48 :                 p += strlen(p) + 1;
   18933             :             }
   18934             :         }
   18935             : 
   18936         180 :         trigStmt = makeNode(CreateTrigStmt);
   18937         180 :         trigStmt->replace = false;
   18938         180 :         trigStmt->isconstraint = OidIsValid(trigForm->tgconstraint);
   18939         180 :         trigStmt->trigname = NameStr(trigForm->tgname);
   18940         180 :         trigStmt->relation = NULL;
   18941         180 :         trigStmt->funcname = NULL;   /* passed separately */
   18942         180 :         trigStmt->args = trigargs;
   18943         180 :         trigStmt->row = true;
   18944         180 :         trigStmt->timing = trigForm->tgtype & TRIGGER_TYPE_TIMING_MASK;
   18945         180 :         trigStmt->events = trigForm->tgtype & TRIGGER_TYPE_EVENT_MASK;
   18946         180 :         trigStmt->columns = cols;
   18947         180 :         trigStmt->whenClause = NULL; /* passed separately */
   18948         180 :         trigStmt->transitionRels = NIL; /* not supported at present */
   18949         180 :         trigStmt->deferrable = trigForm->tgdeferrable;
   18950         180 :         trigStmt->initdeferred = trigForm->tginitdeferred;
   18951         180 :         trigStmt->constrrel = NULL; /* passed separately */
   18952             : 
   18953         180 :         CreateTriggerFiringOn(trigStmt, NULL, RelationGetRelid(partition),
   18954             :                               trigForm->tgconstrrelid, InvalidOid, InvalidOid,
   18955             :                               trigForm->tgfoid, trigForm->oid, qual,
   18956         180 :                               false, true, trigForm->tgenabled);
   18957             : 
   18958         174 :         MemoryContextSwitchTo(oldcxt);
   18959         174 :         MemoryContextReset(perTupCxt);
   18960             :     }
   18961             : 
   18962        2886 :     MemoryContextDelete(perTupCxt);
   18963             : 
   18964        2886 :     systable_endscan(scan);
   18965        2886 :     table_close(pg_trigger, RowExclusiveLock);
   18966        2886 : }
   18967             : 
   18968             : /*
   18969             :  * ALTER TABLE DETACH PARTITION
   18970             :  *
   18971             :  * Return the address of the relation that is no longer a partition of rel.
   18972             :  *
   18973             :  * If concurrent mode is requested, we run in two transactions.  A side-
   18974             :  * effect is that this command cannot run in a multi-part ALTER TABLE.
   18975             :  * Currently, that's enforced by the grammar.
   18976             :  *
   18977             :  * The strategy for concurrency is to first modify the partition's
   18978             :  * pg_inherit catalog row to make it visible to everyone that the
   18979             :  * partition is detached, lock the partition against writes, and commit
   18980             :  * the transaction; anyone who requests the partition descriptor from
   18981             :  * that point onwards has to ignore such a partition.  In a second
   18982             :  * transaction, we wait until all transactions that could have seen the
   18983             :  * partition as attached are gone, then we remove the rest of partition
   18984             :  * metadata (pg_inherits and pg_class.relpartbounds).
   18985             :  */
   18986             : static ObjectAddress
   18987         540 : ATExecDetachPartition(List **wqueue, AlteredTableInfo *tab, Relation rel,
   18988             :                       RangeVar *name, bool concurrent)
   18989             : {
   18990             :     Relation    partRel;
   18991             :     ObjectAddress address;
   18992             :     Oid         defaultPartOid;
   18993             : 
   18994             :     /*
   18995             :      * We must lock the default partition, because detaching this partition
   18996             :      * will change its partition constraint.
   18997             :      */
   18998             :     defaultPartOid =
   18999         540 :         get_default_oid_from_partdesc(RelationGetPartitionDesc(rel, true));
   19000         540 :     if (OidIsValid(defaultPartOid))
   19001             :     {
   19002             :         /*
   19003             :          * Concurrent detaching when a default partition exists is not
   19004             :          * supported. The main problem is that the default partition
   19005             :          * constraint would change.  And there's a definitional problem: what
   19006             :          * should happen to the tuples that are being inserted that belong to
   19007             :          * the partition being detached?  Putting them on the partition being
   19008             :          * detached would be wrong, since they'd become "lost" after the
   19009             :          * detaching completes but we cannot put them in the default partition
   19010             :          * either until we alter its partition constraint.
   19011             :          *
   19012             :          * I think we could solve this problem if we effected the constraint
   19013             :          * change before committing the first transaction.  But the lock would
   19014             :          * have to remain AEL and it would cause concurrent query planning to
   19015             :          * be blocked, so changing it that way would be even worse.
   19016             :          */
   19017         112 :         if (concurrent)
   19018          12 :             ereport(ERROR,
   19019             :                     (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
   19020             :                      errmsg("cannot detach partitions concurrently when a default partition exists")));
   19021         100 :         LockRelationOid(defaultPartOid, AccessExclusiveLock);
   19022             :     }
   19023             : 
   19024             :     /*
   19025             :      * In concurrent mode, the partition is locked with share-update-exclusive
   19026             :      * in the first transaction.  This allows concurrent transactions to be
   19027             :      * doing DML to the partition.
   19028             :      */
   19029         528 :     partRel = table_openrv(name, concurrent ? ShareUpdateExclusiveLock :
   19030             :                            AccessExclusiveLock);
   19031             : 
   19032             :     /*
   19033             :      * Check inheritance conditions and either delete the pg_inherits row (in
   19034             :      * non-concurrent mode) or just set the inhdetachpending flag.
   19035             :      */
   19036         516 :     if (!concurrent)
   19037         370 :         RemoveInheritance(partRel, rel, false);
   19038             :     else
   19039         146 :         MarkInheritDetached(partRel, rel);
   19040             : 
   19041             :     /*
   19042             :      * Ensure that foreign keys still hold after this detach.  This keeps
   19043             :      * locks on the referencing tables, which prevents concurrent transactions
   19044             :      * from adding rows that we wouldn't see.  For this to work in concurrent
   19045             :      * mode, it is critical that the partition appears as no longer attached
   19046             :      * for the RI queries as soon as the first transaction commits.
   19047             :      */
   19048         496 :     ATDetachCheckNoForeignKeyRefs(partRel);
   19049             : 
   19050             :     /*
   19051             :      * Concurrent mode has to work harder; first we add a new constraint to
   19052             :      * the partition that matches the partition constraint.  Then we close our
   19053             :      * existing transaction, and in a new one wait for all processes to catch
   19054             :      * up on the catalog updates we've done so far; at that point we can
   19055             :      * complete the operation.
   19056             :      */
   19057         462 :     if (concurrent)
   19058             :     {
   19059             :         Oid         partrelid,
   19060             :                     parentrelid;
   19061             :         LOCKTAG     tag;
   19062             :         char       *parentrelname;
   19063             :         char       *partrelname;
   19064             : 
   19065             :         /*
   19066             :          * Add a new constraint to the partition being detached, which
   19067             :          * supplants the partition constraint (unless there is one already).
   19068             :          */
   19069         140 :         DetachAddConstraintIfNeeded(wqueue, partRel);
   19070             : 
   19071             :         /*
   19072             :          * We're almost done now; the only traces that remain are the
   19073             :          * pg_inherits tuple and the partition's relpartbounds.  Before we can
   19074             :          * remove those, we need to wait until all transactions that know that
   19075             :          * this is a partition are gone.
   19076             :          */
   19077             : 
   19078             :         /*
   19079             :          * Remember relation OIDs to re-acquire them later; and relation names
   19080             :          * too, for error messages if something is dropped in between.
   19081             :          */
   19082         140 :         partrelid = RelationGetRelid(partRel);
   19083         140 :         parentrelid = RelationGetRelid(rel);
   19084         140 :         parentrelname = MemoryContextStrdup(PortalContext,
   19085         140 :                                             RelationGetRelationName(rel));
   19086         140 :         partrelname = MemoryContextStrdup(PortalContext,
   19087         140 :                                           RelationGetRelationName(partRel));
   19088             : 
   19089             :         /* Invalidate relcache entries for the parent -- must be before close */
   19090         140 :         CacheInvalidateRelcache(rel);
   19091             : 
   19092         140 :         table_close(partRel, NoLock);
   19093         140 :         table_close(rel, NoLock);
   19094         140 :         tab->rel = NULL;
   19095             : 
   19096             :         /* Make updated catalog entry visible */
   19097         140 :         PopActiveSnapshot();
   19098         140 :         CommitTransactionCommand();
   19099             : 
   19100         140 :         StartTransactionCommand();
   19101             : 
   19102             :         /*
   19103             :          * Now wait.  This ensures that all queries that were planned
   19104             :          * including the partition are finished before we remove the rest of
   19105             :          * catalog entries.  We don't need or indeed want to acquire this
   19106             :          * lock, though -- that would block later queries.
   19107             :          *
   19108             :          * We don't need to concern ourselves with waiting for a lock on the
   19109             :          * partition itself, since we will acquire AccessExclusiveLock below.
   19110             :          */
   19111         140 :         SET_LOCKTAG_RELATION(tag, MyDatabaseId, parentrelid);
   19112         140 :         WaitForLockersMultiple(list_make1(&tag), AccessExclusiveLock, false);
   19113             : 
   19114             :         /*
   19115             :          * Now acquire locks in both relations again.  Note they may have been
   19116             :          * removed in the meantime, so care is required.
   19117             :          */
   19118          90 :         rel = try_relation_open(parentrelid, ShareUpdateExclusiveLock);
   19119          90 :         partRel = try_relation_open(partrelid, AccessExclusiveLock);
   19120             : 
   19121             :         /* If the relations aren't there, something bad happened; bail out */
   19122          90 :         if (rel == NULL)
   19123             :         {
   19124           0 :             if (partRel != NULL)    /* shouldn't happen */
   19125           0 :                 elog(WARNING, "dangling partition \"%s\" remains, can't fix",
   19126             :                      partrelname);
   19127           0 :             ereport(ERROR,
   19128             :                     (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
   19129             :                      errmsg("partitioned table \"%s\" was removed concurrently",
   19130             :                             parentrelname)));
   19131             :         }
   19132          90 :         if (partRel == NULL)
   19133           0 :             ereport(ERROR,
   19134             :                     (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
   19135             :                      errmsg("partition \"%s\" was removed concurrently", partrelname)));
   19136             : 
   19137          90 :         tab->rel = rel;
   19138             :     }
   19139             : 
   19140             :     /* Do the final part of detaching */
   19141         412 :     DetachPartitionFinalize(rel, partRel, concurrent, defaultPartOid);
   19142             : 
   19143         410 :     ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partRel));
   19144             : 
   19145             :     /* keep our lock until commit */
   19146         410 :     table_close(partRel, NoLock);
   19147             : 
   19148         410 :     return address;
   19149             : }
   19150             : 
   19151             : /*
   19152             :  * Second part of ALTER TABLE .. DETACH.
   19153             :  *
   19154             :  * This is separate so that it can be run independently when the second
   19155             :  * transaction of the concurrent algorithm fails (crash or abort).
   19156             :  */
   19157             : static void
   19158         846 : DetachPartitionFinalize(Relation rel, Relation partRel, bool concurrent,
   19159             :                         Oid defaultPartOid)
   19160             : {
   19161             :     Relation    classRel;
   19162             :     List       *fks;
   19163             :     ListCell   *cell;
   19164             :     List       *indexes;
   19165             :     Datum       new_val[Natts_pg_class];
   19166             :     bool        new_null[Natts_pg_class],
   19167             :                 new_repl[Natts_pg_class];
   19168             :     HeapTuple   tuple,
   19169             :                 newtuple;
   19170         846 :     Relation    trigrel = NULL;
   19171             : 
   19172         846 :     if (concurrent)
   19173             :     {
   19174             :         /*
   19175             :          * We can remove the pg_inherits row now. (In the non-concurrent case,
   19176             :          * this was already done).
   19177             :          */
   19178         104 :         RemoveInheritance(partRel, rel, true);
   19179             :     }
   19180             : 
   19181             :     /* Drop any triggers that were cloned on creation/attach. */
   19182         846 :     DropClonedTriggersFromPartition(RelationGetRelid(partRel));
   19183             : 
   19184             :     /*
   19185             :      * Detach any foreign keys that are inherited.  This includes creating
   19186             :      * additional action triggers.
   19187             :      */
   19188         846 :     fks = copyObject(RelationGetFKeyList(partRel));
   19189         846 :     if (fks != NIL)
   19190          54 :         trigrel = table_open(TriggerRelationId, RowExclusiveLock);
   19191         930 :     foreach(cell, fks)
   19192             :     {
   19193          84 :         ForeignKeyCacheInfo *fk = lfirst(cell);
   19194             :         HeapTuple   contup;
   19195             :         Form_pg_constraint conform;
   19196             :         Constraint *fkconstraint;
   19197             :         Oid         insertTriggerOid,
   19198             :                     updateTriggerOid;
   19199             : 
   19200          84 :         contup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(fk->conoid));
   19201          84 :         if (!HeapTupleIsValid(contup))
   19202           0 :             elog(ERROR, "cache lookup failed for constraint %u", fk->conoid);
   19203          84 :         conform = (Form_pg_constraint) GETSTRUCT(contup);
   19204             : 
   19205             :         /* consider only the inherited foreign keys */
   19206          84 :         if (conform->contype != CONSTRAINT_FOREIGN ||
   19207          84 :             !OidIsValid(conform->conparentid))
   19208             :         {
   19209          18 :             ReleaseSysCache(contup);
   19210          18 :             continue;
   19211             :         }
   19212             : 
   19213             :         /* unset conparentid and adjust conislocal, coninhcount, etc. */
   19214          66 :         ConstraintSetParentConstraint(fk->conoid, InvalidOid, InvalidOid);
   19215             : 
   19216             :         /*
   19217             :          * Also, look up the partition's "check" triggers corresponding to the
   19218             :          * constraint being detached and detach them from the parent triggers.
   19219             :          */
   19220          66 :         GetForeignKeyCheckTriggers(trigrel,
   19221             :                                    fk->conoid, fk->confrelid, fk->conrelid,
   19222             :                                    &insertTriggerOid, &updateTriggerOid);
   19223             :         Assert(OidIsValid(insertTriggerOid));
   19224          66 :         TriggerSetParentTrigger(trigrel, insertTriggerOid, InvalidOid,
   19225             :                                 RelationGetRelid(partRel));
   19226             :         Assert(OidIsValid(updateTriggerOid));
   19227          66 :         TriggerSetParentTrigger(trigrel, updateTriggerOid, InvalidOid,
   19228             :                                 RelationGetRelid(partRel));
   19229             : 
   19230             :         /*
   19231             :          * Make the action triggers on the referenced relation.  When this was
   19232             :          * a partition the action triggers pointed to the parent rel (they
   19233             :          * still do), but now we need separate ones of our own.
   19234             :          */
   19235          66 :         fkconstraint = makeNode(Constraint);
   19236          66 :         fkconstraint->contype = CONSTRAINT_FOREIGN;
   19237          66 :         fkconstraint->conname = pstrdup(NameStr(conform->conname));
   19238          66 :         fkconstraint->deferrable = conform->condeferrable;
   19239          66 :         fkconstraint->initdeferred = conform->condeferred;
   19240          66 :         fkconstraint->location = -1;
   19241          66 :         fkconstraint->pktable = NULL;
   19242          66 :         fkconstraint->fk_attrs = NIL;
   19243          66 :         fkconstraint->pk_attrs = NIL;
   19244          66 :         fkconstraint->fk_matchtype = conform->confmatchtype;
   19245          66 :         fkconstraint->fk_upd_action = conform->confupdtype;
   19246          66 :         fkconstraint->fk_del_action = conform->confdeltype;
   19247          66 :         fkconstraint->fk_del_set_cols = NIL;
   19248          66 :         fkconstraint->old_conpfeqop = NIL;
   19249          66 :         fkconstraint->old_pktable_oid = InvalidOid;
   19250          66 :         fkconstraint->skip_validation = false;
   19251          66 :         fkconstraint->initially_valid = true;
   19252             : 
   19253          66 :         createForeignKeyActionTriggers(partRel, conform->confrelid,
   19254             :                                        fkconstraint, fk->conoid,
   19255             :                                        conform->conindid,
   19256             :                                        InvalidOid, InvalidOid,
   19257             :                                        NULL, NULL);
   19258             : 
   19259          66 :         ReleaseSysCache(contup);
   19260             :     }
   19261         846 :     list_free_deep(fks);
   19262         846 :     if (trigrel)
   19263          54 :         table_close(trigrel, RowExclusiveLock);
   19264             : 
   19265             :     /*
   19266             :      * Any sub-constraints that are in the referenced-side of a larger
   19267             :      * constraint have to be removed.  This partition is no longer part of the
   19268             :      * key space of the constraint.
   19269             :      */
   19270         888 :     foreach(cell, GetParentedForeignKeyRefs(partRel))
   19271             :     {
   19272          44 :         Oid         constrOid = lfirst_oid(cell);
   19273             :         ObjectAddress constraint;
   19274             : 
   19275          44 :         ConstraintSetParentConstraint(constrOid, InvalidOid, InvalidOid);
   19276          44 :         deleteDependencyRecordsForClass(ConstraintRelationId,
   19277             :                                         constrOid,
   19278             :                                         ConstraintRelationId,
   19279             :                                         DEPENDENCY_INTERNAL);
   19280          44 :         CommandCounterIncrement();
   19281             : 
   19282          44 :         ObjectAddressSet(constraint, ConstraintRelationId, constrOid);
   19283          44 :         performDeletion(&constraint, DROP_RESTRICT, 0);
   19284             :     }
   19285             : 
   19286             :     /* Now we can detach indexes */
   19287         844 :     indexes = RelationGetIndexList(partRel);
   19288        1230 :     foreach(cell, indexes)
   19289             :     {
   19290         386 :         Oid         idxid = lfirst_oid(cell);
   19291             :         Oid         parentidx;
   19292             :         Relation    idx;
   19293             :         Oid         constrOid;
   19294             :         Oid         parentConstrOid;
   19295             : 
   19296         386 :         if (!has_superclass(idxid))
   19297          12 :             continue;
   19298             : 
   19299         374 :         parentidx = get_partition_parent(idxid, false);
   19300             :         Assert((IndexGetRelation(parentidx, false) == RelationGetRelid(rel)));
   19301             : 
   19302         374 :         idx = index_open(idxid, AccessExclusiveLock);
   19303         374 :         IndexSetParentIndex(idx, InvalidOid);
   19304             : 
   19305             :         /*
   19306             :          * If there's a constraint associated with the index, detach it too.
   19307             :          * Careful: it is possible for a constraint index in a partition to be
   19308             :          * the child of a non-constraint index, so verify whether the parent
   19309             :          * index does actually have a constraint.
   19310             :          */
   19311         374 :         constrOid = get_relation_idx_constraint_oid(RelationGetRelid(partRel),
   19312             :                                                     idxid);
   19313         374 :         parentConstrOid = get_relation_idx_constraint_oid(RelationGetRelid(rel),
   19314             :                                                           parentidx);
   19315         374 :         if (OidIsValid(parentConstrOid) && OidIsValid(constrOid))
   19316         150 :             ConstraintSetParentConstraint(constrOid, InvalidOid, InvalidOid);
   19317             : 
   19318         374 :         index_close(idx, NoLock);
   19319             :     }
   19320             : 
   19321             :     /* Update pg_class tuple */
   19322         844 :     classRel = table_open(RelationRelationId, RowExclusiveLock);
   19323         844 :     tuple = SearchSysCacheCopy1(RELOID,
   19324             :                                 ObjectIdGetDatum(RelationGetRelid(partRel)));
   19325         844 :     if (!HeapTupleIsValid(tuple))
   19326           0 :         elog(ERROR, "cache lookup failed for relation %u",
   19327             :              RelationGetRelid(partRel));
   19328             :     Assert(((Form_pg_class) GETSTRUCT(tuple))->relispartition);
   19329             : 
   19330             :     /* Clear relpartbound and reset relispartition */
   19331         844 :     memset(new_val, 0, sizeof(new_val));
   19332         844 :     memset(new_null, false, sizeof(new_null));
   19333         844 :     memset(new_repl, false, sizeof(new_repl));
   19334         844 :     new_val[Anum_pg_class_relpartbound - 1] = (Datum) 0;
   19335         844 :     new_null[Anum_pg_class_relpartbound - 1] = true;
   19336         844 :     new_repl[Anum_pg_class_relpartbound - 1] = true;
   19337         844 :     newtuple = heap_modify_tuple(tuple, RelationGetDescr(classRel),
   19338             :                                  new_val, new_null, new_repl);
   19339             : 
   19340         844 :     ((Form_pg_class) GETSTRUCT(newtuple))->relispartition = false;
   19341         844 :     CatalogTupleUpdate(classRel, &newtuple->t_self, newtuple);
   19342         844 :     heap_freetuple(newtuple);
   19343         844 :     table_close(classRel, RowExclusiveLock);
   19344             : 
   19345             :     /*
   19346             :      * Drop identity property from all identity columns of partition.
   19347             :      */
   19348        2788 :     for (int attno = 0; attno < RelationGetNumberOfAttributes(partRel); attno++)
   19349             :     {
   19350        1944 :         Form_pg_attribute attr = TupleDescAttr(partRel->rd_att, attno);
   19351             : 
   19352        1944 :         if (!attr->attisdropped && attr->attidentity)
   19353          30 :             ATExecDropIdentity(partRel, NameStr(attr->attname), false,
   19354             :                                AccessExclusiveLock, true, true);
   19355             :     }
   19356             : 
   19357         844 :     if (OidIsValid(defaultPartOid))
   19358             :     {
   19359             :         /*
   19360             :          * If the relation being detached is the default partition itself,
   19361             :          * remove it from the parent's pg_partitioned_table entry.
   19362             :          *
   19363             :          * If not, we must invalidate default partition's relcache entry, as
   19364             :          * in StorePartitionBound: its partition constraint depends on every
   19365             :          * other partition's partition constraint.
   19366             :          */
   19367         232 :         if (RelationGetRelid(partRel) == defaultPartOid)
   19368          38 :             update_default_partition_oid(RelationGetRelid(rel), InvalidOid);
   19369             :         else
   19370         194 :             CacheInvalidateRelcacheByRelid(defaultPartOid);
   19371             :     }
   19372             : 
   19373             :     /*
   19374             :      * Invalidate the parent's relcache so that the partition is no longer
   19375             :      * included in its partition descriptor.
   19376             :      */
   19377         844 :     CacheInvalidateRelcache(rel);
   19378             : 
   19379             :     /*
   19380             :      * If the partition we just detached is partitioned itself, invalidate
   19381             :      * relcache for all descendent partitions too to ensure that their
   19382             :      * rd_partcheck expression trees are rebuilt; must lock partitions before
   19383             :      * doing so, using the same lockmode as what partRel has been locked with
   19384             :      * by the caller.
   19385             :      */
   19386         844 :     if (partRel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
   19387             :     {
   19388             :         List       *children;
   19389             : 
   19390          50 :         children = find_all_inheritors(RelationGetRelid(partRel),
   19391             :                                        AccessExclusiveLock, NULL);
   19392         162 :         foreach(cell, children)
   19393             :         {
   19394         112 :             CacheInvalidateRelcacheByRelid(lfirst_oid(cell));
   19395             :         }
   19396             :     }
   19397         844 : }
   19398             : 
   19399             : /*
   19400             :  * ALTER TABLE ... DETACH PARTITION ... FINALIZE
   19401             :  *
   19402             :  * To use when a DETACH PARTITION command previously did not run to
   19403             :  * completion; this completes the detaching process.
   19404             :  */
   19405             : static ObjectAddress
   19406          14 : ATExecDetachPartitionFinalize(Relation rel, RangeVar *name)
   19407             : {
   19408             :     Relation    partRel;
   19409             :     ObjectAddress address;
   19410          14 :     Snapshot    snap = GetActiveSnapshot();
   19411             : 
   19412          14 :     partRel = table_openrv(name, AccessExclusiveLock);
   19413             : 
   19414             :     /*
   19415             :      * Wait until existing snapshots are gone.  This is important if the
   19416             :      * second transaction of DETACH PARTITION CONCURRENTLY is canceled: the
   19417             :      * user could immediately run DETACH FINALIZE without actually waiting for
   19418             :      * existing transactions.  We must not complete the detach action until
   19419             :      * all such queries are complete (otherwise we would present them with an
   19420             :      * inconsistent view of catalogs).
   19421             :      */
   19422          14 :     WaitForOlderSnapshots(snap->xmin, false);
   19423             : 
   19424          14 :     DetachPartitionFinalize(rel, partRel, true, InvalidOid);
   19425             : 
   19426          14 :     ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partRel));
   19427             : 
   19428          14 :     table_close(partRel, NoLock);
   19429             : 
   19430          14 :     return address;
   19431             : }
   19432             : 
   19433             : /*
   19434             :  * DetachAddConstraintIfNeeded
   19435             :  *      Subroutine for ATExecDetachPartition.  Create a constraint that
   19436             :  *      takes the place of the partition constraint, but avoid creating
   19437             :  *      a dupe if an constraint already exists which implies the needed
   19438             :  *      constraint.
   19439             :  */
   19440             : static void
   19441         140 : DetachAddConstraintIfNeeded(List **wqueue, Relation partRel)
   19442             : {
   19443             :     List       *constraintExpr;
   19444             : 
   19445         140 :     constraintExpr = RelationGetPartitionQual(partRel);
   19446         140 :     constraintExpr = (List *) eval_const_expressions(NULL, (Node *) constraintExpr);
   19447             : 
   19448             :     /*
   19449             :      * Avoid adding a new constraint if the needed constraint is implied by an
   19450             :      * existing constraint
   19451             :      */
   19452         140 :     if (!PartConstraintImpliedByRelConstraint(partRel, constraintExpr))
   19453             :     {
   19454             :         AlteredTableInfo *tab;
   19455             :         Constraint *n;
   19456             : 
   19457         134 :         tab = ATGetQueueEntry(wqueue, partRel);
   19458             : 
   19459             :         /* Add constraint on partition, equivalent to the partition constraint */
   19460         134 :         n = makeNode(Constraint);
   19461         134 :         n->contype = CONSTR_CHECK;
   19462         134 :         n->conname = NULL;
   19463         134 :         n->location = -1;
   19464         134 :         n->is_no_inherit = false;
   19465         134 :         n->raw_expr = NULL;
   19466         134 :         n->cooked_expr = nodeToString(make_ands_explicit(constraintExpr));
   19467         134 :         n->initially_valid = true;
   19468         134 :         n->skip_validation = true;
   19469             :         /* It's a re-add, since it nominally already exists */
   19470         134 :         ATAddCheckConstraint(wqueue, tab, partRel, n,
   19471             :                              true, false, true, ShareUpdateExclusiveLock);
   19472             :     }
   19473         140 : }
   19474             : 
   19475             : /*
   19476             :  * DropClonedTriggersFromPartition
   19477             :  *      subroutine for ATExecDetachPartition to remove any triggers that were
   19478             :  *      cloned to the partition when it was created-as-partition or attached.
   19479             :  *      This undoes what CloneRowTriggersToPartition did.
   19480             :  */
   19481             : static void
   19482         846 : DropClonedTriggersFromPartition(Oid partitionId)
   19483             : {
   19484             :     ScanKeyData skey;
   19485             :     SysScanDesc scan;
   19486             :     HeapTuple   trigtup;
   19487             :     Relation    tgrel;
   19488             :     ObjectAddresses *objects;
   19489             : 
   19490         846 :     objects = new_object_addresses();
   19491             : 
   19492             :     /*
   19493             :      * Scan pg_trigger to search for all triggers on this rel.
   19494             :      */
   19495         846 :     ScanKeyInit(&skey, Anum_pg_trigger_tgrelid, BTEqualStrategyNumber,
   19496             :                 F_OIDEQ, ObjectIdGetDatum(partitionId));
   19497         846 :     tgrel = table_open(TriggerRelationId, RowExclusiveLock);
   19498         846 :     scan = systable_beginscan(tgrel, TriggerRelidNameIndexId,
   19499             :                               true, NULL, 1, &skey);
   19500        1144 :     while (HeapTupleIsValid(trigtup = systable_getnext(scan)))
   19501             :     {
   19502         298 :         Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(trigtup);
   19503             :         ObjectAddress trig;
   19504             : 
   19505             :         /* Ignore triggers that weren't cloned */
   19506         298 :         if (!OidIsValid(pg_trigger->tgparentid))
   19507         256 :             continue;
   19508             : 
   19509             :         /*
   19510             :          * Ignore internal triggers that are implementation objects of foreign
   19511             :          * keys, because these will be detached when the foreign keys
   19512             :          * themselves are.
   19513             :          */
   19514         262 :         if (OidIsValid(pg_trigger->tgconstrrelid))
   19515         220 :             continue;
   19516             : 
   19517             :         /*
   19518             :          * This is ugly, but necessary: remove the dependency markings on the
   19519             :          * trigger so that it can be removed.
   19520             :          */
   19521          42 :         deleteDependencyRecordsForClass(TriggerRelationId, pg_trigger->oid,
   19522             :                                         TriggerRelationId,
   19523             :                                         DEPENDENCY_PARTITION_PRI);
   19524          42 :         deleteDependencyRecordsForClass(TriggerRelationId, pg_trigger->oid,
   19525             :                                         RelationRelationId,
   19526             :                                         DEPENDENCY_PARTITION_SEC);
   19527             : 
   19528             :         /* remember this trigger to remove it below */
   19529          42 :         ObjectAddressSet(trig, TriggerRelationId, pg_trigger->oid);
   19530          42 :         add_exact_object_address(&trig, objects);
   19531             :     }
   19532             : 
   19533             :     /* make the dependency removal visible to the deletion below */
   19534         846 :     CommandCounterIncrement();
   19535         846 :     performMultipleDeletions(objects, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
   19536             : 
   19537             :     /* done */
   19538         846 :     free_object_addresses(objects);
   19539         846 :     systable_endscan(scan);
   19540         846 :     table_close(tgrel, RowExclusiveLock);
   19541         846 : }
   19542             : 
   19543             : /*
   19544             :  * Before acquiring lock on an index, acquire the same lock on the owning
   19545             :  * table.
   19546             :  */
   19547             : struct AttachIndexCallbackState
   19548             : {
   19549             :     Oid         partitionOid;
   19550             :     Oid         parentTblOid;
   19551             :     bool        lockedParentTbl;
   19552             : };
   19553             : 
   19554             : static void
   19555         402 : RangeVarCallbackForAttachIndex(const RangeVar *rv, Oid relOid, Oid oldRelOid,
   19556             :                                void *arg)
   19557             : {
   19558             :     struct AttachIndexCallbackState *state;
   19559             :     Form_pg_class classform;
   19560             :     HeapTuple   tuple;
   19561             : 
   19562         402 :     state = (struct AttachIndexCallbackState *) arg;
   19563             : 
   19564         402 :     if (!state->lockedParentTbl)
   19565             :     {
   19566         392 :         LockRelationOid(state->parentTblOid, AccessShareLock);
   19567         392 :         state->lockedParentTbl = true;
   19568             :     }
   19569             : 
   19570             :     /*
   19571             :      * If we previously locked some other heap, and the name we're looking up
   19572             :      * no longer refers to an index on that relation, release the now-useless
   19573             :      * lock.  XXX maybe we should do *after* we verify whether the index does
   19574             :      * not actually belong to the same relation ...
   19575             :      */
   19576         402 :     if (relOid != oldRelOid && OidIsValid(state->partitionOid))
   19577             :     {
   19578           0 :         UnlockRelationOid(state->partitionOid, AccessShareLock);
   19579           0 :         state->partitionOid = InvalidOid;
   19580             :     }
   19581             : 
   19582             :     /* Didn't find a relation, so no need for locking or permission checks. */
   19583         402 :     if (!OidIsValid(relOid))
   19584           6 :         return;
   19585             : 
   19586         396 :     tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid));
   19587         396 :     if (!HeapTupleIsValid(tuple))
   19588           0 :         return;                 /* concurrently dropped, so nothing to do */
   19589         396 :     classform = (Form_pg_class) GETSTRUCT(tuple);
   19590         396 :     if (classform->relkind != RELKIND_PARTITIONED_INDEX &&
   19591         306 :         classform->relkind != RELKIND_INDEX)
   19592           6 :         ereport(ERROR,
   19593             :                 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
   19594             :                  errmsg("\"%s\" is not an index", rv->relname)));
   19595         390 :     ReleaseSysCache(tuple);
   19596             : 
   19597             :     /*
   19598             :      * Since we need only examine the heap's tupledesc, an access share lock
   19599             :      * on it (preventing any DDL) is sufficient.
   19600             :      */
   19601         390 :     state->partitionOid = IndexGetRelation(relOid, false);
   19602         390 :     LockRelationOid(state->partitionOid, AccessShareLock);
   19603             : }
   19604             : 
   19605             : /*
   19606             :  * ALTER INDEX i1 ATTACH PARTITION i2
   19607             :  */
   19608             : static ObjectAddress
   19609         392 : ATExecAttachPartitionIdx(List **wqueue, Relation parentIdx, RangeVar *name)
   19610             : {
   19611             :     Relation    partIdx;
   19612             :     Relation    partTbl;
   19613             :     Relation    parentTbl;
   19614             :     ObjectAddress address;
   19615             :     Oid         partIdxId;
   19616             :     Oid         currParent;
   19617             :     struct AttachIndexCallbackState state;
   19618             : 
   19619             :     /*
   19620             :      * We need to obtain lock on the index 'name' to modify it, but we also
   19621             :      * need to read its owning table's tuple descriptor -- so we need to lock
   19622             :      * both.  To avoid deadlocks, obtain lock on the table before doing so on
   19623             :      * the index.  Furthermore, we need to examine the parent table of the
   19624             :      * partition, so lock that one too.
   19625             :      */
   19626         392 :     state.partitionOid = InvalidOid;
   19627         392 :     state.parentTblOid = parentIdx->rd_index->indrelid;
   19628         392 :     state.lockedParentTbl = false;
   19629             :     partIdxId =
   19630         392 :         RangeVarGetRelidExtended(name, AccessExclusiveLock, 0,
   19631             :                                  RangeVarCallbackForAttachIndex,
   19632             :                                  (void *) &state);
   19633             :     /* Not there? */
   19634         380 :     if (!OidIsValid(partIdxId))
   19635           0 :         ereport(ERROR,
   19636             :                 (errcode(ERRCODE_UNDEFINED_OBJECT),
   19637             :                  errmsg("index \"%s\" does not exist", name->relname)));
   19638             : 
   19639             :     /* no deadlock risk: RangeVarGetRelidExtended already acquired the lock */
   19640         380 :     partIdx = relation_open(partIdxId, AccessExclusiveLock);
   19641             : 
   19642             :     /* we already hold locks on both tables, so this is safe: */
   19643         380 :     parentTbl = relation_open(parentIdx->rd_index->indrelid, AccessShareLock);
   19644         380 :     partTbl = relation_open(partIdx->rd_index->indrelid, NoLock);
   19645             : 
   19646         380 :     ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partIdx));
   19647             : 
   19648             :     /* Silently do nothing if already in the right state */
   19649         760 :     currParent = partIdx->rd_rel->relispartition ?
   19650         380 :         get_partition_parent(partIdxId, false) : InvalidOid;
   19651         380 :     if (currParent != RelationGetRelid(parentIdx))
   19652             :     {
   19653             :         IndexInfo  *childInfo;
   19654             :         IndexInfo  *parentInfo;
   19655             :         AttrMap    *attmap;
   19656             :         bool        found;
   19657             :         int         i;
   19658             :         PartitionDesc partDesc;
   19659             :         Oid         constraintOid,
   19660         356 :                     cldConstrId = InvalidOid;
   19661             : 
   19662             :         /*
   19663             :          * If this partition already has an index attached, refuse the
   19664             :          * operation.
   19665             :          */
   19666         356 :         refuseDupeIndexAttach(parentIdx, partIdx, partTbl);
   19667             : 
   19668         350 :         if (OidIsValid(currParent))
   19669           0 :             ereport(ERROR,
   19670             :                     (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
   19671             :                      errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
   19672             :                             RelationGetRelationName(partIdx),
   19673             :                             RelationGetRelationName(parentIdx)),
   19674             :                      errdetail("Index \"%s\" is already attached to another index.",
   19675             :                                RelationGetRelationName(partIdx))));
   19676             : 
   19677             :         /* Make sure it indexes a partition of the other index's table */
   19678         350 :         partDesc = RelationGetPartitionDesc(parentTbl, true);
   19679         350 :         found = false;
   19680         552 :         for (i = 0; i < partDesc->nparts; i++)
   19681             :         {
   19682         546 :             if (partDesc->oids[i] == state.partitionOid)
   19683             :             {
   19684         344 :                 found = true;
   19685         344 :                 break;
   19686             :             }
   19687             :         }
   19688         350 :         if (!found)
   19689           6 :             ereport(ERROR,
   19690             :                     (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
   19691             :                      errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
   19692             :                             RelationGetRelationName(partIdx),
   19693             :                             RelationGetRelationName(parentIdx)),
   19694             :                      errdetail("Index \"%s\" is not an index on any partition of table \"%s\".",
   19695             :                                RelationGetRelationName(partIdx),
   19696             :                                RelationGetRelationName(parentTbl))));
   19697             : 
   19698             :         /* Ensure the indexes are compatible */
   19699         344 :         childInfo = BuildIndexInfo(partIdx);
   19700         344 :         parentInfo = BuildIndexInfo(parentIdx);
   19701         344 :         attmap = build_attrmap_by_name(RelationGetDescr(partTbl),
   19702             :                                        RelationGetDescr(parentTbl),
   19703             :                                        false);
   19704         344 :         if (!CompareIndexInfo(childInfo, parentInfo,
   19705         344 :                               partIdx->rd_indcollation,
   19706         344 :                               parentIdx->rd_indcollation,
   19707         344 :                               partIdx->rd_opfamily,
   19708         344 :                               parentIdx->rd_opfamily,
   19709             :                               attmap))
   19710          42 :             ereport(ERROR,
   19711             :                     (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
   19712             :                      errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
   19713             :                             RelationGetRelationName(partIdx),
   19714             :                             RelationGetRelationName(parentIdx)),
   19715             :                      errdetail("The index definitions do not match.")));
   19716             : 
   19717             :         /*
   19718             :          * If there is a constraint in the parent, make sure there is one in
   19719             :          * the child too.
   19720             :          */
   19721         302 :         constraintOid = get_relation_idx_constraint_oid(RelationGetRelid(parentTbl),
   19722             :                                                         RelationGetRelid(parentIdx));
   19723             : 
   19724         302 :         if (OidIsValid(constraintOid))
   19725             :         {
   19726         122 :             cldConstrId = get_relation_idx_constraint_oid(RelationGetRelid(partTbl),
   19727             :                                                           partIdxId);
   19728         122 :             if (!OidIsValid(cldConstrId))
   19729           6 :                 ereport(ERROR,
   19730             :                         (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
   19731             :                          errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
   19732             :                                 RelationGetRelationName(partIdx),
   19733             :                                 RelationGetRelationName(parentIdx)),
   19734             :                          errdetail("The index \"%s\" belongs to a constraint in table \"%s\" but no constraint exists for index \"%s\".",
   19735             :                                    RelationGetRelationName(parentIdx),
   19736             :                                    RelationGetRelationName(parentTbl),
   19737             :                                    RelationGetRelationName(partIdx))));
   19738             :         }
   19739             : 
   19740             :         /* All good -- do it */
   19741         296 :         IndexSetParentIndex(partIdx, RelationGetRelid(parentIdx));
   19742         296 :         if (OidIsValid(constraintOid))
   19743         116 :             ConstraintSetParentConstraint(cldConstrId, constraintOid,
   19744             :                                           RelationGetRelid(partTbl));
   19745             : 
   19746         296 :         free_attrmap(attmap);
   19747             : 
   19748         296 :         validatePartitionedIndex(parentIdx, parentTbl);
   19749             :     }
   19750             : 
   19751         320 :     relation_close(parentTbl, AccessShareLock);
   19752             :     /* keep these locks till commit */
   19753         320 :     relation_close(partTbl, NoLock);
   19754         320 :     relation_close(partIdx, NoLock);
   19755             : 
   19756         320 :     return address;
   19757             : }
   19758             : 
   19759             : /*
   19760             :  * Verify whether the given partition already contains an index attached
   19761             :  * to the given partitioned index.  If so, raise an error.
   19762             :  */
   19763             : static void
   19764         356 : refuseDupeIndexAttach(Relation parentIdx, Relation partIdx, Relation partitionTbl)
   19765             : {
   19766             :     Oid         existingIdx;
   19767             : 
   19768         356 :     existingIdx = index_get_partition(partitionTbl,
   19769             :                                       RelationGetRelid(parentIdx));
   19770         356 :     if (OidIsValid(existingIdx))
   19771           6 :         ereport(ERROR,
   19772             :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
   19773             :                  errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
   19774             :                         RelationGetRelationName(partIdx),
   19775             :                         RelationGetRelationName(parentIdx)),
   19776             :                  errdetail("Another index is already attached for partition \"%s\".",
   19777             :                            RelationGetRelationName(partitionTbl))));
   19778         350 : }
   19779             : 
   19780             : /*
   19781             :  * Verify whether the set of attached partition indexes to a parent index on
   19782             :  * a partitioned table is complete.  If it is, mark the parent index valid.
   19783             :  *
   19784             :  * This should be called each time a partition index is attached.
   19785             :  */
   19786             : static void
   19787         338 : validatePartitionedIndex(Relation partedIdx, Relation partedTbl)
   19788             : {
   19789             :     Relation    inheritsRel;
   19790             :     SysScanDesc scan;
   19791             :     ScanKeyData key;
   19792         338 :     int         tuples = 0;
   19793             :     HeapTuple   inhTup;
   19794         338 :     bool        updated = false;
   19795             : 
   19796             :     Assert(partedIdx->rd_rel->relkind == RELKIND_PARTITIONED_INDEX);
   19797             : 
   19798             :     /*
   19799             :      * Scan pg_inherits for this parent index.  Count each valid index we find
   19800             :      * (verifying the pg_index entry for each), and if we reach the total
   19801             :      * amount we expect, we can mark this parent index as valid.
   19802             :      */
   19803         338 :     inheritsRel = table_open(InheritsRelationId, AccessShareLock);
   19804         338 :     ScanKeyInit(&key, Anum_pg_inherits_inhparent,
   19805             :                 BTEqualStrategyNumber, F_OIDEQ,
   19806             :                 ObjectIdGetDatum(RelationGetRelid(partedIdx)));
   19807         338 :     scan = systable_beginscan(inheritsRel, InheritsParentIndexId, true,
   19808             :                               NULL, 1, &key);
   19809         880 :     while ((inhTup = systable_getnext(scan)) != NULL)
   19810             :     {
   19811         542 :         Form_pg_inherits inhForm = (Form_pg_inherits) GETSTRUCT(inhTup);
   19812             :         HeapTuple   indTup;
   19813             :         Form_pg_index indexForm;
   19814             : 
   19815         542 :         indTup = SearchSysCache1(INDEXRELID,
   19816             :                                  ObjectIdGetDatum(inhForm->inhrelid));
   19817         542 :         if (!HeapTupleIsValid(indTup))
   19818           0 :             elog(ERROR, "cache lookup failed for index %u", inhForm->inhrelid);
   19819         542 :         indexForm = (Form_pg_index) GETSTRUCT(indTup);
   19820         542 :         if (indexForm->indisvalid)
   19821         484 :             tuples += 1;
   19822         542 :         ReleaseSysCache(indTup);
   19823             :     }
   19824             : 
   19825             :     /* Done with pg_inherits */
   19826         338 :     systable_endscan(scan);
   19827         338 :     table_close(inheritsRel, AccessShareLock);
   19828             : 
   19829             :     /*
   19830             :      * If we found as many inherited indexes as the partitioned table has
   19831             :      * partitions, we're good; update pg_index to set indisvalid.
   19832             :      */
   19833         338 :     if (tuples == RelationGetPartitionDesc(partedTbl, true)->nparts)
   19834             :     {
   19835             :         Relation    idxRel;
   19836             :         HeapTuple   indTup;
   19837             :         Form_pg_index indexForm;
   19838             : 
   19839         168 :         idxRel = table_open(IndexRelationId, RowExclusiveLock);
   19840         168 :         indTup = SearchSysCacheCopy1(INDEXRELID,
   19841             :                                      ObjectIdGetDatum(RelationGetRelid(partedIdx)));
   19842         168 :         if (!HeapTupleIsValid(indTup))
   19843           0 :             elog(ERROR, "cache lookup failed for index %u",
   19844             :                  RelationGetRelid(partedIdx));
   19845         168 :         indexForm = (Form_pg_index) GETSTRUCT(indTup);
   19846             : 
   19847         168 :         indexForm->indisvalid = true;
   19848         168 :         updated = true;
   19849             : 
   19850         168 :         CatalogTupleUpdate(idxRel, &indTup->t_self, indTup);
   19851             : 
   19852         168 :         table_close(idxRel, RowExclusiveLock);
   19853         168 :         heap_freetuple(indTup);
   19854             :     }
   19855             : 
   19856             :     /*
   19857             :      * If this index is in turn a partition of a larger index, validating it
   19858             :      * might cause the parent to become valid also.  Try that.
   19859             :      */
   19860         338 :     if (updated && partedIdx->rd_rel->relispartition)
   19861             :     {
   19862             :         Oid         parentIdxId,
   19863             :                     parentTblId;
   19864             :         Relation    parentIdx,
   19865             :                     parentTbl;
   19866             : 
   19867             :         /* make sure we see the validation we just did */
   19868          42 :         CommandCounterIncrement();
   19869             : 
   19870          42 :         parentIdxId = get_partition_parent(RelationGetRelid(partedIdx), false);
   19871          42 :         parentTblId = get_partition_parent(RelationGetRelid(partedTbl), false);
   19872          42 :         parentIdx = relation_open(parentIdxId, AccessExclusiveLock);
   19873          42 :         parentTbl = relation_open(parentTblId, AccessExclusiveLock);
   19874             :         Assert(!parentIdx->rd_index->indisvalid);
   19875             : 
   19876          42 :         validatePartitionedIndex(parentIdx, parentTbl);
   19877             : 
   19878          42 :         relation_close(parentIdx, AccessExclusiveLock);
   19879          42 :         relation_close(parentTbl, AccessExclusiveLock);
   19880             :     }
   19881         338 : }
   19882             : 
   19883             : /*
   19884             :  * Return an OID list of constraints that reference the given relation
   19885             :  * that are marked as having a parent constraints.
   19886             :  */
   19887             : static List *
   19888        1342 : GetParentedForeignKeyRefs(Relation partition)
   19889             : {
   19890             :     Relation    pg_constraint;
   19891             :     HeapTuple   tuple;
   19892             :     SysScanDesc scan;
   19893             :     ScanKeyData key[2];
   19894        1342 :     List       *constraints = NIL;
   19895             : 
   19896             :     /*
   19897             :      * If no indexes, or no columns are referenceable by FKs, we can avoid the
   19898             :      * scan.
   19899             :      */
   19900        1902 :     if (RelationGetIndexList(partition) == NIL ||
   19901         560 :         bms_is_empty(RelationGetIndexAttrBitmap(partition,
   19902             :                                                 INDEX_ATTR_BITMAP_KEY)))
   19903        1028 :         return NIL;
   19904             : 
   19905             :     /* Search for constraints referencing this table */
   19906         314 :     pg_constraint = table_open(ConstraintRelationId, AccessShareLock);
   19907         314 :     ScanKeyInit(&key[0],
   19908             :                 Anum_pg_constraint_confrelid, BTEqualStrategyNumber,
   19909             :                 F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(partition)));
   19910         314 :     ScanKeyInit(&key[1],
   19911             :                 Anum_pg_constraint_contype, BTEqualStrategyNumber,
   19912             :                 F_CHAREQ, CharGetDatum(CONSTRAINT_FOREIGN));
   19913             : 
   19914             :     /* XXX This is a seqscan, as we don't have a usable index */
   19915         314 :     scan = systable_beginscan(pg_constraint, InvalidOid, true, NULL, 2, key);
   19916         444 :     while ((tuple = systable_getnext(scan)) != NULL)
   19917             :     {
   19918         130 :         Form_pg_constraint constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
   19919             : 
   19920             :         /*
   19921             :          * We only need to process constraints that are part of larger ones.
   19922             :          */
   19923         130 :         if (!OidIsValid(constrForm->conparentid))
   19924           0 :             continue;
   19925             : 
   19926         130 :         constraints = lappend_oid(constraints, constrForm->oid);
   19927             :     }
   19928             : 
   19929         314 :     systable_endscan(scan);
   19930         314 :     table_close(pg_constraint, AccessShareLock);
   19931             : 
   19932         314 :     return constraints;
   19933             : }
   19934             : 
   19935             : /*
   19936             :  * During DETACH PARTITION, verify that any foreign keys pointing to the
   19937             :  * partitioned table would not become invalid.  An error is raised if any
   19938             :  * referenced values exist.
   19939             :  */
   19940             : static void
   19941         496 : ATDetachCheckNoForeignKeyRefs(Relation partition)
   19942             : {
   19943             :     List       *constraints;
   19944             :     ListCell   *cell;
   19945             : 
   19946         496 :     constraints = GetParentedForeignKeyRefs(partition);
   19947             : 
   19948         548 :     foreach(cell, constraints)
   19949             :     {
   19950          86 :         Oid         constrOid = lfirst_oid(cell);
   19951             :         HeapTuple   tuple;
   19952             :         Form_pg_constraint constrForm;
   19953             :         Relation    rel;
   19954          86 :         Trigger     trig = {0};
   19955             : 
   19956          86 :         tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constrOid));
   19957          86 :         if (!HeapTupleIsValid(tuple))
   19958           0 :             elog(ERROR, "cache lookup failed for constraint %u", constrOid);
   19959          86 :         constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
   19960             : 
   19961             :         Assert(OidIsValid(constrForm->conparentid));
   19962             :         Assert(constrForm->confrelid == RelationGetRelid(partition));
   19963             : 
   19964             :         /* prevent data changes into the referencing table until commit */
   19965          86 :         rel = table_open(constrForm->conrelid, ShareLock);
   19966             : 
   19967          86 :         trig.tgoid = InvalidOid;
   19968          86 :         trig.tgname = NameStr(constrForm->conname);
   19969          86 :         trig.tgenabled = TRIGGER_FIRES_ON_ORIGIN;
   19970          86 :         trig.tgisinternal = true;
   19971          86 :         trig.tgconstrrelid = RelationGetRelid(partition);
   19972          86 :         trig.tgconstrindid = constrForm->conindid;
   19973          86 :         trig.tgconstraint = constrForm->oid;
   19974          86 :         trig.tgdeferrable = false;
   19975          86 :         trig.tginitdeferred = false;
   19976             :         /* we needn't fill in remaining fields */
   19977             : 
   19978          86 :         RI_PartitionRemove_Check(&trig, rel, partition);
   19979             : 
   19980          52 :         ReleaseSysCache(tuple);
   19981             : 
   19982          52 :         table_close(rel, NoLock);
   19983             :     }
   19984         462 : }
   19985             : 
   19986             : /*
   19987             :  * resolve column compression specification to compression method.
   19988             :  */
   19989             : static char
   19990      216654 : GetAttributeCompression(Oid atttypid, const char *compression)
   19991             : {
   19992             :     char        cmethod;
   19993             : 
   19994      216654 :     if (compression == NULL || strcmp(compression, "default") == 0)
   19995      216504 :         return InvalidCompressionMethod;
   19996             : 
   19997             :     /*
   19998             :      * To specify a nondefault method, the column data type must be toastable.
   19999             :      * Note this says nothing about whether the column's attstorage setting
   20000             :      * permits compression; we intentionally allow attstorage and
   20001             :      * attcompression to be independent.  But with a non-toastable type,
   20002             :      * attstorage could not be set to a value that would permit compression.
   20003             :      *
   20004             :      * We don't actually need to enforce this, since nothing bad would happen
   20005             :      * if attcompression were non-default; it would never be consulted.  But
   20006             :      * it seems more user-friendly to complain about a certainly-useless
   20007             :      * attempt to set the property.
   20008             :      */
   20009         150 :     if (!TypeIsToastable(atttypid))
   20010           6 :         ereport(ERROR,
   20011             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   20012             :                  errmsg("column data type %s does not support compression",
   20013             :                         format_type_be(atttypid))));
   20014             : 
   20015         144 :     cmethod = CompressionNameToMethod(compression);
   20016         144 :     if (!CompressionMethodIsValid(cmethod))
   20017          12 :         ereport(ERROR,
   20018             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
   20019             :                  errmsg("invalid compression method \"%s\"", compression)));
   20020             : 
   20021         132 :     return cmethod;
   20022             : }
   20023             : 
   20024             : /*
   20025             :  * resolve column storage specification
   20026             :  */
   20027             : static char
   20028         242 : GetAttributeStorage(Oid atttypid, const char *storagemode)
   20029             : {
   20030         242 :     char        cstorage = 0;
   20031             : 
   20032         242 :     if (pg_strcasecmp(storagemode, "plain") == 0)
   20033          50 :         cstorage = TYPSTORAGE_PLAIN;
   20034         192 :     else if (pg_strcasecmp(storagemode, "external") == 0)
   20035         156 :         cstorage = TYPSTORAGE_EXTERNAL;
   20036          36 :     else if (pg_strcasecmp(storagemode, "extended") == 0)
   20037          16 :         cstorage = TYPSTORAGE_EXTENDED;
   20038          20 :     else if (pg_strcasecmp(storagemode, "main") == 0)
   20039          14 :         cstorage = TYPSTORAGE_MAIN;
   20040           6 :     else if (pg_strcasecmp(storagemode, "default") == 0)
   20041           6 :         cstorage = get_typstorage(atttypid);
   20042             :     else
   20043           0 :         ereport(ERROR,
   20044             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
   20045             :                  errmsg("invalid storage type \"%s\"",
   20046             :                         storagemode)));
   20047             : 
   20048             :     /*
   20049             :      * safety check: do not allow toasted storage modes unless column datatype
   20050             :      * is TOAST-aware.
   20051             :      */
   20052         242 :     if (!(cstorage == TYPSTORAGE_PLAIN || TypeIsToastable(atttypid)))
   20053           6 :         ereport(ERROR,
   20054             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   20055             :                  errmsg("column data type %s can only have storage PLAIN",
   20056             :                         format_type_be(atttypid))));
   20057             : 
   20058         236 :     return cstorage;
   20059             : }
   20060             : 
   20061             : /*
   20062             :  * Struct with context of new partition for inserting rows from split partition
   20063             :  */
   20064             : typedef struct SplitPartitionContext
   20065             : {
   20066             :     ExprState  *partqualstate;  /* expression for checking slot for partition
   20067             :                                  * (NULL for DEFAULT partition) */
   20068             :     BulkInsertState bistate;    /* state of bulk inserts for partition */
   20069             :     TupleTableSlot *dstslot;    /* slot for inserting row into partition */
   20070             :     Relation    partRel;        /* relation for partition */
   20071             : } SplitPartitionContext;
   20072             : 
   20073             : 
   20074             : /*
   20075             :  * createSplitPartitionContext: create context for partition and fill it
   20076             :  */
   20077             : static SplitPartitionContext *
   20078         480 : createSplitPartitionContext(Relation partRel)
   20079             : {
   20080             :     SplitPartitionContext *pc;
   20081             : 
   20082         480 :     pc = (SplitPartitionContext *) palloc0(sizeof(SplitPartitionContext));
   20083         480 :     pc->partRel = partRel;
   20084             : 
   20085             :     /*
   20086             :      * Prepare a BulkInsertState for table_tuple_insert. The FSM is empty, so
   20087             :      * don't bother using it.
   20088             :      */
   20089         480 :     pc->bistate = GetBulkInsertState();
   20090             : 
   20091             :     /* Create tuple slot for new partition. */
   20092         480 :     pc->dstslot = MakeSingleTupleTableSlot(RelationGetDescr(pc->partRel),
   20093             :                                            table_slot_callbacks(pc->partRel));
   20094         480 :     ExecStoreAllNullTuple(pc->dstslot);
   20095             : 
   20096         480 :     return pc;
   20097             : }
   20098             : 
   20099             : /*
   20100             :  * deleteSplitPartitionContext: delete context for partition
   20101             :  */
   20102             : static void
   20103         480 : deleteSplitPartitionContext(SplitPartitionContext *pc, int ti_options)
   20104             : {
   20105         480 :     ExecDropSingleTupleTableSlot(pc->dstslot);
   20106         480 :     FreeBulkInsertState(pc->bistate);
   20107             : 
   20108         480 :     table_finish_bulk_insert(pc->partRel, ti_options);
   20109             : 
   20110         480 :     pfree(pc);
   20111         480 : }
   20112             : 
   20113             : /*
   20114             :  * moveSplitTableRows: scan split partition (splitRel) of partitioned table
   20115             :  * (rel) and move rows into new partitions.
   20116             :  *
   20117             :  * New partitions description:
   20118             :  * partlist: list of pointers to SinglePartitionSpec structures.
   20119             :  * newPartRels: list of Relations.
   20120             :  * defaultPartOid: oid of DEFAULT partition, for table rel.
   20121             :  */
   20122             : static void
   20123         150 : moveSplitTableRows(Relation rel, Relation splitRel, List *partlist, List *newPartRels, Oid defaultPartOid)
   20124             : {
   20125             :     /* The FSM is empty, so don't bother using it. */
   20126         150 :     int         ti_options = TABLE_INSERT_SKIP_FSM;
   20127             :     CommandId   mycid;
   20128             :     EState     *estate;
   20129             :     ListCell   *listptr,
   20130             :                *listptr2;
   20131             :     TupleTableSlot *srcslot;
   20132             :     ExprContext *econtext;
   20133             :     TableScanDesc scan;
   20134             :     Snapshot    snapshot;
   20135             :     MemoryContext oldCxt;
   20136         150 :     List       *partContexts = NIL;
   20137             :     TupleConversionMap *tuple_map;
   20138         150 :     SplitPartitionContext *defaultPartCtx = NULL,
   20139             :                *pc;
   20140         150 :     bool        isOldDefaultPart = false;
   20141             : 
   20142         150 :     mycid = GetCurrentCommandId(true);
   20143             : 
   20144         150 :     estate = CreateExecutorState();
   20145             : 
   20146         582 :     forboth(listptr, partlist, listptr2, newPartRels)
   20147             :     {
   20148         432 :         SinglePartitionSpec *sps = (SinglePartitionSpec *) lfirst(listptr);
   20149             : 
   20150         432 :         pc = createSplitPartitionContext((Relation) lfirst(listptr2));
   20151             : 
   20152         432 :         if (sps->bound->is_default)
   20153             :         {
   20154             :             /* We should not create constraint for detached DEFAULT partition. */
   20155          30 :             defaultPartCtx = pc;
   20156             :         }
   20157             :         else
   20158             :         {
   20159             :             List       *partConstraint;
   20160             : 
   20161             :             /* Build expression execution states for partition check quals. */
   20162         402 :             partConstraint = get_qual_from_partbound(rel, sps->bound);
   20163             :             partConstraint =
   20164         402 :                 (List *) eval_const_expressions(NULL,
   20165             :                                                 (Node *) partConstraint);
   20166             :             /* Make boolean expression for ExecCheck(). */
   20167         402 :             partConstraint = list_make1(make_ands_explicit(partConstraint));
   20168             : 
   20169             :             /*
   20170             :              * Map the vars in the constraint expression from rel's attnos to
   20171             :              * splitRel's.
   20172             :              */
   20173         402 :             partConstraint = map_partition_varattnos(partConstraint,
   20174             :                                                      1, splitRel, rel);
   20175             : 
   20176         402 :             pc->partqualstate =
   20177         402 :                 ExecPrepareExpr((Expr *) linitial(partConstraint), estate);
   20178             :             Assert(pc->partqualstate != NULL);
   20179             :         }
   20180             : 
   20181             :         /* Store partition context into list. */
   20182         432 :         partContexts = lappend(partContexts, pc);
   20183             :     }
   20184             : 
   20185             :     /*
   20186             :      * Create partition context for DEFAULT partition. We can insert values
   20187             :      * into this partition in case spaces with values between new partitions.
   20188             :      */
   20189         150 :     if (!defaultPartCtx && OidIsValid(defaultPartOid))
   20190             :     {
   20191             :         /* Indicate that we allocate context for old DEFAULT partition */
   20192          48 :         isOldDefaultPart = true;
   20193          48 :         defaultPartCtx = createSplitPartitionContext(table_open(defaultPartOid, AccessExclusiveLock));
   20194             :     }
   20195             : 
   20196         150 :     econtext = GetPerTupleExprContext(estate);
   20197             : 
   20198             :     /* Create necessary tuple slot. */
   20199         150 :     srcslot = MakeSingleTupleTableSlot(RelationGetDescr(splitRel),
   20200             :                                        table_slot_callbacks(splitRel));
   20201             : 
   20202             :     /*
   20203             :      * Map computing for moving attributes of split partition to new partition
   20204             :      * (for first new partition, but other new partitions can use the same
   20205             :      * map).
   20206             :      */
   20207         150 :     pc = (SplitPartitionContext *) lfirst(list_head(partContexts));
   20208         150 :     tuple_map = convert_tuples_by_name(RelationGetDescr(splitRel),
   20209         150 :                                        RelationGetDescr(pc->partRel));
   20210             : 
   20211             :     /* Scan through the rows. */
   20212         150 :     snapshot = RegisterSnapshot(GetLatestSnapshot());
   20213         150 :     scan = table_beginscan(splitRel, snapshot, 0, NULL);
   20214             : 
   20215             :     /*
   20216             :      * Switch to per-tuple memory context and reset it for each tuple
   20217             :      * produced, so we don't leak memory.
   20218             :      */
   20219         150 :     oldCxt = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
   20220             : 
   20221         690 :     while (table_scan_getnextslot(scan, ForwardScanDirection, srcslot))
   20222             :     {
   20223         540 :         bool        found = false;
   20224             :         TupleTableSlot *insertslot;
   20225             : 
   20226             :         /* Extract data from old tuple. */
   20227         540 :         slot_getallattrs(srcslot);
   20228             : 
   20229         540 :         econtext->ecxt_scantuple = srcslot;
   20230             : 
   20231             :         /* Search partition for current slot srcslot. */
   20232        1488 :         foreach(listptr, partContexts)
   20233             :         {
   20234        1374 :             pc = (SplitPartitionContext *) lfirst(listptr);
   20235             : 
   20236        2640 :             if (pc->partqualstate /* skip DEFAULT partition */ &&
   20237        1266 :                 ExecCheck(pc->partqualstate, econtext))
   20238             :             {
   20239         426 :                 found = true;
   20240         426 :                 break;
   20241             :             }
   20242         948 :             ResetExprContext(econtext);
   20243             :         }
   20244         540 :         if (!found)
   20245             :         {
   20246             :             /* Use DEFAULT partition if it exists. */
   20247         114 :             if (defaultPartCtx)
   20248         114 :                 pc = defaultPartCtx;
   20249             :             else
   20250           0 :                 ereport(ERROR,
   20251             :                         (errcode(ERRCODE_CHECK_VIOLATION),
   20252             :                          errmsg("can not find partition for split partition row"),
   20253             :                          errtable(splitRel)));
   20254             :         }
   20255             : 
   20256         540 :         if (tuple_map)
   20257             :         {
   20258             :             /* Need to use map to copy attributes. */
   20259          24 :             insertslot = execute_attr_map_slot(tuple_map->attrMap, srcslot, pc->dstslot);
   20260             :         }
   20261             :         else
   20262             :         {
   20263             :             /* Copy attributes directly. */
   20264         516 :             insertslot = pc->dstslot;
   20265             : 
   20266         516 :             ExecClearTuple(insertslot);
   20267             : 
   20268         516 :             memcpy(insertslot->tts_values, srcslot->tts_values,
   20269         516 :                    sizeof(Datum) * srcslot->tts_nvalid);
   20270         516 :             memcpy(insertslot->tts_isnull, srcslot->tts_isnull,
   20271         516 :                    sizeof(bool) * srcslot->tts_nvalid);
   20272             : 
   20273         516 :             ExecStoreVirtualTuple(insertslot);
   20274             :         }
   20275             : 
   20276             :         /* Write the tuple out to the new relation. */
   20277         540 :         table_tuple_insert(pc->partRel, insertslot, mycid,
   20278             :                            ti_options, pc->bistate);
   20279             : 
   20280         540 :         ResetExprContext(econtext);
   20281             : 
   20282         540 :         CHECK_FOR_INTERRUPTS();
   20283             :     }
   20284             : 
   20285         150 :     MemoryContextSwitchTo(oldCxt);
   20286             : 
   20287         150 :     table_endscan(scan);
   20288         150 :     UnregisterSnapshot(snapshot);
   20289             : 
   20290         150 :     if (tuple_map)
   20291           6 :         free_conversion_map(tuple_map);
   20292             : 
   20293         150 :     ExecDropSingleTupleTableSlot(srcslot);
   20294             : 
   20295         150 :     FreeExecutorState(estate);
   20296             : 
   20297         582 :     foreach(listptr, partContexts)
   20298         432 :         deleteSplitPartitionContext((SplitPartitionContext *) lfirst(listptr), ti_options);
   20299             : 
   20300             :     /* Need to close table and free buffers for DEFAULT partition. */
   20301         150 :     if (isOldDefaultPart)
   20302             :     {
   20303          48 :         Relation    defaultPartRel = defaultPartCtx->partRel;
   20304             : 
   20305          48 :         deleteSplitPartitionContext(defaultPartCtx, ti_options);
   20306             :         /* Keep the lock until commit. */
   20307          48 :         table_close(defaultPartRel, NoLock);
   20308             :     }
   20309         150 : }
   20310             : 
   20311             : /*
   20312             :  * createPartitionTable: create table for a new partition with given name
   20313             :  * (newPartName) like table (modelRel)
   20314             :  *
   20315             :  * Emulates command: CREATE [TEMP] TABLE <newPartName> (LIKE <modelRel's name>
   20316             :  * INCLUDING ALL EXCLUDING INDEXES EXCLUDING IDENTITY EXCLUDING STATISTICS)
   20317             :  *
   20318             :  * Also, this function sets the new partition access method same as parent
   20319             :  * table access methods (similarly to CREATE TABLE ... PARTITION OF).  It
   20320             :  * checks that parent and child tables have compatible persistence.
   20321             :  *
   20322             :  * Function returns the created relation (locked in AccessExclusiveLock mode).
   20323             :  */
   20324             : static Relation
   20325         546 : createPartitionTable(RangeVar *newPartName, Relation modelRel,
   20326             :                      AlterTableUtilityContext *context)
   20327             : {
   20328             :     CreateStmt *createStmt;
   20329             :     TableLikeClause *tlc;
   20330             :     PlannedStmt *wrapper;
   20331             :     Relation    newRel;
   20332             : 
   20333             :     /* If existing rel is temp, it must belong to this session */
   20334         546 :     if (modelRel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
   20335          36 :         !modelRel->rd_islocaltemp)
   20336           0 :         ereport(ERROR,
   20337             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   20338             :                  errmsg("cannot create as partition of temporary relation of another session")));
   20339             : 
   20340             :     /* New partition should have the same persistence as modelRel */
   20341         546 :     newPartName->relpersistence = modelRel->rd_rel->relpersistence;
   20342             : 
   20343         546 :     createStmt = makeNode(CreateStmt);
   20344         546 :     createStmt->relation = newPartName;
   20345         546 :     createStmt->tableElts = NIL;
   20346         546 :     createStmt->inhRelations = NIL;
   20347         546 :     createStmt->constraints = NIL;
   20348         546 :     createStmt->options = NIL;
   20349         546 :     createStmt->oncommit = ONCOMMIT_NOOP;
   20350         546 :     createStmt->tablespacename = get_tablespace_name(modelRel->rd_rel->reltablespace);
   20351         546 :     createStmt->if_not_exists = false;
   20352         546 :     createStmt->accessMethod = get_am_name(modelRel->rd_rel->relam);
   20353             : 
   20354         546 :     tlc = makeNode(TableLikeClause);
   20355         546 :     tlc->relation = makeRangeVar(get_namespace_name(RelationGetNamespace(modelRel)),
   20356         546 :                                  RelationGetRelationName(modelRel), -1);
   20357             : 
   20358             :     /*
   20359             :      * Indexes will be inherited on "attach new partitions" stage, after data
   20360             :      * moving.  We also don't copy the extended statistics for consistency
   20361             :      * with CREATE TABLE PARTITION OF.
   20362             :      */
   20363         546 :     tlc->options = CREATE_TABLE_LIKE_ALL &
   20364             :         ~(CREATE_TABLE_LIKE_INDEXES | CREATE_TABLE_LIKE_IDENTITY | CREATE_TABLE_LIKE_STATISTICS);
   20365         546 :     tlc->relationOid = InvalidOid;
   20366         546 :     createStmt->tableElts = lappend(createStmt->tableElts, tlc);
   20367             : 
   20368             :     /* Need to make a wrapper PlannedStmt. */
   20369         546 :     wrapper = makeNode(PlannedStmt);
   20370         546 :     wrapper->commandType = CMD_UTILITY;
   20371         546 :     wrapper->canSetTag = false;
   20372         546 :     wrapper->utilityStmt = (Node *) createStmt;
   20373         546 :     wrapper->stmt_location = context->pstmt->stmt_location;
   20374         546 :     wrapper->stmt_len = context->pstmt->stmt_len;
   20375             : 
   20376         546 :     ProcessUtility(wrapper,
   20377             :                    context->queryString,
   20378             :                    false,
   20379             :                    PROCESS_UTILITY_SUBCOMMAND,
   20380             :                    NULL,
   20381             :                    NULL,
   20382             :                    None_Receiver,
   20383             :                    NULL);
   20384             : 
   20385             :     /*
   20386             :      * Open the new partition with no lock, because we already have
   20387             :      * AccessExclusiveLock placed there after creation.
   20388             :      */
   20389         546 :     newRel = table_openrv(newPartName, NoLock);
   20390             : 
   20391             :     /*
   20392             :      * We intended to create the partition with the same persistence as the
   20393             :      * parent table, but we still need to recheck because that might be
   20394             :      * affected by the search_path.  If the parent is permanent, so must be
   20395             :      * all of its partitions.
   20396             :      */
   20397         546 :     if (modelRel->rd_rel->relpersistence != RELPERSISTENCE_TEMP &&
   20398         510 :         newRel->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
   20399          12 :         ereport(ERROR,
   20400             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   20401             :                  errmsg("cannot create a temporary relation as partition of permanent relation \"%s\"",
   20402             :                         RelationGetRelationName(modelRel))));
   20403             : 
   20404             :     /* Permanent rels cannot be partitions belonging to temporary parent */
   20405         534 :     if (newRel->rd_rel->relpersistence != RELPERSISTENCE_TEMP &&
   20406         498 :         modelRel->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
   20407           0 :         ereport(ERROR,
   20408             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   20409             :                  errmsg("cannot create a permanent relation as partition of temporary relation \"%s\"",
   20410             :                         RelationGetRelationName(modelRel))));
   20411             : 
   20412         534 :     return newRel;
   20413             : }
   20414             : 
   20415             : /*
   20416             :  * ALTER TABLE <name> SPLIT PARTITION <partition-name> INTO <partition-list>
   20417             :  */
   20418             : static void
   20419         156 : ATExecSplitPartition(List **wqueue, AlteredTableInfo *tab, Relation rel,
   20420             :                      PartitionCmd *cmd, AlterTableUtilityContext *context)
   20421             : {
   20422             :     Relation    splitRel;
   20423             :     Oid         splitRelOid;
   20424             :     char        relname[NAMEDATALEN];
   20425             :     Oid         namespaceId;
   20426             :     ListCell   *listptr,
   20427             :                *listptr2;
   20428         156 :     bool        isSameName = false;
   20429             :     char        tmpRelName[NAMEDATALEN];
   20430         156 :     List       *newPartRels = NIL;
   20431             :     ObjectAddress object;
   20432             :     Oid         defaultPartOid;
   20433             : 
   20434         156 :     defaultPartOid = get_default_oid_from_partdesc(RelationGetPartitionDesc(rel, true));
   20435             : 
   20436             :     /*
   20437             :      * We are going to detach and remove this partition: need to use exclusive
   20438             :      * lock for preventing DML-queries to the partition.
   20439             :      */
   20440         156 :     splitRel = table_openrv(cmd->name, AccessExclusiveLock);
   20441             : 
   20442         156 :     splitRelOid = RelationGetRelid(splitRel);
   20443             : 
   20444             :     /* Check descriptions of new partitions. */
   20445         588 :     foreach(listptr, cmd->partlist)
   20446             :     {
   20447             :         Oid         existing_relid;
   20448         438 :         SinglePartitionSpec *sps = (SinglePartitionSpec *) lfirst(listptr);
   20449             : 
   20450         438 :         strlcpy(relname, sps->name->relname, NAMEDATALEN);
   20451             : 
   20452             :         /*
   20453             :          * Look up the namespace in which we are supposed to create the
   20454             :          * partition, check we have permission to create there, lock it
   20455             :          * against concurrent drop, and mark stmt->relation as
   20456             :          * RELPERSISTENCE_TEMP if a temporary namespace is selected.
   20457             :          */
   20458         438 :         sps->name->relpersistence = rel->rd_rel->relpersistence;
   20459             :         namespaceId =
   20460         438 :             RangeVarGetAndCheckCreationNamespace(sps->name, NoLock, NULL);
   20461             : 
   20462             :         /*
   20463             :          * This would fail later on anyway if the relation already exists. But
   20464             :          * by catching it here we can emit a nicer error message.
   20465             :          */
   20466         438 :         existing_relid = get_relname_relid(relname, namespaceId);
   20467         438 :         if (existing_relid == splitRelOid && !isSameName)
   20468             :             /* One new partition can have the same name as split partition. */
   20469          30 :             isSameName = true;
   20470         408 :         else if (existing_relid != InvalidOid)
   20471           6 :             ereport(ERROR,
   20472             :                     (errcode(ERRCODE_DUPLICATE_TABLE),
   20473             :                      errmsg("relation \"%s\" already exists", relname)));
   20474             :     }
   20475             : 
   20476             :     /* Detach split partition. */
   20477         150 :     RemoveInheritance(splitRel, rel, false);
   20478             :     /* Do the final part of detaching. */
   20479         150 :     DetachPartitionFinalize(rel, splitRel, false, defaultPartOid);
   20480             : 
   20481             :     /*
   20482             :      * If new partition has the same name as split partition then we should
   20483             :      * rename split partition for reusing name.
   20484             :      */
   20485         150 :     if (isSameName)
   20486             :     {
   20487             :         /*
   20488             :          * We must bump the command counter to make the split partition tuple
   20489             :          * visible for renaming.
   20490             :          */
   20491          30 :         CommandCounterIncrement();
   20492             :         /* Rename partition. */
   20493          30 :         sprintf(tmpRelName, "split-%u-%X-tmp", RelationGetRelid(rel), MyProcPid);
   20494          30 :         RenameRelationInternal(splitRelOid, tmpRelName, false, false);
   20495             : 
   20496             :         /*
   20497             :          * We must bump the command counter to make the split partition tuple
   20498             :          * visible after renaming.
   20499             :          */
   20500          30 :         CommandCounterIncrement();
   20501             :     }
   20502             : 
   20503             :     /* Create new partitions (like split partition), without indexes. */
   20504         582 :     foreach(listptr, cmd->partlist)
   20505             :     {
   20506         432 :         SinglePartitionSpec *sps = (SinglePartitionSpec *) lfirst(listptr);
   20507             :         Relation    newPartRel;
   20508             : 
   20509         432 :         newPartRel = createPartitionTable(sps->name, rel, context);
   20510         432 :         newPartRels = lappend(newPartRels, newPartRel);
   20511             :     }
   20512             : 
   20513             :     /* Copy data from split partition to new partitions. */
   20514         150 :     moveSplitTableRows(rel, splitRel, cmd->partlist, newPartRels, defaultPartOid);
   20515             :     /* Keep the lock until commit. */
   20516         150 :     table_close(splitRel, NoLock);
   20517             : 
   20518             :     /* Attach new partitions to partitioned table. */
   20519         582 :     forboth(listptr, cmd->partlist, listptr2, newPartRels)
   20520             :     {
   20521         432 :         SinglePartitionSpec *sps = (SinglePartitionSpec *) lfirst(listptr);
   20522         432 :         Relation    newPartRel = (Relation) lfirst(listptr2);
   20523             : 
   20524             :         /*
   20525             :          * wqueue = NULL: verification for each cloned constraint is not
   20526             :          * needed.
   20527             :          */
   20528         432 :         attachPartitionTable(NULL, rel, newPartRel, sps->bound);
   20529             :         /* Keep the lock until commit. */
   20530         432 :         table_close(newPartRel, NoLock);
   20531             :     }
   20532             : 
   20533             :     /* Drop split partition. */
   20534         150 :     object.classId = RelationRelationId;
   20535         150 :     object.objectId = splitRelOid;
   20536         150 :     object.objectSubId = 0;
   20537             :     /* Probably DROP_CASCADE is not needed. */
   20538         150 :     performDeletion(&object, DROP_RESTRICT, 0);
   20539         150 : }
   20540             : 
   20541             : /*
   20542             :  * moveMergedTablesRows: scan partitions to be merged (mergingPartitionsList)
   20543             :  * of the partitioned table (rel) and move rows into the new partition
   20544             :  * (newPartRel).
   20545             :  */
   20546             : static void
   20547         102 : moveMergedTablesRows(Relation rel, List *mergingPartitionsList,
   20548             :                      Relation newPartRel)
   20549             : {
   20550             :     CommandId   mycid;
   20551             : 
   20552             :     /* The FSM is empty, so don't bother using it. */
   20553         102 :     int         ti_options = TABLE_INSERT_SKIP_FSM;
   20554             :     ListCell   *listptr;
   20555             :     BulkInsertState bistate;    /* state of bulk inserts for partition */
   20556             :     TupleTableSlot *dstslot;
   20557             : 
   20558         102 :     mycid = GetCurrentCommandId(true);
   20559             : 
   20560             :     /* Prepare a BulkInsertState for table_tuple_insert. */
   20561         102 :     bistate = GetBulkInsertState();
   20562             : 
   20563             :     /* Create necessary tuple slot. */
   20564         102 :     dstslot = MakeSingleTupleTableSlot(RelationGetDescr(newPartRel),
   20565             :                                        table_slot_callbacks(newPartRel));
   20566         102 :     ExecStoreAllNullTuple(dstslot);
   20567             : 
   20568         348 :     foreach(listptr, mergingPartitionsList)
   20569             :     {
   20570         246 :         Relation    mergingPartition = (Relation) lfirst(listptr);
   20571             :         TupleTableSlot *srcslot;
   20572             :         TupleConversionMap *tuple_map;
   20573             :         TableScanDesc scan;
   20574             :         Snapshot    snapshot;
   20575             : 
   20576             :         /* Create tuple slot for new partition. */
   20577         246 :         srcslot = MakeSingleTupleTableSlot(RelationGetDescr(mergingPartition),
   20578             :                                            table_slot_callbacks(mergingPartition));
   20579             : 
   20580             :         /*
   20581             :          * Map computing for moving attributes of merged partition to new
   20582             :          * partition.
   20583             :          */
   20584         246 :         tuple_map = convert_tuples_by_name(RelationGetDescr(mergingPartition),
   20585             :                                            RelationGetDescr(newPartRel));
   20586             : 
   20587             :         /* Scan through the rows. */
   20588         246 :         snapshot = RegisterSnapshot(GetLatestSnapshot());
   20589         246 :         scan = table_beginscan(mergingPartition, snapshot, 0, NULL);
   20590             : 
   20591         552 :         while (table_scan_getnextslot(scan, ForwardScanDirection, srcslot))
   20592             :         {
   20593             :             TupleTableSlot *insertslot;
   20594             : 
   20595             :             /* Extract data from old tuple. */
   20596         306 :             slot_getallattrs(srcslot);
   20597             : 
   20598         306 :             if (tuple_map)
   20599             :             {
   20600             :                 /* Need to use map to copy attributes. */
   20601          30 :                 insertslot = execute_attr_map_slot(tuple_map->attrMap, srcslot, dstslot);
   20602             :             }
   20603             :             else
   20604             :             {
   20605             :                 /* Copy attributes directly. */
   20606         276 :                 insertslot = dstslot;
   20607             : 
   20608         276 :                 ExecClearTuple(insertslot);
   20609             : 
   20610         276 :                 memcpy(insertslot->tts_values, srcslot->tts_values,
   20611         276 :                        sizeof(Datum) * srcslot->tts_nvalid);
   20612         276 :                 memcpy(insertslot->tts_isnull, srcslot->tts_isnull,
   20613         276 :                        sizeof(bool) * srcslot->tts_nvalid);
   20614             : 
   20615         276 :                 ExecStoreVirtualTuple(insertslot);
   20616             :             }
   20617             : 
   20618             :             /* Write the tuple out to the new relation. */
   20619         306 :             table_tuple_insert(newPartRel, insertslot, mycid,
   20620             :                                ti_options, bistate);
   20621             : 
   20622         306 :             CHECK_FOR_INTERRUPTS();
   20623             :         }
   20624             : 
   20625         246 :         table_endscan(scan);
   20626         246 :         UnregisterSnapshot(snapshot);
   20627             : 
   20628         246 :         if (tuple_map)
   20629          18 :             free_conversion_map(tuple_map);
   20630             : 
   20631         246 :         ExecDropSingleTupleTableSlot(srcslot);
   20632             :     }
   20633             : 
   20634         102 :     ExecDropSingleTupleTableSlot(dstslot);
   20635         102 :     FreeBulkInsertState(bistate);
   20636             : 
   20637         102 :     table_finish_bulk_insert(newPartRel, ti_options);
   20638         102 : }
   20639             : 
   20640             : /*
   20641             :  * ALTER TABLE <name> MERGE PARTITIONS <partition-list> INTO <partition-name>
   20642             :  */
   20643             : static void
   20644         114 : ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel,
   20645             :                       PartitionCmd *cmd, AlterTableUtilityContext *context)
   20646             : {
   20647             :     Relation    newPartRel;
   20648             :     ListCell   *listptr;
   20649         114 :     List       *mergingPartitionsList = NIL;
   20650             :     Oid         defaultPartOid;
   20651             :     Oid         namespaceId;
   20652             :     Oid         existingRelid;
   20653             : 
   20654             :     /*
   20655             :      * Lock all merged partitions, check them and create list with partitions
   20656             :      * contexts.
   20657             :      */
   20658         384 :     foreach(listptr, cmd->partlist)
   20659             :     {
   20660         270 :         RangeVar   *name = (RangeVar *) lfirst(listptr);
   20661             :         Relation    mergingPartition;
   20662             : 
   20663             :         /*
   20664             :          * We are going to detach and remove this partition: need to use
   20665             :          * exclusive lock for preventing DML-queries to the partition.
   20666             :          */
   20667         270 :         mergingPartition = table_openrv(name, AccessExclusiveLock);
   20668             : 
   20669             :         /* Store a next merging partition into the list. */
   20670         270 :         mergingPartitionsList = lappend(mergingPartitionsList,
   20671             :                                         mergingPartition);
   20672             :     }
   20673             : 
   20674             :     /*
   20675             :      * Look up the namespace in which we are supposed to create the partition,
   20676             :      * check we have permission to create there, lock it against concurrent
   20677             :      * drop, and mark stmt->relation as RELPERSISTENCE_TEMP if a temporary
   20678             :      * namespace is selected.
   20679             :      */
   20680         114 :     cmd->name->relpersistence = rel->rd_rel->relpersistence;
   20681             :     namespaceId =
   20682         114 :         RangeVarGetAndCheckCreationNamespace(cmd->name, NoLock, NULL);
   20683             : 
   20684             :     /*
   20685             :      * Check if this name is already taken.  This helps us to detect the
   20686             :      * situation when one of the merging partitions has the same name as the
   20687             :      * new partition.  Otherwise, this would fail later on anyway but catching
   20688             :      * this here allows us to emit a nicer error message.
   20689             :      */
   20690         114 :     existingRelid = get_relname_relid(cmd->name->relname, namespaceId);
   20691             : 
   20692         114 :     if (OidIsValid(existingRelid))
   20693             :     {
   20694          12 :         Relation    sameNamePartition = NULL;
   20695             : 
   20696          36 :         foreach_ptr(RelationData, mergingPartition, mergingPartitionsList)
   20697             :         {
   20698          24 :             if (RelationGetRelid(mergingPartition) == existingRelid)
   20699             :             {
   20700          12 :                 sameNamePartition = mergingPartition;
   20701          12 :                 break;
   20702             :             }
   20703             :         }
   20704             : 
   20705          12 :         if (sameNamePartition)
   20706             :         {
   20707             :             /*
   20708             :              * The new partition has the same name as one of merging
   20709             :              * partitions.
   20710             :              */
   20711             :             char        tmpRelName[NAMEDATALEN];
   20712             : 
   20713             :             /* Generate temporary name. */
   20714          12 :             sprintf(tmpRelName, "merge-%u-%X-tmp", RelationGetRelid(rel), MyProcPid);
   20715             : 
   20716             :             /*
   20717             :              * Rename the existing partition with a temporary name, leaving it
   20718             :              * free for the new partition.  We don't need to care about this
   20719             :              * in the future because we're going to eventually drop the
   20720             :              * existing partition anyway.
   20721             :              */
   20722          12 :             RenameRelationInternal(RelationGetRelid(sameNamePartition),
   20723             :                                    tmpRelName, false, false);
   20724             : 
   20725             :             /*
   20726             :              * We must bump the command counter to make the new partition
   20727             :              * tuple visible for rename.
   20728             :              */
   20729          12 :             CommandCounterIncrement();
   20730             :         }
   20731             :         else
   20732             :         {
   20733           0 :             ereport(ERROR,
   20734             :                     (errcode(ERRCODE_DUPLICATE_TABLE),
   20735             :                      errmsg("relation \"%s\" already exists", cmd->name->relname)));
   20736             :         }
   20737             :     }
   20738             : 
   20739             :     /* Detach all merged partitions. */
   20740             :     defaultPartOid =
   20741         114 :         get_default_oid_from_partdesc(RelationGetPartitionDesc(rel, true));
   20742         384 :     foreach(listptr, mergingPartitionsList)
   20743             :     {
   20744         270 :         Relation    mergingPartition = (Relation) lfirst(listptr);
   20745             : 
   20746             :         /* Remove the pg_inherits row first. */
   20747         270 :         RemoveInheritance(mergingPartition, rel, false);
   20748             :         /* Do the final part of detaching. */
   20749         270 :         DetachPartitionFinalize(rel, mergingPartition, false, defaultPartOid);
   20750             :     }
   20751             : 
   20752             :     /* Create table for new partition, use partitioned table as model. */
   20753         114 :     newPartRel = createPartitionTable(cmd->name, rel, context);
   20754             : 
   20755             :     /* Copy data from merged partitions to new partition. */
   20756         102 :     moveMergedTablesRows(rel, mergingPartitionsList, newPartRel);
   20757             : 
   20758             :     /* Drop the current partitions before attaching the new one. */
   20759         348 :     foreach(listptr, mergingPartitionsList)
   20760             :     {
   20761             :         ObjectAddress object;
   20762         246 :         Relation    mergingPartition = (Relation) lfirst(listptr);
   20763             : 
   20764             :         /* Get relation id before table_close() call. */
   20765         246 :         object.objectId = RelationGetRelid(mergingPartition);
   20766         246 :         object.classId = RelationRelationId;
   20767         246 :         object.objectSubId = 0;
   20768             : 
   20769             :         /* Keep the lock until commit. */
   20770         246 :         table_close(mergingPartition, NoLock);
   20771             : 
   20772         246 :         performDeletion(&object, DROP_RESTRICT, 0);
   20773             :     }
   20774         102 :     list_free(mergingPartitionsList);
   20775             : 
   20776             :     /*
   20777             :      * Attach a new partition to the partitioned table. wqueue = NULL:
   20778             :      * verification for each cloned constraint is not needed.
   20779             :      */
   20780         102 :     attachPartitionTable(NULL, rel, newPartRel, cmd->bound);
   20781             : 
   20782             :     /* Keep the lock until commit. */
   20783         102 :     table_close(newPartRel, NoLock);
   20784         102 : }

Generated by: LCOV version 1.14