LCOV - code coverage report
Current view: top level - src/backend/commands - tablecmds.c (source / functions) Hit Total Coverage
Test: PostgreSQL 18devel Lines: 5962 6508 91.6 %
Date: 2025-01-18 04:15:08 Functions: 197 198 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-2025, 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/gist.h"
      20             : #include "access/heapam.h"
      21             : #include "access/heapam_xlog.h"
      22             : #include "access/multixact.h"
      23             : #include "access/reloptions.h"
      24             : #include "access/relscan.h"
      25             : #include "access/sysattr.h"
      26             : #include "access/tableam.h"
      27             : #include "access/toast_compression.h"
      28             : #include "access/xact.h"
      29             : #include "access/xlog.h"
      30             : #include "access/xloginsert.h"
      31             : #include "catalog/catalog.h"
      32             : #include "catalog/heap.h"
      33             : #include "catalog/index.h"
      34             : #include "catalog/namespace.h"
      35             : #include "catalog/objectaccess.h"
      36             : #include "catalog/partition.h"
      37             : #include "catalog/pg_am.h"
      38             : #include "catalog/pg_attrdef.h"
      39             : #include "catalog/pg_collation.h"
      40             : #include "catalog/pg_constraint.h"
      41             : #include "catalog/pg_depend.h"
      42             : #include "catalog/pg_foreign_table.h"
      43             : #include "catalog/pg_inherits.h"
      44             : #include "catalog/pg_largeobject.h"
      45             : #include "catalog/pg_namespace.h"
      46             : #include "catalog/pg_opclass.h"
      47             : #include "catalog/pg_policy.h"
      48             : #include "catalog/pg_proc.h"
      49             : #include "catalog/pg_publication_rel.h"
      50             : #include "catalog/pg_rewrite.h"
      51             : #include "catalog/pg_statistic_ext.h"
      52             : #include "catalog/pg_tablespace.h"
      53             : #include "catalog/pg_trigger.h"
      54             : #include "catalog/pg_type.h"
      55             : #include "catalog/storage.h"
      56             : #include "catalog/storage_xlog.h"
      57             : #include "catalog/toasting.h"
      58             : #include "commands/cluster.h"
      59             : #include "commands/comment.h"
      60             : #include "commands/defrem.h"
      61             : #include "commands/event_trigger.h"
      62             : #include "commands/sequence.h"
      63             : #include "commands/tablecmds.h"
      64             : #include "commands/tablespace.h"
      65             : #include "commands/trigger.h"
      66             : #include "commands/typecmds.h"
      67             : #include "commands/user.h"
      68             : #include "commands/vacuum.h"
      69             : #include "common/int.h"
      70             : #include "executor/executor.h"
      71             : #include "foreign/fdwapi.h"
      72             : #include "foreign/foreign.h"
      73             : #include "miscadmin.h"
      74             : #include "nodes/makefuncs.h"
      75             : #include "nodes/nodeFuncs.h"
      76             : #include "nodes/parsenodes.h"
      77             : #include "optimizer/optimizer.h"
      78             : #include "parser/parse_coerce.h"
      79             : #include "parser/parse_collate.h"
      80             : #include "parser/parse_expr.h"
      81             : #include "parser/parse_relation.h"
      82             : #include "parser/parse_type.h"
      83             : #include "parser/parse_utilcmd.h"
      84             : #include "parser/parser.h"
      85             : #include "partitioning/partbounds.h"
      86             : #include "partitioning/partdesc.h"
      87             : #include "pgstat.h"
      88             : #include "rewrite/rewriteDefine.h"
      89             : #include "rewrite/rewriteHandler.h"
      90             : #include "rewrite/rewriteManip.h"
      91             : #include "storage/bufmgr.h"
      92             : #include "storage/lmgr.h"
      93             : #include "storage/lock.h"
      94             : #include "storage/predicate.h"
      95             : #include "storage/smgr.h"
      96             : #include "tcop/utility.h"
      97             : #include "utils/acl.h"
      98             : #include "utils/builtins.h"
      99             : #include "utils/fmgroids.h"
     100             : #include "utils/inval.h"
     101             : #include "utils/lsyscache.h"
     102             : #include "utils/memutils.h"
     103             : #include "utils/partcache.h"
     104             : #include "utils/relcache.h"
     105             : #include "utils/ruleutils.h"
     106             : #include "utils/snapmgr.h"
     107             : #include "utils/syscache.h"
     108             : #include "utils/timestamp.h"
     109             : #include "utils/typcache.h"
     110             : #include "utils/usercontext.h"
     111             : 
     112             : /*
     113             :  * ON COMMIT action list
     114             :  */
     115             : typedef struct OnCommitItem
     116             : {
     117             :     Oid         relid;          /* relid of relation */
     118             :     OnCommitAction oncommit;    /* what to do at end of xact */
     119             : 
     120             :     /*
     121             :      * If this entry was created during the current transaction,
     122             :      * creating_subid is the ID of the creating subxact; if created in a prior
     123             :      * transaction, creating_subid is zero.  If deleted during the current
     124             :      * transaction, deleting_subid is the ID of the deleting subxact; if no
     125             :      * deletion request is pending, deleting_subid is zero.
     126             :      */
     127             :     SubTransactionId creating_subid;
     128             :     SubTransactionId deleting_subid;
     129             : } OnCommitItem;
     130             : 
     131             : static List *on_commits = NIL;
     132             : 
     133             : 
     134             : /*
     135             :  * State information for ALTER TABLE
     136             :  *
     137             :  * The pending-work queue for an ALTER TABLE is a List of AlteredTableInfo
     138             :  * structs, one for each table modified by the operation (the named table
     139             :  * plus any child tables that are affected).  We save lists of subcommands
     140             :  * to apply to this table (possibly modified by parse transformation steps);
     141             :  * these lists will be executed in Phase 2.  If a Phase 3 step is needed,
     142             :  * necessary information is stored in the constraints and newvals lists.
     143             :  *
     144             :  * Phase 2 is divided into multiple passes; subcommands are executed in
     145             :  * a pass determined by subcommand type.
     146             :  */
     147             : 
     148             : typedef enum AlterTablePass
     149             : {
     150             :     AT_PASS_UNSET = -1,         /* UNSET will cause ERROR */
     151             :     AT_PASS_DROP,               /* DROP (all flavors) */
     152             :     AT_PASS_ALTER_TYPE,         /* ALTER COLUMN TYPE */
     153             :     AT_PASS_ADD_COL,            /* ADD COLUMN */
     154             :     AT_PASS_SET_EXPRESSION,     /* ALTER SET EXPRESSION */
     155             :     AT_PASS_OLD_INDEX,          /* re-add existing indexes */
     156             :     AT_PASS_OLD_CONSTR,         /* re-add existing constraints */
     157             :     /* We could support a RENAME COLUMN pass here, but not currently used */
     158             :     AT_PASS_ADD_CONSTR,         /* ADD constraints (initial examination) */
     159             :     AT_PASS_COL_ATTRS,          /* set column attributes, eg NOT NULL */
     160             :     AT_PASS_ADD_INDEXCONSTR,    /* ADD index-based constraints */
     161             :     AT_PASS_ADD_INDEX,          /* ADD indexes */
     162             :     AT_PASS_ADD_OTHERCONSTR,    /* ADD other constraints, defaults */
     163             :     AT_PASS_MISC,               /* other stuff */
     164             : } AlterTablePass;
     165             : 
     166             : #define AT_NUM_PASSES           (AT_PASS_MISC + 1)
     167             : 
     168             : typedef struct AlteredTableInfo
     169             : {
     170             :     /* Information saved before any work commences: */
     171             :     Oid         relid;          /* Relation to work on */
     172             :     char        relkind;        /* Its relkind */
     173             :     TupleDesc   oldDesc;        /* Pre-modification tuple descriptor */
     174             : 
     175             :     /*
     176             :      * Transiently set during Phase 2, normally set to NULL.
     177             :      *
     178             :      * ATRewriteCatalogs sets this when it starts, and closes when ATExecCmd
     179             :      * returns control.  This can be exploited by ATExecCmd subroutines to
     180             :      * close/reopen across transaction boundaries.
     181             :      */
     182             :     Relation    rel;
     183             : 
     184             :     /* Information saved by Phase 1 for Phase 2: */
     185             :     List       *subcmds[AT_NUM_PASSES]; /* Lists of AlterTableCmd */
     186             :     /* Information saved by Phases 1/2 for Phase 3: */
     187             :     List       *constraints;    /* List of NewConstraint */
     188             :     List       *newvals;        /* List of NewColumnValue */
     189             :     List       *afterStmts;     /* List of utility command parsetrees */
     190             :     bool        verify_new_notnull; /* T if we should recheck NOT NULL */
     191             :     int         rewrite;        /* Reason for forced rewrite, if any */
     192             :     bool        chgAccessMethod;    /* T if SET ACCESS METHOD is used */
     193             :     Oid         newAccessMethod;    /* new access method; 0 means no change,
     194             :                                      * if above is true */
     195             :     Oid         newTableSpace;  /* new tablespace; 0 means no change */
     196             :     bool        chgPersistence; /* T if SET LOGGED/UNLOGGED is used */
     197             :     char        newrelpersistence;  /* if above is true */
     198             :     Expr       *partition_constraint;   /* for attach partition validation */
     199             :     /* true, if validating default due to some other attach/detach */
     200             :     bool        validate_default;
     201             :     /* Objects to rebuild after completing ALTER TYPE operations */
     202             :     List       *changedConstraintOids;  /* OIDs of constraints to rebuild */
     203             :     List       *changedConstraintDefs;  /* string definitions of same */
     204             :     List       *changedIndexOids;   /* OIDs of indexes to rebuild */
     205             :     List       *changedIndexDefs;   /* string definitions of same */
     206             :     char       *replicaIdentityIndex;   /* index to reset as REPLICA IDENTITY */
     207             :     char       *clusterOnIndex; /* index to use for CLUSTER */
     208             :     List       *changedStatisticsOids;  /* OIDs of statistics to rebuild */
     209             :     List       *changedStatisticsDefs;  /* string definitions of same */
     210             : } AlteredTableInfo;
     211             : 
     212             : /* Struct describing one new constraint to check in Phase 3 scan */
     213             : /* Note: new not-null constraints are handled elsewhere */
     214             : typedef struct NewConstraint
     215             : {
     216             :     char       *name;           /* Constraint name, or NULL if none */
     217             :     ConstrType  contype;        /* CHECK or FOREIGN */
     218             :     Oid         refrelid;       /* PK rel, if FOREIGN */
     219             :     Oid         refindid;       /* OID of PK's index, if FOREIGN */
     220             :     bool        conwithperiod;  /* Whether the new FOREIGN KEY uses PERIOD */
     221             :     Oid         conid;          /* OID of pg_constraint entry, if FOREIGN */
     222             :     Node       *qual;           /* Check expr or CONSTR_FOREIGN Constraint */
     223             :     ExprState  *qualstate;      /* Execution state for CHECK expr */
     224             : } NewConstraint;
     225             : 
     226             : /*
     227             :  * Struct describing one new column value that needs to be computed during
     228             :  * Phase 3 copy (this could be either a new column with a non-null default, or
     229             :  * a column that we're changing the type of).  Columns without such an entry
     230             :  * are just copied from the old table during ATRewriteTable.  Note that the
     231             :  * expr is an expression over *old* table values, except when is_generated
     232             :  * is true; then it is an expression over columns of the *new* tuple.
     233             :  */
     234             : typedef struct NewColumnValue
     235             : {
     236             :     AttrNumber  attnum;         /* which column */
     237             :     Expr       *expr;           /* expression to compute */
     238             :     ExprState  *exprstate;      /* execution state */
     239             :     bool        is_generated;   /* is it a GENERATED expression? */
     240             : } NewColumnValue;
     241             : 
     242             : /*
     243             :  * Error-reporting support for RemoveRelations
     244             :  */
     245             : struct dropmsgstrings
     246             : {
     247             :     char        kind;
     248             :     int         nonexistent_code;
     249             :     const char *nonexistent_msg;
     250             :     const char *skipping_msg;
     251             :     const char *nota_msg;
     252             :     const char *drophint_msg;
     253             : };
     254             : 
     255             : static const struct dropmsgstrings dropmsgstringarray[] = {
     256             :     {RELKIND_RELATION,
     257             :         ERRCODE_UNDEFINED_TABLE,
     258             :         gettext_noop("table \"%s\" does not exist"),
     259             :         gettext_noop("table \"%s\" does not exist, skipping"),
     260             :         gettext_noop("\"%s\" is not a table"),
     261             :     gettext_noop("Use DROP TABLE to remove a table.")},
     262             :     {RELKIND_SEQUENCE,
     263             :         ERRCODE_UNDEFINED_TABLE,
     264             :         gettext_noop("sequence \"%s\" does not exist"),
     265             :         gettext_noop("sequence \"%s\" does not exist, skipping"),
     266             :         gettext_noop("\"%s\" is not a sequence"),
     267             :     gettext_noop("Use DROP SEQUENCE to remove a sequence.")},
     268             :     {RELKIND_VIEW,
     269             :         ERRCODE_UNDEFINED_TABLE,
     270             :         gettext_noop("view \"%s\" does not exist"),
     271             :         gettext_noop("view \"%s\" does not exist, skipping"),
     272             :         gettext_noop("\"%s\" is not a view"),
     273             :     gettext_noop("Use DROP VIEW to remove a view.")},
     274             :     {RELKIND_MATVIEW,
     275             :         ERRCODE_UNDEFINED_TABLE,
     276             :         gettext_noop("materialized view \"%s\" does not exist"),
     277             :         gettext_noop("materialized view \"%s\" does not exist, skipping"),
     278             :         gettext_noop("\"%s\" is not a materialized view"),
     279             :     gettext_noop("Use DROP MATERIALIZED VIEW to remove a materialized view.")},
     280             :     {RELKIND_INDEX,
     281             :         ERRCODE_UNDEFINED_OBJECT,
     282             :         gettext_noop("index \"%s\" does not exist"),
     283             :         gettext_noop("index \"%s\" does not exist, skipping"),
     284             :         gettext_noop("\"%s\" is not an index"),
     285             :     gettext_noop("Use DROP INDEX to remove an index.")},
     286             :     {RELKIND_COMPOSITE_TYPE,
     287             :         ERRCODE_UNDEFINED_OBJECT,
     288             :         gettext_noop("type \"%s\" does not exist"),
     289             :         gettext_noop("type \"%s\" does not exist, skipping"),
     290             :         gettext_noop("\"%s\" is not a type"),
     291             :     gettext_noop("Use DROP TYPE to remove a type.")},
     292             :     {RELKIND_FOREIGN_TABLE,
     293             :         ERRCODE_UNDEFINED_OBJECT,
     294             :         gettext_noop("foreign table \"%s\" does not exist"),
     295             :         gettext_noop("foreign table \"%s\" does not exist, skipping"),
     296             :         gettext_noop("\"%s\" is not a foreign table"),
     297             :     gettext_noop("Use DROP FOREIGN TABLE to remove a foreign table.")},
     298             :     {RELKIND_PARTITIONED_TABLE,
     299             :         ERRCODE_UNDEFINED_TABLE,
     300             :         gettext_noop("table \"%s\" does not exist"),
     301             :         gettext_noop("table \"%s\" does not exist, skipping"),
     302             :         gettext_noop("\"%s\" is not a table"),
     303             :     gettext_noop("Use DROP TABLE to remove a table.")},
     304             :     {RELKIND_PARTITIONED_INDEX,
     305             :         ERRCODE_UNDEFINED_OBJECT,
     306             :         gettext_noop("index \"%s\" does not exist"),
     307             :         gettext_noop("index \"%s\" does not exist, skipping"),
     308             :         gettext_noop("\"%s\" is not an index"),
     309             :     gettext_noop("Use DROP INDEX to remove an index.")},
     310             :     {'\0', 0, NULL, NULL, NULL, NULL}
     311             : };
     312             : 
     313             : /* communication between RemoveRelations and RangeVarCallbackForDropRelation */
     314             : struct DropRelationCallbackState
     315             : {
     316             :     /* These fields are set by RemoveRelations: */
     317             :     char        expected_relkind;
     318             :     LOCKMODE    heap_lockmode;
     319             :     /* These fields are state to track which subsidiary locks are held: */
     320             :     Oid         heapOid;
     321             :     Oid         partParentOid;
     322             :     /* These fields are passed back by RangeVarCallbackForDropRelation: */
     323             :     char        actual_relkind;
     324             :     char        actual_relpersistence;
     325             : };
     326             : 
     327             : /* Alter table target-type flags for ATSimplePermissions */
     328             : #define     ATT_TABLE               0x0001
     329             : #define     ATT_VIEW                0x0002
     330             : #define     ATT_MATVIEW             0x0004
     331             : #define     ATT_INDEX               0x0008
     332             : #define     ATT_COMPOSITE_TYPE      0x0010
     333             : #define     ATT_FOREIGN_TABLE       0x0020
     334             : #define     ATT_PARTITIONED_INDEX   0x0040
     335             : #define     ATT_SEQUENCE            0x0080
     336             : #define     ATT_PARTITIONED_TABLE   0x0100
     337             : 
     338             : /*
     339             :  * ForeignTruncateInfo
     340             :  *
     341             :  * Information related to truncation of foreign tables.  This is used for
     342             :  * the elements in a hash table. It uses the server OID as lookup key,
     343             :  * and includes a per-server list of all foreign tables involved in the
     344             :  * truncation.
     345             :  */
     346             : typedef struct ForeignTruncateInfo
     347             : {
     348             :     Oid         serverid;
     349             :     List       *rels;
     350             : } ForeignTruncateInfo;
     351             : 
     352             : /* Partial or complete FK creation in addFkConstraint() */
     353             : typedef enum addFkConstraintSides
     354             : {
     355             :     addFkReferencedSide,
     356             :     addFkReferencingSide,
     357             :     addFkBothSides,
     358             : } addFkConstraintSides;
     359             : 
     360             : /*
     361             :  * Partition tables are expected to be dropped when the parent partitioned
     362             :  * table gets dropped. Hence for partitioning we use AUTO dependency.
     363             :  * Otherwise, for regular inheritance use NORMAL dependency.
     364             :  */
     365             : #define child_dependency_type(child_is_partition)   \
     366             :     ((child_is_partition) ? DEPENDENCY_AUTO : DEPENDENCY_NORMAL)
     367             : 
     368             : static void truncate_check_rel(Oid relid, Form_pg_class reltuple);
     369             : static void truncate_check_perms(Oid relid, Form_pg_class reltuple);
     370             : static void truncate_check_activity(Relation rel);
     371             : static void RangeVarCallbackForTruncate(const RangeVar *relation,
     372             :                                         Oid relId, Oid oldRelId, void *arg);
     373             : static List *MergeAttributes(List *columns, const List *supers, char relpersistence,
     374             :                              bool is_partition, List **supconstr,
     375             :                              List **supnotnulls);
     376             : static List *MergeCheckConstraint(List *constraints, const char *name, Node *expr, bool is_enforced);
     377             : static void MergeChildAttribute(List *inh_columns, int exist_attno, int newcol_attno, const ColumnDef *newdef);
     378             : static ColumnDef *MergeInheritedAttribute(List *inh_columns, int exist_attno, const ColumnDef *newdef);
     379             : static void MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel, bool ispartition);
     380             : static void MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel);
     381             : static void StoreCatalogInheritance(Oid relationId, List *supers,
     382             :                                     bool child_is_partition);
     383             : static void StoreCatalogInheritance1(Oid relationId, Oid parentOid,
     384             :                                      int32 seqNumber, Relation inhRelation,
     385             :                                      bool child_is_partition);
     386             : static int  findAttrByName(const char *attributeName, const List *columns);
     387             : static void AlterIndexNamespaces(Relation classRel, Relation rel,
     388             :                                  Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved);
     389             : static void AlterSeqNamespaces(Relation classRel, Relation rel,
     390             :                                Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved,
     391             :                                LOCKMODE lockmode);
     392             : static ObjectAddress ATExecAlterConstraint(Relation rel, AlterTableCmd *cmd,
     393             :                                            bool recurse, bool recursing, LOCKMODE lockmode);
     394             : static bool ATExecAlterConstrRecurse(Constraint *cmdcon, Relation conrel, Relation tgrel,
     395             :                                      Relation rel, HeapTuple contuple, List **otherrelids,
     396             :                                      LOCKMODE lockmode);
     397             : static void AlterConstrTriggerDeferrability(Oid conoid, Relation tgrel, Relation rel,
     398             :                                             bool deferrable, bool initdeferred,
     399             :                                             List **otherrelids);
     400             : static void ATExecAlterChildConstr(Constraint *cmdcon, Relation conrel, Relation tgrel,
     401             :                                    Relation rel, HeapTuple contuple, List **otherrelids,
     402             :                                    LOCKMODE lockmode);
     403             : static ObjectAddress ATExecValidateConstraint(List **wqueue,
     404             :                                               Relation rel, char *constrName,
     405             :                                               bool recurse, bool recursing, LOCKMODE lockmode);
     406             : static void QueueFKConstraintValidation(List **wqueue, Relation conrel, Relation rel,
     407             :                                         HeapTuple contuple, LOCKMODE lockmode);
     408             : static void QueueCheckConstraintValidation(List **wqueue, Relation conrel, Relation rel,
     409             :                                            char *constrName, HeapTuple contuple,
     410             :                                            bool recurse, bool recursing, LOCKMODE lockmode);
     411             : static int  transformColumnNameList(Oid relId, List *colList,
     412             :                                     int16 *attnums, Oid *atttypids, Oid *attcollids);
     413             : static int  transformFkeyGetPrimaryKey(Relation pkrel, Oid *indexOid,
     414             :                                        List **attnamelist,
     415             :                                        int16 *attnums, Oid *atttypids, Oid *attcollids,
     416             :                                        Oid *opclasses, bool *pk_has_without_overlaps);
     417             : static Oid  transformFkeyCheckAttrs(Relation pkrel,
     418             :                                     int numattrs, int16 *attnums,
     419             :                                     bool with_period, Oid *opclasses,
     420             :                                     bool *pk_has_without_overlaps);
     421             : static void checkFkeyPermissions(Relation rel, int16 *attnums, int natts);
     422             : static CoercionPathType findFkeyCast(Oid targetTypeId, Oid sourceTypeId,
     423             :                                      Oid *funcid);
     424             : static void validateForeignKeyConstraint(char *conname,
     425             :                                          Relation rel, Relation pkrel,
     426             :                                          Oid pkindOid, Oid constraintOid, bool hasperiod);
     427             : static void CheckAlterTableIsSafe(Relation rel);
     428             : static void ATController(AlterTableStmt *parsetree,
     429             :                          Relation rel, List *cmds, bool recurse, LOCKMODE lockmode,
     430             :                          AlterTableUtilityContext *context);
     431             : static void ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
     432             :                       bool recurse, bool recursing, LOCKMODE lockmode,
     433             :                       AlterTableUtilityContext *context);
     434             : static void ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode,
     435             :                               AlterTableUtilityContext *context);
     436             : static void ATExecCmd(List **wqueue, AlteredTableInfo *tab,
     437             :                       AlterTableCmd *cmd, LOCKMODE lockmode, AlterTablePass cur_pass,
     438             :                       AlterTableUtilityContext *context);
     439             : static AlterTableCmd *ATParseTransformCmd(List **wqueue, AlteredTableInfo *tab,
     440             :                                           Relation rel, AlterTableCmd *cmd,
     441             :                                           bool recurse, LOCKMODE lockmode,
     442             :                                           AlterTablePass cur_pass,
     443             :                                           AlterTableUtilityContext *context);
     444             : static void ATRewriteTables(AlterTableStmt *parsetree,
     445             :                             List **wqueue, LOCKMODE lockmode,
     446             :                             AlterTableUtilityContext *context);
     447             : static void ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap);
     448             : static AlteredTableInfo *ATGetQueueEntry(List **wqueue, Relation rel);
     449             : static void ATSimplePermissions(AlterTableType cmdtype, Relation rel, int allowed_targets);
     450             : static void ATSimpleRecursion(List **wqueue, Relation rel,
     451             :                               AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode,
     452             :                               AlterTableUtilityContext *context);
     453             : static void ATCheckPartitionsNotInUse(Relation rel, LOCKMODE lockmode);
     454             : static void ATTypedTableRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd,
     455             :                                   LOCKMODE lockmode,
     456             :                                   AlterTableUtilityContext *context);
     457             : static List *find_typed_table_dependencies(Oid typeOid, const char *typeName,
     458             :                                            DropBehavior behavior);
     459             : static void ATPrepAddColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
     460             :                             bool is_view, AlterTableCmd *cmd, LOCKMODE lockmode,
     461             :                             AlterTableUtilityContext *context);
     462             : static ObjectAddress ATExecAddColumn(List **wqueue, AlteredTableInfo *tab,
     463             :                                      Relation rel, AlterTableCmd **cmd,
     464             :                                      bool recurse, bool recursing,
     465             :                                      LOCKMODE lockmode, AlterTablePass cur_pass,
     466             :                                      AlterTableUtilityContext *context);
     467             : static bool check_for_column_name_collision(Relation rel, const char *colname,
     468             :                                             bool if_not_exists);
     469             : static void add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid);
     470             : static void add_column_collation_dependency(Oid relid, int32 attnum, Oid collid);
     471             : static ObjectAddress ATExecDropNotNull(Relation rel, const char *colName, bool recurse,
     472             :                                        LOCKMODE lockmode);
     473             : static void set_attnotnull(List **wqueue, Relation rel, AttrNumber attnum,
     474             :                            LOCKMODE lockmode);
     475             : static ObjectAddress ATExecSetNotNull(List **wqueue, Relation rel,
     476             :                                       char *constrname, char *colName,
     477             :                                       bool recurse, bool recursing,
     478             :                                       LOCKMODE lockmode);
     479             : static bool NotNullImpliedByRelConstraints(Relation rel, Form_pg_attribute attr);
     480             : static bool ConstraintImpliedByRelConstraint(Relation scanrel,
     481             :                                              List *testConstraint, List *provenConstraint);
     482             : static ObjectAddress ATExecColumnDefault(Relation rel, const char *colName,
     483             :                                          Node *newDefault, LOCKMODE lockmode);
     484             : static ObjectAddress ATExecCookedColumnDefault(Relation rel, AttrNumber attnum,
     485             :                                                Node *newDefault);
     486             : static ObjectAddress ATExecAddIdentity(Relation rel, const char *colName,
     487             :                                        Node *def, LOCKMODE lockmode, bool recurse, bool recursing);
     488             : static ObjectAddress ATExecSetIdentity(Relation rel, const char *colName,
     489             :                                        Node *def, LOCKMODE lockmode, bool recurse, bool recursing);
     490             : static ObjectAddress ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode,
     491             :                                         bool recurse, bool recursing);
     492             : static ObjectAddress ATExecSetExpression(AlteredTableInfo *tab, Relation rel, const char *colName,
     493             :                                          Node *newExpr, LOCKMODE lockmode);
     494             : static void ATPrepDropExpression(Relation rel, AlterTableCmd *cmd, bool recurse, bool recursing, LOCKMODE lockmode);
     495             : static ObjectAddress ATExecDropExpression(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode);
     496             : static ObjectAddress ATExecSetStatistics(Relation rel, const char *colName, int16 colNum,
     497             :                                          Node *newValue, LOCKMODE lockmode);
     498             : static ObjectAddress ATExecSetOptions(Relation rel, const char *colName,
     499             :                                       Node *options, bool isReset, LOCKMODE lockmode);
     500             : static ObjectAddress ATExecSetStorage(Relation rel, const char *colName,
     501             :                                       Node *newValue, LOCKMODE lockmode);
     502             : static void ATPrepDropColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
     503             :                              AlterTableCmd *cmd, LOCKMODE lockmode,
     504             :                              AlterTableUtilityContext *context);
     505             : static ObjectAddress ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
     506             :                                       DropBehavior behavior,
     507             :                                       bool recurse, bool recursing,
     508             :                                       bool missing_ok, LOCKMODE lockmode,
     509             :                                       ObjectAddresses *addrs);
     510             : static void ATPrepAddPrimaryKey(List **wqueue, Relation rel, AlterTableCmd *cmd,
     511             :                                 bool recurse, LOCKMODE lockmode,
     512             :                                 AlterTableUtilityContext *context);
     513             : static ObjectAddress ATExecAddIndex(AlteredTableInfo *tab, Relation rel,
     514             :                                     IndexStmt *stmt, bool is_rebuild, LOCKMODE lockmode);
     515             : static ObjectAddress ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
     516             :                                          CreateStatsStmt *stmt, bool is_rebuild, LOCKMODE lockmode);
     517             : static ObjectAddress ATExecAddConstraint(List **wqueue,
     518             :                                          AlteredTableInfo *tab, Relation rel,
     519             :                                          Constraint *newConstraint, bool recurse, bool is_readd,
     520             :                                          LOCKMODE lockmode);
     521             : static char *ChooseForeignKeyConstraintNameAddition(List *colnames);
     522             : static ObjectAddress ATExecAddIndexConstraint(AlteredTableInfo *tab, Relation rel,
     523             :                                               IndexStmt *stmt, LOCKMODE lockmode);
     524             : static ObjectAddress ATAddCheckNNConstraint(List **wqueue,
     525             :                                             AlteredTableInfo *tab, Relation rel,
     526             :                                             Constraint *constr,
     527             :                                             bool recurse, bool recursing, bool is_readd,
     528             :                                             LOCKMODE lockmode);
     529             : static ObjectAddress ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab,
     530             :                                                Relation rel, Constraint *fkconstraint,
     531             :                                                bool recurse, bool recursing,
     532             :                                                LOCKMODE lockmode);
     533             : static void validateFkOnDeleteSetColumns(int numfks, const int16 *fkattnums,
     534             :                                          int numfksetcols, const int16 *fksetcolsattnums,
     535             :                                          List *fksetcols);
     536             : static ObjectAddress addFkConstraint(addFkConstraintSides fkside,
     537             :                                      char *constraintname,
     538             :                                      Constraint *fkconstraint, Relation rel,
     539             :                                      Relation pkrel, Oid indexOid,
     540             :                                      Oid parentConstr,
     541             :                                      int numfks, int16 *pkattnum, int16 *fkattnum,
     542             :                                      Oid *pfeqoperators, Oid *ppeqoperators,
     543             :                                      Oid *ffeqoperators, int numfkdelsetcols,
     544             :                                      int16 *fkdelsetcols, bool is_internal,
     545             :                                      bool with_period);
     546             : static void addFkRecurseReferenced(Constraint *fkconstraint,
     547             :                                    Relation rel, Relation pkrel, Oid indexOid, Oid parentConstr,
     548             :                                    int numfks, int16 *pkattnum, int16 *fkattnum,
     549             :                                    Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators,
     550             :                                    int numfkdelsetcols, int16 *fkdelsetcols,
     551             :                                    bool old_check_ok,
     552             :                                    Oid parentDelTrigger, Oid parentUpdTrigger,
     553             :                                    bool with_period);
     554             : static void addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint,
     555             :                                     Relation rel, Relation pkrel, Oid indexOid, Oid parentConstr,
     556             :                                     int numfks, int16 *pkattnum, int16 *fkattnum,
     557             :                                     Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators,
     558             :                                     int numfkdelsetcols, int16 *fkdelsetcols,
     559             :                                     bool old_check_ok, LOCKMODE lockmode,
     560             :                                     Oid parentInsTrigger, Oid parentUpdTrigger,
     561             :                                     bool with_period);
     562             : static void CloneForeignKeyConstraints(List **wqueue, Relation parentRel,
     563             :                                        Relation partitionRel);
     564             : static void CloneFkReferenced(Relation parentRel, Relation partitionRel);
     565             : static void CloneFkReferencing(List **wqueue, Relation parentRel,
     566             :                                Relation partRel);
     567             : static void createForeignKeyCheckTriggers(Oid myRelOid, Oid refRelOid,
     568             :                                           Constraint *fkconstraint, Oid constraintOid,
     569             :                                           Oid indexOid,
     570             :                                           Oid parentInsTrigger, Oid parentUpdTrigger,
     571             :                                           Oid *insertTrigOid, Oid *updateTrigOid);
     572             : static void createForeignKeyActionTriggers(Relation rel, Oid refRelOid,
     573             :                                            Constraint *fkconstraint, Oid constraintOid,
     574             :                                            Oid indexOid,
     575             :                                            Oid parentDelTrigger, Oid parentUpdTrigger,
     576             :                                            Oid *deleteTrigOid, Oid *updateTrigOid);
     577             : static bool tryAttachPartitionForeignKey(ForeignKeyCacheInfo *fk,
     578             :                                          Oid partRelid,
     579             :                                          Oid parentConstrOid, int numfks,
     580             :                                          AttrNumber *mapped_conkey, AttrNumber *confkey,
     581             :                                          Oid *conpfeqop,
     582             :                                          Oid parentInsTrigger,
     583             :                                          Oid parentUpdTrigger,
     584             :                                          Relation trigrel);
     585             : static void GetForeignKeyActionTriggers(Relation trigrel,
     586             :                                         Oid conoid, Oid confrelid, Oid conrelid,
     587             :                                         Oid *deleteTriggerOid,
     588             :                                         Oid *updateTriggerOid);
     589             : static void GetForeignKeyCheckTriggers(Relation trigrel,
     590             :                                        Oid conoid, Oid confrelid, Oid conrelid,
     591             :                                        Oid *insertTriggerOid,
     592             :                                        Oid *updateTriggerOid);
     593             : static void ATExecDropConstraint(Relation rel, const char *constrName,
     594             :                                  DropBehavior behavior, bool recurse,
     595             :                                  bool missing_ok, LOCKMODE lockmode);
     596             : static ObjectAddress dropconstraint_internal(Relation rel,
     597             :                                              HeapTuple constraintTup, DropBehavior behavior,
     598             :                                              bool recurse, bool recursing,
     599             :                                              bool missing_ok, LOCKMODE lockmode);
     600             : static void ATPrepAlterColumnType(List **wqueue,
     601             :                                   AlteredTableInfo *tab, Relation rel,
     602             :                                   bool recurse, bool recursing,
     603             :                                   AlterTableCmd *cmd, LOCKMODE lockmode,
     604             :                                   AlterTableUtilityContext *context);
     605             : static bool ATColumnChangeRequiresRewrite(Node *expr, AttrNumber varattno);
     606             : static ObjectAddress ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
     607             :                                            AlterTableCmd *cmd, LOCKMODE lockmode);
     608             : static void RememberAllDependentForRebuilding(AlteredTableInfo *tab, AlterTableType subtype,
     609             :                                               Relation rel, AttrNumber attnum, const char *colName);
     610             : static void RememberConstraintForRebuilding(Oid conoid, AlteredTableInfo *tab);
     611             : static void RememberIndexForRebuilding(Oid indoid, AlteredTableInfo *tab);
     612             : static void RememberStatisticsForRebuilding(Oid stxoid, AlteredTableInfo *tab);
     613             : static void ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab,
     614             :                                    LOCKMODE lockmode);
     615             : static void ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId,
     616             :                                  char *cmd, List **wqueue, LOCKMODE lockmode,
     617             :                                  bool rewrite);
     618             : static void RebuildConstraintComment(AlteredTableInfo *tab, AlterTablePass pass,
     619             :                                      Oid objid, Relation rel, List *domname,
     620             :                                      const char *conname);
     621             : static void TryReuseIndex(Oid oldId, IndexStmt *stmt);
     622             : static void TryReuseForeignKey(Oid oldId, Constraint *con);
     623             : static ObjectAddress ATExecAlterColumnGenericOptions(Relation rel, const char *colName,
     624             :                                                      List *options, LOCKMODE lockmode);
     625             : static void change_owner_fix_column_acls(Oid relationOid,
     626             :                                          Oid oldOwnerId, Oid newOwnerId);
     627             : static void change_owner_recurse_to_sequences(Oid relationOid,
     628             :                                               Oid newOwnerId, LOCKMODE lockmode);
     629             : static ObjectAddress ATExecClusterOn(Relation rel, const char *indexName,
     630             :                                      LOCKMODE lockmode);
     631             : static void ATExecDropCluster(Relation rel, LOCKMODE lockmode);
     632             : static void ATPrepSetAccessMethod(AlteredTableInfo *tab, Relation rel, const char *amname);
     633             : static void ATExecSetAccessMethodNoStorage(Relation rel, Oid newAccessMethodId);
     634             : static void ATPrepChangePersistence(AlteredTableInfo *tab, Relation rel,
     635             :                                     bool toLogged);
     636             : static void ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel,
     637             :                                 const char *tablespacename, LOCKMODE lockmode);
     638             : static void ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode);
     639             : static void ATExecSetTableSpaceNoStorage(Relation rel, Oid newTableSpace);
     640             : static void ATExecSetRelOptions(Relation rel, List *defList,
     641             :                                 AlterTableType operation,
     642             :                                 LOCKMODE lockmode);
     643             : static void ATExecEnableDisableTrigger(Relation rel, const char *trigname,
     644             :                                        char fires_when, bool skip_system, bool recurse,
     645             :                                        LOCKMODE lockmode);
     646             : static void ATExecEnableDisableRule(Relation rel, const char *rulename,
     647             :                                     char fires_when, LOCKMODE lockmode);
     648             : static void ATPrepAddInherit(Relation child_rel);
     649             : static ObjectAddress ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode);
     650             : static ObjectAddress ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode);
     651             : static void drop_parent_dependency(Oid relid, Oid refclassid, Oid refobjid,
     652             :                                    DependencyType deptype);
     653             : static ObjectAddress ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode);
     654             : static void ATExecDropOf(Relation rel, LOCKMODE lockmode);
     655             : static void ATExecReplicaIdentity(Relation rel, ReplicaIdentityStmt *stmt, LOCKMODE lockmode);
     656             : static void ATExecGenericOptions(Relation rel, List *options);
     657             : static void ATExecSetRowSecurity(Relation rel, bool rls);
     658             : static void ATExecForceNoForceRowSecurity(Relation rel, bool force_rls);
     659             : static ObjectAddress ATExecSetCompression(Relation rel,
     660             :                                           const char *column, Node *newValue, LOCKMODE lockmode);
     661             : 
     662             : static void index_copy_data(Relation rel, RelFileLocator newrlocator);
     663             : static const char *storage_name(char c);
     664             : 
     665             : static void RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid,
     666             :                                             Oid oldRelOid, void *arg);
     667             : static void RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid,
     668             :                                              Oid oldrelid, void *arg);
     669             : static PartitionSpec *transformPartitionSpec(Relation rel, PartitionSpec *partspec);
     670             : static void ComputePartitionAttrs(ParseState *pstate, Relation rel, List *partParams, AttrNumber *partattrs,
     671             :                                   List **partexprs, Oid *partopclass, Oid *partcollation,
     672             :                                   PartitionStrategy strategy);
     673             : static void CreateInheritance(Relation child_rel, Relation parent_rel, bool ispartition);
     674             : static void RemoveInheritance(Relation child_rel, Relation parent_rel,
     675             :                               bool expect_detached);
     676             : static ObjectAddress ATExecAttachPartition(List **wqueue, Relation rel,
     677             :                                            PartitionCmd *cmd,
     678             :                                            AlterTableUtilityContext *context);
     679             : static void AttachPartitionEnsureIndexes(List **wqueue, Relation rel, Relation attachrel);
     680             : static void QueuePartitionConstraintValidation(List **wqueue, Relation scanrel,
     681             :                                                List *partConstraint,
     682             :                                                bool validate_default);
     683             : static void CloneRowTriggersToPartition(Relation parent, Relation partition);
     684             : static void DetachAddConstraintIfNeeded(List **wqueue, Relation partRel);
     685             : static void DropClonedTriggersFromPartition(Oid partitionId);
     686             : static ObjectAddress ATExecDetachPartition(List **wqueue, AlteredTableInfo *tab,
     687             :                                            Relation rel, RangeVar *name,
     688             :                                            bool concurrent);
     689             : static void DetachPartitionFinalize(Relation rel, Relation partRel,
     690             :                                     bool concurrent, Oid defaultPartOid);
     691             : static ObjectAddress ATExecDetachPartitionFinalize(Relation rel, RangeVar *name);
     692             : static ObjectAddress ATExecAttachPartitionIdx(List **wqueue, Relation parentIdx,
     693             :                                               RangeVar *name);
     694             : static void validatePartitionedIndex(Relation partedIdx, Relation partedTbl);
     695             : static void refuseDupeIndexAttach(Relation parentIdx, Relation partIdx,
     696             :                                   Relation partitionTbl);
     697             : static void verifyPartitionIndexNotNull(IndexInfo *iinfo, Relation partIdx);
     698             : static List *GetParentedForeignKeyRefs(Relation partition);
     699             : static void ATDetachCheckNoForeignKeyRefs(Relation partition);
     700             : static char GetAttributeCompression(Oid atttypid, const char *compression);
     701             : static char GetAttributeStorage(Oid atttypid, const char *storagemode);
     702             : 
     703             : 
     704             : /* ----------------------------------------------------------------
     705             :  *      DefineRelation
     706             :  *              Creates a new relation.
     707             :  *
     708             :  * stmt carries parsetree information from an ordinary CREATE TABLE statement.
     709             :  * The other arguments are used to extend the behavior for other cases:
     710             :  * relkind: relkind to assign to the new relation
     711             :  * ownerId: if not InvalidOid, use this as the new relation's owner.
     712             :  * typaddress: if not null, it's set to the pg_type entry's address.
     713             :  * queryString: for error reporting
     714             :  *
     715             :  * Note that permissions checks are done against current user regardless of
     716             :  * ownerId.  A nonzero ownerId is used when someone is creating a relation
     717             :  * "on behalf of" someone else, so we still want to see that the current user
     718             :  * has permissions to do it.
     719             :  *
     720             :  * If successful, returns the address of the new relation.
     721             :  * ----------------------------------------------------------------
     722             :  */
     723             : ObjectAddress
     724       57328 : DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
     725             :                ObjectAddress *typaddress, const char *queryString)
     726             : {
     727             :     char        relname[NAMEDATALEN];
     728             :     Oid         namespaceId;
     729             :     Oid         relationId;
     730             :     Oid         tablespaceId;
     731             :     Relation    rel;
     732             :     TupleDesc   descriptor;
     733             :     List       *inheritOids;
     734             :     List       *old_constraints;
     735             :     List       *old_notnulls;
     736             :     List       *rawDefaults;
     737             :     List       *cookedDefaults;
     738             :     List       *nncols;
     739             :     Datum       reloptions;
     740             :     ListCell   *listptr;
     741             :     AttrNumber  attnum;
     742             :     bool        partitioned;
     743       57328 :     const char *const validnsps[] = HEAP_RELOPT_NAMESPACES;
     744             :     Oid         ofTypeId;
     745             :     ObjectAddress address;
     746             :     LOCKMODE    parentLockmode;
     747       57328 :     Oid         accessMethodId = InvalidOid;
     748             : 
     749             :     /*
     750             :      * Truncate relname to appropriate length (probably a waste of time, as
     751             :      * parser should have done this already).
     752             :      */
     753       57328 :     strlcpy(relname, stmt->relation->relname, NAMEDATALEN);
     754             : 
     755             :     /*
     756             :      * Check consistency of arguments
     757             :      */
     758       57328 :     if (stmt->oncommit != ONCOMMIT_NOOP
     759         178 :         && stmt->relation->relpersistence != RELPERSISTENCE_TEMP)
     760          12 :         ereport(ERROR,
     761             :                 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
     762             :                  errmsg("ON COMMIT can only be used on temporary tables")));
     763             : 
     764       57316 :     if (stmt->partspec != NULL)
     765             :     {
     766        4796 :         if (relkind != RELKIND_RELATION)
     767           0 :             elog(ERROR, "unexpected relkind: %d", (int) relkind);
     768             : 
     769        4796 :         relkind = RELKIND_PARTITIONED_TABLE;
     770        4796 :         partitioned = true;
     771             :     }
     772             :     else
     773       52520 :         partitioned = false;
     774             : 
     775       57316 :     if (relkind == RELKIND_PARTITIONED_TABLE &&
     776        4796 :         stmt->relation->relpersistence == RELPERSISTENCE_UNLOGGED)
     777           6 :         ereport(ERROR,
     778             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     779             :                  errmsg("partitioned tables cannot be unlogged")));
     780             : 
     781             :     /*
     782             :      * Look up the namespace in which we are supposed to create the relation,
     783             :      * check we have permission to create there, lock it against concurrent
     784             :      * drop, and mark stmt->relation as RELPERSISTENCE_TEMP if a temporary
     785             :      * namespace is selected.
     786             :      */
     787             :     namespaceId =
     788       57310 :         RangeVarGetAndCheckCreationNamespace(stmt->relation, NoLock, NULL);
     789             : 
     790             :     /*
     791             :      * Security check: disallow creating temp tables from security-restricted
     792             :      * code.  This is needed because calling code might not expect untrusted
     793             :      * tables to appear in pg_temp at the front of its search path.
     794             :      */
     795       57310 :     if (stmt->relation->relpersistence == RELPERSISTENCE_TEMP
     796        3054 :         && InSecurityRestrictedOperation())
     797           0 :         ereport(ERROR,
     798             :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
     799             :                  errmsg("cannot create temporary table within security-restricted operation")));
     800             : 
     801             :     /*
     802             :      * Determine the lockmode to use when scanning parents.  A self-exclusive
     803             :      * lock is needed here.
     804             :      *
     805             :      * For regular inheritance, if two backends attempt to add children to the
     806             :      * same parent simultaneously, and that parent has no pre-existing
     807             :      * children, then both will attempt to update the parent's relhassubclass
     808             :      * field, leading to a "tuple concurrently updated" error.  Also, this
     809             :      * interlocks against a concurrent ANALYZE on the parent table, which
     810             :      * might otherwise be attempting to clear the parent's relhassubclass
     811             :      * field, if its previous children were recently dropped.
     812             :      *
     813             :      * If the child table is a partition, then we instead grab an exclusive
     814             :      * lock on the parent because its partition descriptor will be changed by
     815             :      * addition of the new partition.
     816             :      */
     817       57310 :     parentLockmode = (stmt->partbound != NULL ? AccessExclusiveLock :
     818             :                       ShareUpdateExclusiveLock);
     819             : 
     820             :     /* Determine the list of OIDs of the parents. */
     821       57310 :     inheritOids = NIL;
     822       67310 :     foreach(listptr, stmt->inhRelations)
     823             :     {
     824       10000 :         RangeVar   *rv = (RangeVar *) lfirst(listptr);
     825             :         Oid         parentOid;
     826             : 
     827       10000 :         parentOid = RangeVarGetRelid(rv, parentLockmode, false);
     828             : 
     829             :         /*
     830             :          * Reject duplications in the list of parents.
     831             :          */
     832       10000 :         if (list_member_oid(inheritOids, parentOid))
     833           0 :             ereport(ERROR,
     834             :                     (errcode(ERRCODE_DUPLICATE_TABLE),
     835             :                      errmsg("relation \"%s\" would be inherited from more than once",
     836             :                             get_rel_name(parentOid))));
     837             : 
     838       10000 :         inheritOids = lappend_oid(inheritOids, parentOid);
     839             :     }
     840             : 
     841             :     /*
     842             :      * Select tablespace to use: an explicitly indicated one, or (in the case
     843             :      * of a partitioned table) the parent's, if it has one.
     844             :      */
     845       57310 :     if (stmt->tablespacename)
     846             :     {
     847         118 :         tablespaceId = get_tablespace_oid(stmt->tablespacename, false);
     848             : 
     849         112 :         if (partitioned && tablespaceId == MyDatabaseTableSpace)
     850           6 :             ereport(ERROR,
     851             :                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     852             :                      errmsg("cannot specify default tablespace for partitioned relations")));
     853             :     }
     854       57192 :     else if (stmt->partbound)
     855             :     {
     856             :         Assert(list_length(inheritOids) == 1);
     857        7772 :         tablespaceId = get_rel_tablespace(linitial_oid(inheritOids));
     858             :     }
     859             :     else
     860       49420 :         tablespaceId = InvalidOid;
     861             : 
     862             :     /* still nothing? use the default */
     863       57298 :     if (!OidIsValid(tablespaceId))
     864       57170 :         tablespaceId = GetDefaultTablespace(stmt->relation->relpersistence,
     865             :                                             partitioned);
     866             : 
     867             :     /* Check permissions except when using database's default */
     868       57292 :     if (OidIsValid(tablespaceId) && tablespaceId != MyDatabaseTableSpace)
     869             :     {
     870             :         AclResult   aclresult;
     871             : 
     872         146 :         aclresult = object_aclcheck(TableSpaceRelationId, tablespaceId, GetUserId(),
     873             :                                     ACL_CREATE);
     874         146 :         if (aclresult != ACLCHECK_OK)
     875           6 :             aclcheck_error(aclresult, OBJECT_TABLESPACE,
     876           6 :                            get_tablespace_name(tablespaceId));
     877             :     }
     878             : 
     879             :     /* In all cases disallow placing user relations in pg_global */
     880       57286 :     if (tablespaceId == GLOBALTABLESPACE_OID)
     881          18 :         ereport(ERROR,
     882             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     883             :                  errmsg("only shared relations can be placed in pg_global tablespace")));
     884             : 
     885             :     /* Identify user ID that will own the table */
     886       57268 :     if (!OidIsValid(ownerId))
     887       57028 :         ownerId = GetUserId();
     888             : 
     889             :     /*
     890             :      * Parse and validate reloptions, if any.
     891             :      */
     892       57268 :     reloptions = transformRelOptions((Datum) 0, stmt->options, NULL, validnsps,
     893             :                                      true, false);
     894             : 
     895       57250 :     switch (relkind)
     896             :     {
     897       14884 :         case RELKIND_VIEW:
     898       14884 :             (void) view_reloptions(reloptions, true);
     899       14866 :             break;
     900        4772 :         case RELKIND_PARTITIONED_TABLE:
     901        4772 :             (void) partitioned_table_reloptions(reloptions, true);
     902        4766 :             break;
     903       37594 :         default:
     904       37594 :             (void) heap_reloptions(relkind, reloptions, true);
     905             :     }
     906             : 
     907       57130 :     if (stmt->ofTypename)
     908             :     {
     909             :         AclResult   aclresult;
     910             : 
     911          86 :         ofTypeId = typenameTypeId(NULL, stmt->ofTypename);
     912             : 
     913          86 :         aclresult = object_aclcheck(TypeRelationId, ofTypeId, GetUserId(), ACL_USAGE);
     914          86 :         if (aclresult != ACLCHECK_OK)
     915           6 :             aclcheck_error_type(aclresult, ofTypeId);
     916             :     }
     917             :     else
     918       57044 :         ofTypeId = InvalidOid;
     919             : 
     920             :     /*
     921             :      * Look up inheritance ancestors and generate relation schema, including
     922             :      * inherited attributes.  (Note that stmt->tableElts is destructively
     923             :      * modified by MergeAttributes.)
     924             :      */
     925       56956 :     stmt->tableElts =
     926       57124 :         MergeAttributes(stmt->tableElts, inheritOids,
     927       57124 :                         stmt->relation->relpersistence,
     928       57124 :                         stmt->partbound != NULL,
     929             :                         &old_constraints, &old_notnulls);
     930             : 
     931             :     /*
     932             :      * Create a tuple descriptor from the relation schema.  Note that this
     933             :      * deals with column names, types, and in-descriptor NOT NULL flags, but
     934             :      * not default values, NOT NULL or CHECK constraints; we handle those
     935             :      * below.
     936             :      */
     937       56956 :     descriptor = BuildDescForRelation(stmt->tableElts);
     938             : 
     939             :     /*
     940             :      * Find columns with default values and prepare for insertion of the
     941             :      * defaults.  Pre-cooked (that is, inherited) defaults go into a list of
     942             :      * CookedConstraint structs that we'll pass to heap_create_with_catalog,
     943             :      * while raw defaults go into a list of RawColumnDefault structs that will
     944             :      * be processed by AddRelationNewConstraints.  (We can't deal with raw
     945             :      * expressions until we can do transformExpr.)
     946             :      *
     947             :      * We can set the atthasdef flags now in the tuple descriptor; this just
     948             :      * saves StoreAttrDefault from having to do an immediate update of the
     949             :      * pg_attribute rows.
     950             :      */
     951       56908 :     rawDefaults = NIL;
     952       56908 :     cookedDefaults = NIL;
     953       56908 :     attnum = 0;
     954             : 
     955      285534 :     foreach(listptr, stmt->tableElts)
     956             :     {
     957      228626 :         ColumnDef  *colDef = lfirst(listptr);
     958             :         Form_pg_attribute attr;
     959             : 
     960      228626 :         attnum++;
     961      228626 :         attr = TupleDescAttr(descriptor, attnum - 1);
     962             : 
     963      228626 :         if (colDef->raw_default != NULL)
     964             :         {
     965             :             RawColumnDefault *rawEnt;
     966             : 
     967             :             Assert(colDef->cooked_default == NULL);
     968             : 
     969        2428 :             rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
     970        2428 :             rawEnt->attnum = attnum;
     971        2428 :             rawEnt->raw_default = colDef->raw_default;
     972        2428 :             rawEnt->missingMode = false;
     973        2428 :             rawEnt->generated = colDef->generated;
     974        2428 :             rawDefaults = lappend(rawDefaults, rawEnt);
     975        2428 :             attr->atthasdef = true;
     976             :         }
     977      226198 :         else if (colDef->cooked_default != NULL)
     978             :         {
     979             :             CookedConstraint *cooked;
     980             : 
     981         344 :             cooked = (CookedConstraint *) palloc(sizeof(CookedConstraint));
     982         344 :             cooked->contype = CONSTR_DEFAULT;
     983         344 :             cooked->conoid = InvalidOid; /* until created */
     984         344 :             cooked->name = NULL;
     985         344 :             cooked->attnum = attnum;
     986         344 :             cooked->expr = colDef->cooked_default;
     987         344 :             cooked->is_enforced = true;
     988         344 :             cooked->skip_validation = false;
     989         344 :             cooked->is_local = true; /* not used for defaults */
     990         344 :             cooked->inhcount = 0;    /* ditto */
     991         344 :             cooked->is_no_inherit = false;
     992         344 :             cookedDefaults = lappend(cookedDefaults, cooked);
     993         344 :             attr->atthasdef = true;
     994             :         }
     995             : 
     996      228626 :         populate_compact_attribute(descriptor, attnum - 1);
     997             :     }
     998             : 
     999             :     /*
    1000             :      * For relations with table AM and partitioned tables, select access
    1001             :      * method to use: an explicitly indicated one, or (in the case of a
    1002             :      * partitioned table) the parent's, if it has one.
    1003             :      */
    1004       56908 :     if (stmt->accessMethod != NULL)
    1005             :     {
    1006             :         Assert(RELKIND_HAS_TABLE_AM(relkind) || relkind == RELKIND_PARTITIONED_TABLE);
    1007         122 :         accessMethodId = get_table_am_oid(stmt->accessMethod, false);
    1008             :     }
    1009       56786 :     else if (RELKIND_HAS_TABLE_AM(relkind) || relkind == RELKIND_PARTITIONED_TABLE)
    1010             :     {
    1011       36288 :         if (stmt->partbound)
    1012             :         {
    1013             :             Assert(list_length(inheritOids) == 1);
    1014        7614 :             accessMethodId = get_rel_relam(linitial_oid(inheritOids));
    1015             :         }
    1016             : 
    1017       36288 :         if (RELKIND_HAS_TABLE_AM(relkind) && !OidIsValid(accessMethodId))
    1018       31508 :             accessMethodId = get_table_am_oid(default_table_access_method, false);
    1019             :     }
    1020             : 
    1021             :     /*
    1022             :      * Create the relation.  Inherited defaults and CHECK constraints are
    1023             :      * passed in for immediate handling --- since they don't need parsing,
    1024             :      * they can be stored immediately.
    1025             :      */
    1026       56890 :     relationId = heap_create_with_catalog(relname,
    1027             :                                           namespaceId,
    1028             :                                           tablespaceId,
    1029             :                                           InvalidOid,
    1030             :                                           InvalidOid,
    1031             :                                           ofTypeId,
    1032             :                                           ownerId,
    1033             :                                           accessMethodId,
    1034             :                                           descriptor,
    1035             :                                           list_concat(cookedDefaults,
    1036             :                                                       old_constraints),
    1037             :                                           relkind,
    1038       56890 :                                           stmt->relation->relpersistence,
    1039             :                                           false,
    1040             :                                           false,
    1041             :                                           stmt->oncommit,
    1042             :                                           reloptions,
    1043             :                                           true,
    1044             :                                           allowSystemTableMods,
    1045             :                                           false,
    1046             :                                           InvalidOid,
    1047             :                                           typaddress);
    1048             : 
    1049             :     /*
    1050             :      * We must bump the command counter to make the newly-created relation
    1051             :      * tuple visible for opening.
    1052             :      */
    1053       56866 :     CommandCounterIncrement();
    1054             : 
    1055             :     /*
    1056             :      * Open the new relation and acquire exclusive lock on it.  This isn't
    1057             :      * really necessary for locking out other backends (since they can't see
    1058             :      * the new rel anyway until we commit), but it keeps the lock manager from
    1059             :      * complaining about deadlock risks.
    1060             :      */
    1061       56866 :     rel = relation_open(relationId, AccessExclusiveLock);
    1062             : 
    1063             :     /*
    1064             :      * Now add any newly specified column default and generation expressions
    1065             :      * to the new relation.  These are passed to us in the form of raw
    1066             :      * parsetrees; we need to transform them to executable expression trees
    1067             :      * before they can be added. The most convenient way to do that is to
    1068             :      * apply the parser's transformExpr routine, but transformExpr doesn't
    1069             :      * work unless we have a pre-existing relation. So, the transformation has
    1070             :      * to be postponed to this final step of CREATE TABLE.
    1071             :      *
    1072             :      * This needs to be before processing the partitioning clauses because
    1073             :      * those could refer to generated columns.
    1074             :      */
    1075       56866 :     if (rawDefaults)
    1076        2064 :         AddRelationNewConstraints(rel, rawDefaults, NIL,
    1077             :                                   true, true, false, queryString);
    1078             : 
    1079             :     /*
    1080             :      * Make column generation expressions visible for use by partitioning.
    1081             :      */
    1082       56764 :     CommandCounterIncrement();
    1083             : 
    1084             :     /* Process and store partition bound, if any. */
    1085       56764 :     if (stmt->partbound)
    1086             :     {
    1087             :         PartitionBoundSpec *bound;
    1088             :         ParseState *pstate;
    1089        7718 :         Oid         parentId = linitial_oid(inheritOids),
    1090             :                     defaultPartOid;
    1091             :         Relation    parent,
    1092        7718 :                     defaultRel = NULL;
    1093             :         ParseNamespaceItem *nsitem;
    1094             : 
    1095             :         /* Already have strong enough lock on the parent */
    1096        7718 :         parent = table_open(parentId, NoLock);
    1097             : 
    1098             :         /*
    1099             :          * We are going to try to validate the partition bound specification
    1100             :          * against the partition key of parentRel, so it better have one.
    1101             :          */
    1102        7718 :         if (parent->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
    1103          18 :             ereport(ERROR,
    1104             :                     (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
    1105             :                      errmsg("\"%s\" is not partitioned",
    1106             :                             RelationGetRelationName(parent))));
    1107             : 
    1108             :         /*
    1109             :          * The partition constraint of the default partition depends on the
    1110             :          * partition bounds of every other partition. It is possible that
    1111             :          * another backend might be about to execute a query on the default
    1112             :          * partition table, and that the query relies on previously cached
    1113             :          * default partition constraints. We must therefore take a table lock
    1114             :          * strong enough to prevent all queries on the default partition from
    1115             :          * proceeding until we commit and send out a shared-cache-inval notice
    1116             :          * that will make them update their index lists.
    1117             :          *
    1118             :          * Order of locking: The relation being added won't be visible to
    1119             :          * other backends until it is committed, hence here in
    1120             :          * DefineRelation() the order of locking the default partition and the
    1121             :          * relation being added does not matter. But at all other places we
    1122             :          * need to lock the default relation before we lock the relation being
    1123             :          * added or removed i.e. we should take the lock in same order at all
    1124             :          * the places such that lock parent, lock default partition and then
    1125             :          * lock the partition so as to avoid a deadlock.
    1126             :          */
    1127             :         defaultPartOid =
    1128        7700 :             get_default_oid_from_partdesc(RelationGetPartitionDesc(parent,
    1129             :                                                                    true));
    1130        7700 :         if (OidIsValid(defaultPartOid))
    1131         378 :             defaultRel = table_open(defaultPartOid, AccessExclusiveLock);
    1132             : 
    1133             :         /* Transform the bound values */
    1134        7700 :         pstate = make_parsestate(NULL);
    1135        7700 :         pstate->p_sourcetext = queryString;
    1136             : 
    1137             :         /*
    1138             :          * Add an nsitem containing this relation, so that transformExpr
    1139             :          * called on partition bound expressions is able to report errors
    1140             :          * using a proper context.
    1141             :          */
    1142        7700 :         nsitem = addRangeTableEntryForRelation(pstate, rel, AccessShareLock,
    1143             :                                                NULL, false, false);
    1144        7700 :         addNSItemToQuery(pstate, nsitem, false, true, true);
    1145             : 
    1146        7700 :         bound = transformPartitionBound(pstate, parent, stmt->partbound);
    1147             : 
    1148             :         /*
    1149             :          * Check first that the new partition's bound is valid and does not
    1150             :          * overlap with any of existing partitions of the parent.
    1151             :          */
    1152        7496 :         check_new_partition_bound(relname, parent, bound, pstate);
    1153             : 
    1154             :         /*
    1155             :          * If the default partition exists, its partition constraints will
    1156             :          * change after the addition of this new partition such that it won't
    1157             :          * allow any row that qualifies for this new partition. So, check that
    1158             :          * the existing data in the default partition satisfies the constraint
    1159             :          * as it will exist after adding this partition.
    1160             :          */
    1161        7382 :         if (OidIsValid(defaultPartOid))
    1162             :         {
    1163         348 :             check_default_partition_contents(parent, defaultRel, bound);
    1164             :             /* Keep the lock until commit. */
    1165         330 :             table_close(defaultRel, NoLock);
    1166             :         }
    1167             : 
    1168             :         /* Update the pg_class entry. */
    1169        7364 :         StorePartitionBound(rel, parent, bound);
    1170             : 
    1171        7364 :         table_close(parent, NoLock);
    1172             :     }
    1173             : 
    1174             :     /* Store inheritance information for new rel. */
    1175       56410 :     StoreCatalogInheritance(relationId, inheritOids, stmt->partbound != NULL);
    1176             : 
    1177             :     /*
    1178             :      * Process the partitioning specification (if any) and store the partition
    1179             :      * key information into the catalog.
    1180             :      */
    1181       56410 :     if (partitioned)
    1182             :     {
    1183             :         ParseState *pstate;
    1184             :         int         partnatts;
    1185             :         AttrNumber  partattrs[PARTITION_MAX_KEYS];
    1186             :         Oid         partopclass[PARTITION_MAX_KEYS];
    1187             :         Oid         partcollation[PARTITION_MAX_KEYS];
    1188        4766 :         List       *partexprs = NIL;
    1189             : 
    1190        4766 :         pstate = make_parsestate(NULL);
    1191        4766 :         pstate->p_sourcetext = queryString;
    1192             : 
    1193        4766 :         partnatts = list_length(stmt->partspec->partParams);
    1194             : 
    1195             :         /* Protect fixed-size arrays here and in executor */
    1196        4766 :         if (partnatts > PARTITION_MAX_KEYS)
    1197           0 :             ereport(ERROR,
    1198             :                     (errcode(ERRCODE_TOO_MANY_COLUMNS),
    1199             :                      errmsg("cannot partition using more than %d columns",
    1200             :                             PARTITION_MAX_KEYS)));
    1201             : 
    1202             :         /*
    1203             :          * We need to transform the raw parsetrees corresponding to partition
    1204             :          * expressions into executable expression trees.  Like column defaults
    1205             :          * and CHECK constraints, we could not have done the transformation
    1206             :          * earlier.
    1207             :          */
    1208        4766 :         stmt->partspec = transformPartitionSpec(rel, stmt->partspec);
    1209             : 
    1210        4736 :         ComputePartitionAttrs(pstate, rel, stmt->partspec->partParams,
    1211             :                               partattrs, &partexprs, partopclass,
    1212        4736 :                               partcollation, stmt->partspec->strategy);
    1213             : 
    1214        4652 :         StorePartitionKey(rel, stmt->partspec->strategy, partnatts, partattrs,
    1215             :                           partexprs,
    1216             :                           partopclass, partcollation);
    1217             : 
    1218             :         /* make it all visible */
    1219        4652 :         CommandCounterIncrement();
    1220             :     }
    1221             : 
    1222             :     /*
    1223             :      * If we're creating a partition, create now all the indexes, triggers,
    1224             :      * FKs defined in the parent.
    1225             :      *
    1226             :      * We can't do it earlier, because DefineIndex wants to know the partition
    1227             :      * key which we just stored.
    1228             :      */
    1229       56296 :     if (stmt->partbound)
    1230             :     {
    1231        7358 :         Oid         parentId = linitial_oid(inheritOids);
    1232             :         Relation    parent;
    1233             :         List       *idxlist;
    1234             :         ListCell   *cell;
    1235             : 
    1236             :         /* Already have strong enough lock on the parent */
    1237        7358 :         parent = table_open(parentId, NoLock);
    1238        7358 :         idxlist = RelationGetIndexList(parent);
    1239             : 
    1240             :         /*
    1241             :          * For each index in the parent table, create one in the partition
    1242             :          */
    1243        8702 :         foreach(cell, idxlist)
    1244             :         {
    1245        1362 :             Relation    idxRel = index_open(lfirst_oid(cell), AccessShareLock);
    1246             :             AttrMap    *attmap;
    1247             :             IndexStmt  *idxstmt;
    1248             :             Oid         constraintOid;
    1249             : 
    1250        1362 :             if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
    1251             :             {
    1252          36 :                 if (idxRel->rd_index->indisunique)
    1253          12 :                     ereport(ERROR,
    1254             :                             (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    1255             :                              errmsg("cannot create foreign partition of partitioned table \"%s\"",
    1256             :                                     RelationGetRelationName(parent)),
    1257             :                              errdetail("Table \"%s\" contains indexes that are unique.",
    1258             :                                        RelationGetRelationName(parent))));
    1259             :                 else
    1260             :                 {
    1261          24 :                     index_close(idxRel, AccessShareLock);
    1262          24 :                     continue;
    1263             :                 }
    1264             :             }
    1265             : 
    1266        1326 :             attmap = build_attrmap_by_name(RelationGetDescr(rel),
    1267             :                                            RelationGetDescr(parent),
    1268             :                                            false);
    1269             :             idxstmt =
    1270        1326 :                 generateClonedIndexStmt(NULL, idxRel,
    1271             :                                         attmap, &constraintOid);
    1272        1326 :             DefineIndex(RelationGetRelid(rel),
    1273             :                         idxstmt,
    1274             :                         InvalidOid,
    1275             :                         RelationGetRelid(idxRel),
    1276             :                         constraintOid,
    1277             :                         -1,
    1278             :                         false, false, false, false, false);
    1279             : 
    1280        1320 :             index_close(idxRel, AccessShareLock);
    1281             :         }
    1282             : 
    1283        7340 :         list_free(idxlist);
    1284             : 
    1285             :         /*
    1286             :          * If there are any row-level triggers, clone them to the new
    1287             :          * partition.
    1288             :          */
    1289        7340 :         if (parent->trigdesc != NULL)
    1290         408 :             CloneRowTriggersToPartition(parent, rel);
    1291             : 
    1292             :         /*
    1293             :          * And foreign keys too.  Note that because we're freshly creating the
    1294             :          * table, there is no need to verify these new constraints.
    1295             :          */
    1296        7340 :         CloneForeignKeyConstraints(NULL, parent, rel);
    1297             : 
    1298        7340 :         table_close(parent, NoLock);
    1299             :     }
    1300             : 
    1301             :     /*
    1302             :      * Now add any newly specified CHECK constraints to the new relation. Same
    1303             :      * as for defaults above, but these need to come after partitioning is set
    1304             :      * up.
    1305             :      */
    1306       56278 :     if (stmt->constraints)
    1307         700 :         AddRelationNewConstraints(rel, NIL, stmt->constraints,
    1308             :                                   true, true, false, queryString);
    1309             : 
    1310             :     /*
    1311             :      * Finally, merge the not-null constraints that are declared directly with
    1312             :      * those that come from parent relations (making sure to count inheritance
    1313             :      * appropriately for each), create them, and set the attnotnull flag on
    1314             :      * columns that don't yet have it.
    1315             :      */
    1316       56248 :     nncols = AddRelationNotNullConstraints(rel, stmt->nnconstraints,
    1317             :                                            old_notnulls);
    1318      126520 :     foreach_int(attrnum, nncols)
    1319       14180 :         set_attnotnull(NULL, rel, attrnum, NoLock);
    1320             : 
    1321       56170 :     ObjectAddressSet(address, RelationRelationId, relationId);
    1322             : 
    1323             :     /*
    1324             :      * Clean up.  We keep lock on new relation (although it shouldn't be
    1325             :      * visible to anyone else anyway, until commit).
    1326             :      */
    1327       56170 :     relation_close(rel, NoLock);
    1328             : 
    1329       56170 :     return address;
    1330             : }
    1331             : 
    1332             : /*
    1333             :  * BuildDescForRelation
    1334             :  *
    1335             :  * Given a list of ColumnDef nodes, build a TupleDesc.
    1336             :  *
    1337             :  * Note: This is only for the limited purpose of table and view creation.  Not
    1338             :  * everything is filled in.  A real tuple descriptor should be obtained from
    1339             :  * the relcache.
    1340             :  */
    1341             : TupleDesc
    1342       59668 : BuildDescForRelation(const List *columns)
    1343             : {
    1344             :     int         natts;
    1345             :     AttrNumber  attnum;
    1346             :     ListCell   *l;
    1347             :     TupleDesc   desc;
    1348             :     char       *attname;
    1349             :     Oid         atttypid;
    1350             :     int32       atttypmod;
    1351             :     Oid         attcollation;
    1352             :     int         attdim;
    1353             : 
    1354             :     /*
    1355             :      * allocate a new tuple descriptor
    1356             :      */
    1357       59668 :     natts = list_length(columns);
    1358       59668 :     desc = CreateTemplateTupleDesc(natts);
    1359             : 
    1360       59668 :     attnum = 0;
    1361             : 
    1362      291270 :     foreach(l, columns)
    1363             :     {
    1364      231662 :         ColumnDef  *entry = lfirst(l);
    1365             :         AclResult   aclresult;
    1366             :         Form_pg_attribute att;
    1367             : 
    1368             :         /*
    1369             :          * for each entry in the list, get the name and type information from
    1370             :          * the list and have TupleDescInitEntry fill in the attribute
    1371             :          * information we need.
    1372             :          */
    1373      231662 :         attnum++;
    1374             : 
    1375      231662 :         attname = entry->colname;
    1376      231662 :         typenameTypeIdAndMod(NULL, entry->typeName, &atttypid, &atttypmod);
    1377             : 
    1378      231662 :         aclresult = object_aclcheck(TypeRelationId, atttypid, GetUserId(), ACL_USAGE);
    1379      231662 :         if (aclresult != ACLCHECK_OK)
    1380          42 :             aclcheck_error_type(aclresult, atttypid);
    1381             : 
    1382      231620 :         attcollation = GetColumnDefCollation(NULL, entry, atttypid);
    1383      231620 :         attdim = list_length(entry->typeName->arrayBounds);
    1384      231620 :         if (attdim > PG_INT16_MAX)
    1385           0 :             ereport(ERROR,
    1386             :                     errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
    1387             :                     errmsg("too many array dimensions"));
    1388             : 
    1389      231620 :         if (entry->typeName->setof)
    1390           0 :             ereport(ERROR,
    1391             :                     (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    1392             :                      errmsg("column \"%s\" cannot be declared SETOF",
    1393             :                             attname)));
    1394             : 
    1395      231620 :         TupleDescInitEntry(desc, attnum, attname,
    1396             :                            atttypid, atttypmod, attdim);
    1397      231620 :         att = TupleDescAttr(desc, attnum - 1);
    1398             : 
    1399             :         /* Override TupleDescInitEntry's settings as requested */
    1400      231620 :         TupleDescInitEntryCollation(desc, attnum, attcollation);
    1401             : 
    1402             :         /* Fill in additional stuff not handled by TupleDescInitEntry */
    1403      231620 :         att->attnotnull = entry->is_not_null;
    1404      231620 :         att->attislocal = entry->is_local;
    1405      231620 :         att->attinhcount = entry->inhcount;
    1406      231620 :         att->attidentity = entry->identity;
    1407      231620 :         att->attgenerated = entry->generated;
    1408      231620 :         att->attcompression = GetAttributeCompression(att->atttypid, entry->compression);
    1409      231608 :         if (entry->storage)
    1410       19342 :             att->attstorage = entry->storage;
    1411      212266 :         else if (entry->storage_name)
    1412          20 :             att->attstorage = GetAttributeStorage(att->atttypid, entry->storage_name);
    1413             : 
    1414      231602 :         populate_compact_attribute(desc, attnum - 1);
    1415             :     }
    1416             : 
    1417       59608 :     return desc;
    1418             : }
    1419             : 
    1420             : /*
    1421             :  * Emit the right error or warning message for a "DROP" command issued on a
    1422             :  * non-existent relation
    1423             :  */
    1424             : static void
    1425        1060 : DropErrorMsgNonExistent(RangeVar *rel, char rightkind, bool missing_ok)
    1426             : {
    1427             :     const struct dropmsgstrings *rentry;
    1428             : 
    1429        1180 :     if (rel->schemaname != NULL &&
    1430         120 :         !OidIsValid(LookupNamespaceNoError(rel->schemaname)))
    1431             :     {
    1432          42 :         if (!missing_ok)
    1433             :         {
    1434           0 :             ereport(ERROR,
    1435             :                     (errcode(ERRCODE_UNDEFINED_SCHEMA),
    1436             :                      errmsg("schema \"%s\" does not exist", rel->schemaname)));
    1437             :         }
    1438             :         else
    1439             :         {
    1440          42 :             ereport(NOTICE,
    1441             :                     (errmsg("schema \"%s\" does not exist, skipping",
    1442             :                             rel->schemaname)));
    1443             :         }
    1444          42 :         return;
    1445             :     }
    1446             : 
    1447        1338 :     for (rentry = dropmsgstringarray; rentry->kind != '\0'; rentry++)
    1448             :     {
    1449        1338 :         if (rentry->kind == rightkind)
    1450             :         {
    1451        1018 :             if (!missing_ok)
    1452             :             {
    1453         138 :                 ereport(ERROR,
    1454             :                         (errcode(rentry->nonexistent_code),
    1455             :                          errmsg(rentry->nonexistent_msg, rel->relname)));
    1456             :             }
    1457             :             else
    1458             :             {
    1459         880 :                 ereport(NOTICE, (errmsg(rentry->skipping_msg, rel->relname)));
    1460         880 :                 break;
    1461             :             }
    1462             :         }
    1463             :     }
    1464             : 
    1465             :     Assert(rentry->kind != '\0');    /* Should be impossible */
    1466             : }
    1467             : 
    1468             : /*
    1469             :  * Emit the right error message for a "DROP" command issued on a
    1470             :  * relation of the wrong type
    1471             :  */
    1472             : static void
    1473           0 : DropErrorMsgWrongType(const char *relname, char wrongkind, char rightkind)
    1474             : {
    1475             :     const struct dropmsgstrings *rentry;
    1476             :     const struct dropmsgstrings *wentry;
    1477             : 
    1478           0 :     for (rentry = dropmsgstringarray; rentry->kind != '\0'; rentry++)
    1479           0 :         if (rentry->kind == rightkind)
    1480           0 :             break;
    1481             :     Assert(rentry->kind != '\0');
    1482             : 
    1483           0 :     for (wentry = dropmsgstringarray; wentry->kind != '\0'; wentry++)
    1484           0 :         if (wentry->kind == wrongkind)
    1485           0 :             break;
    1486             :     /* wrongkind could be something we don't have in our table... */
    1487             : 
    1488           0 :     ereport(ERROR,
    1489             :             (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    1490             :              errmsg(rentry->nota_msg, relname),
    1491             :              (wentry->kind != '\0') ? errhint("%s", _(wentry->drophint_msg)) : 0));
    1492             : }
    1493             : 
    1494             : /*
    1495             :  * RemoveRelations
    1496             :  *      Implements DROP TABLE, DROP INDEX, DROP SEQUENCE, DROP VIEW,
    1497             :  *      DROP MATERIALIZED VIEW, DROP FOREIGN TABLE
    1498             :  */
    1499             : void
    1500       16494 : RemoveRelations(DropStmt *drop)
    1501             : {
    1502             :     ObjectAddresses *objects;
    1503             :     char        relkind;
    1504             :     ListCell   *cell;
    1505       16494 :     int         flags = 0;
    1506       16494 :     LOCKMODE    lockmode = AccessExclusiveLock;
    1507             : 
    1508             :     /* DROP CONCURRENTLY uses a weaker lock, and has some restrictions */
    1509       16494 :     if (drop->concurrent)
    1510             :     {
    1511             :         /*
    1512             :          * Note that for temporary relations this lock may get upgraded later
    1513             :          * on, but as no other session can access a temporary relation, this
    1514             :          * is actually fine.
    1515             :          */
    1516         150 :         lockmode = ShareUpdateExclusiveLock;
    1517             :         Assert(drop->removeType == OBJECT_INDEX);
    1518         150 :         if (list_length(drop->objects) != 1)
    1519           6 :             ereport(ERROR,
    1520             :                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    1521             :                      errmsg("DROP INDEX CONCURRENTLY does not support dropping multiple objects")));
    1522         144 :         if (drop->behavior == DROP_CASCADE)
    1523           0 :             ereport(ERROR,
    1524             :                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    1525             :                      errmsg("DROP INDEX CONCURRENTLY does not support CASCADE")));
    1526             :     }
    1527             : 
    1528             :     /*
    1529             :      * First we identify all the relations, then we delete them in a single
    1530             :      * performMultipleDeletions() call.  This is to avoid unwanted DROP
    1531             :      * RESTRICT errors if one of the relations depends on another.
    1532             :      */
    1533             : 
    1534             :     /* Determine required relkind */
    1535       16488 :     switch (drop->removeType)
    1536             :     {
    1537       14308 :         case OBJECT_TABLE:
    1538       14308 :             relkind = RELKIND_RELATION;
    1539       14308 :             break;
    1540             : 
    1541         820 :         case OBJECT_INDEX:
    1542         820 :             relkind = RELKIND_INDEX;
    1543         820 :             break;
    1544             : 
    1545         176 :         case OBJECT_SEQUENCE:
    1546         176 :             relkind = RELKIND_SEQUENCE;
    1547         176 :             break;
    1548             : 
    1549         914 :         case OBJECT_VIEW:
    1550         914 :             relkind = RELKIND_VIEW;
    1551         914 :             break;
    1552             : 
    1553         120 :         case OBJECT_MATVIEW:
    1554         120 :             relkind = RELKIND_MATVIEW;
    1555         120 :             break;
    1556             : 
    1557         150 :         case OBJECT_FOREIGN_TABLE:
    1558         150 :             relkind = RELKIND_FOREIGN_TABLE;
    1559         150 :             break;
    1560             : 
    1561           0 :         default:
    1562           0 :             elog(ERROR, "unrecognized drop object type: %d",
    1563             :                  (int) drop->removeType);
    1564             :             relkind = 0;        /* keep compiler quiet */
    1565             :             break;
    1566             :     }
    1567             : 
    1568             :     /* Lock and validate each relation; build a list of object addresses */
    1569       16488 :     objects = new_object_addresses();
    1570             : 
    1571       36670 :     foreach(cell, drop->objects)
    1572             :     {
    1573       20346 :         RangeVar   *rel = makeRangeVarFromNameList((List *) lfirst(cell));
    1574             :         Oid         relOid;
    1575             :         ObjectAddress obj;
    1576             :         struct DropRelationCallbackState state;
    1577             : 
    1578             :         /*
    1579             :          * These next few steps are a great deal like relation_openrv, but we
    1580             :          * don't bother building a relcache entry since we don't need it.
    1581             :          *
    1582             :          * Check for shared-cache-inval messages before trying to access the
    1583             :          * relation.  This is needed to cover the case where the name
    1584             :          * identifies a rel that has been dropped and recreated since the
    1585             :          * start of our transaction: if we don't flush the old syscache entry,
    1586             :          * then we'll latch onto that entry and suffer an error later.
    1587             :          */
    1588       20346 :         AcceptInvalidationMessages();
    1589             : 
    1590             :         /* Look up the appropriate relation using namespace search. */
    1591       20346 :         state.expected_relkind = relkind;
    1592       40692 :         state.heap_lockmode = drop->concurrent ?
    1593       20346 :             ShareUpdateExclusiveLock : AccessExclusiveLock;
    1594             :         /* We must initialize these fields to show that no locks are held: */
    1595       20346 :         state.heapOid = InvalidOid;
    1596       20346 :         state.partParentOid = InvalidOid;
    1597             : 
    1598       20346 :         relOid = RangeVarGetRelidExtended(rel, lockmode, RVR_MISSING_OK,
    1599             :                                           RangeVarCallbackForDropRelation,
    1600             :                                           &state);
    1601             : 
    1602             :         /* Not there? */
    1603       20326 :         if (!OidIsValid(relOid))
    1604             :         {
    1605        1060 :             DropErrorMsgNonExistent(rel, relkind, drop->missing_ok);
    1606         922 :             continue;
    1607             :         }
    1608             : 
    1609             :         /*
    1610             :          * Decide if concurrent mode needs to be used here or not.  The
    1611             :          * callback retrieved the rel's persistence for us.
    1612             :          */
    1613       19266 :         if (drop->concurrent &&
    1614         138 :             state.actual_relpersistence != RELPERSISTENCE_TEMP)
    1615             :         {
    1616             :             Assert(list_length(drop->objects) == 1 &&
    1617             :                    drop->removeType == OBJECT_INDEX);
    1618         120 :             flags |= PERFORM_DELETION_CONCURRENTLY;
    1619             :         }
    1620             : 
    1621             :         /*
    1622             :          * Concurrent index drop cannot be used with partitioned indexes,
    1623             :          * either.
    1624             :          */
    1625       19266 :         if ((flags & PERFORM_DELETION_CONCURRENTLY) != 0 &&
    1626         120 :             state.actual_relkind == RELKIND_PARTITIONED_INDEX)
    1627           6 :             ereport(ERROR,
    1628             :                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    1629             :                      errmsg("cannot drop partitioned index \"%s\" concurrently",
    1630             :                             rel->relname)));
    1631             : 
    1632             :         /*
    1633             :          * If we're told to drop a partitioned index, we must acquire lock on
    1634             :          * all the children of its parent partitioned table before proceeding.
    1635             :          * Otherwise we'd try to lock the child index partitions before their
    1636             :          * tables, leading to potential deadlock against other sessions that
    1637             :          * will lock those objects in the other order.
    1638             :          */
    1639       19260 :         if (state.actual_relkind == RELKIND_PARTITIONED_INDEX)
    1640          70 :             (void) find_all_inheritors(state.heapOid,
    1641             :                                        state.heap_lockmode,
    1642             :                                        NULL);
    1643             : 
    1644             :         /* OK, we're ready to delete this one */
    1645       19260 :         obj.classId = RelationRelationId;
    1646       19260 :         obj.objectId = relOid;
    1647       19260 :         obj.objectSubId = 0;
    1648             : 
    1649       19260 :         add_exact_object_address(&obj, objects);
    1650             :     }
    1651             : 
    1652       16324 :     performMultipleDeletions(objects, drop->behavior, flags);
    1653             : 
    1654       16188 :     free_object_addresses(objects);
    1655       16188 : }
    1656             : 
    1657             : /*
    1658             :  * Before acquiring a table lock, check whether we have sufficient rights.
    1659             :  * In the case of DROP INDEX, also try to lock the table before the index.
    1660             :  * Also, if the table to be dropped is a partition, we try to lock the parent
    1661             :  * first.
    1662             :  */
    1663             : static void
    1664       20652 : RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid, Oid oldRelOid,
    1665             :                                 void *arg)
    1666             : {
    1667             :     HeapTuple   tuple;
    1668             :     struct DropRelationCallbackState *state;
    1669             :     char        expected_relkind;
    1670             :     bool        is_partition;
    1671             :     Form_pg_class classform;
    1672             :     LOCKMODE    heap_lockmode;
    1673       20652 :     bool        invalid_system_index = false;
    1674             : 
    1675       20652 :     state = (struct DropRelationCallbackState *) arg;
    1676       20652 :     heap_lockmode = state->heap_lockmode;
    1677             : 
    1678             :     /*
    1679             :      * If we previously locked some other index's heap, and the name we're
    1680             :      * looking up no longer refers to that relation, release the now-useless
    1681             :      * lock.
    1682             :      */
    1683       20652 :     if (relOid != oldRelOid && OidIsValid(state->heapOid))
    1684             :     {
    1685           0 :         UnlockRelationOid(state->heapOid, heap_lockmode);
    1686           0 :         state->heapOid = InvalidOid;
    1687             :     }
    1688             : 
    1689             :     /*
    1690             :      * Similarly, if we previously locked some other partition's heap, and the
    1691             :      * name we're looking up no longer refers to that relation, release the
    1692             :      * now-useless lock.
    1693             :      */
    1694       20652 :     if (relOid != oldRelOid && OidIsValid(state->partParentOid))
    1695             :     {
    1696           0 :         UnlockRelationOid(state->partParentOid, AccessExclusiveLock);
    1697           0 :         state->partParentOid = InvalidOid;
    1698             :     }
    1699             : 
    1700             :     /* Didn't find a relation, so no need for locking or permission checks. */
    1701       20652 :     if (!OidIsValid(relOid))
    1702        1066 :         return;
    1703             : 
    1704       19586 :     tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid));
    1705       19586 :     if (!HeapTupleIsValid(tuple))
    1706           0 :         return;                 /* concurrently dropped, so nothing to do */
    1707       19586 :     classform = (Form_pg_class) GETSTRUCT(tuple);
    1708       19586 :     is_partition = classform->relispartition;
    1709             : 
    1710             :     /* Pass back some data to save lookups in RemoveRelations */
    1711       19586 :     state->actual_relkind = classform->relkind;
    1712       19586 :     state->actual_relpersistence = classform->relpersistence;
    1713             : 
    1714             :     /*
    1715             :      * Both RELKIND_RELATION and RELKIND_PARTITIONED_TABLE are OBJECT_TABLE,
    1716             :      * but RemoveRelations() can only pass one relkind for a given relation.
    1717             :      * It chooses RELKIND_RELATION for both regular and partitioned tables.
    1718             :      * That means we must be careful before giving the wrong type error when
    1719             :      * the relation is RELKIND_PARTITIONED_TABLE.  An equivalent problem
    1720             :      * exists with indexes.
    1721             :      */
    1722       19586 :     if (classform->relkind == RELKIND_PARTITIONED_TABLE)
    1723        2832 :         expected_relkind = RELKIND_RELATION;
    1724       16754 :     else if (classform->relkind == RELKIND_PARTITIONED_INDEX)
    1725          84 :         expected_relkind = RELKIND_INDEX;
    1726             :     else
    1727       16670 :         expected_relkind = classform->relkind;
    1728             : 
    1729       19586 :     if (state->expected_relkind != expected_relkind)
    1730           0 :         DropErrorMsgWrongType(rel->relname, classform->relkind,
    1731           0 :                               state->expected_relkind);
    1732             : 
    1733             :     /* Allow DROP to either table owner or schema owner */
    1734       19586 :     if (!object_ownercheck(RelationRelationId, relOid, GetUserId()) &&
    1735          18 :         !object_ownercheck(NamespaceRelationId, classform->relnamespace, GetUserId()))
    1736          18 :         aclcheck_error(ACLCHECK_NOT_OWNER,
    1737          18 :                        get_relkind_objtype(classform->relkind),
    1738          18 :                        rel->relname);
    1739             : 
    1740             :     /*
    1741             :      * Check the case of a system index that might have been invalidated by a
    1742             :      * failed concurrent process and allow its drop. For the time being, this
    1743             :      * only concerns indexes of toast relations that became invalid during a
    1744             :      * REINDEX CONCURRENTLY process.
    1745             :      */
    1746       19568 :     if (IsSystemClass(relOid, classform) && classform->relkind == RELKIND_INDEX)
    1747             :     {
    1748             :         HeapTuple   locTuple;
    1749             :         Form_pg_index indexform;
    1750             :         bool        indisvalid;
    1751             : 
    1752           0 :         locTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(relOid));
    1753           0 :         if (!HeapTupleIsValid(locTuple))
    1754             :         {
    1755           0 :             ReleaseSysCache(tuple);
    1756           0 :             return;
    1757             :         }
    1758             : 
    1759           0 :         indexform = (Form_pg_index) GETSTRUCT(locTuple);
    1760           0 :         indisvalid = indexform->indisvalid;
    1761           0 :         ReleaseSysCache(locTuple);
    1762             : 
    1763             :         /* Mark object as being an invalid index of system catalogs */
    1764           0 :         if (!indisvalid)
    1765           0 :             invalid_system_index = true;
    1766             :     }
    1767             : 
    1768             :     /* In the case of an invalid index, it is fine to bypass this check */
    1769       19568 :     if (!invalid_system_index && !allowSystemTableMods && IsSystemClass(relOid, classform))
    1770           2 :         ereport(ERROR,
    1771             :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
    1772             :                  errmsg("permission denied: \"%s\" is a system catalog",
    1773             :                         rel->relname)));
    1774             : 
    1775       19566 :     ReleaseSysCache(tuple);
    1776             : 
    1777             :     /*
    1778             :      * In DROP INDEX, attempt to acquire lock on the parent table before
    1779             :      * locking the index.  index_drop() will need this anyway, and since
    1780             :      * regular queries lock tables before their indexes, we risk deadlock if
    1781             :      * we do it the other way around.  No error if we don't find a pg_index
    1782             :      * entry, though --- the relation may have been dropped.  Note that this
    1783             :      * code will execute for either plain or partitioned indexes.
    1784             :      */
    1785       19566 :     if (expected_relkind == RELKIND_INDEX &&
    1786             :         relOid != oldRelOid)
    1787             :     {
    1788         796 :         state->heapOid = IndexGetRelation(relOid, true);
    1789         796 :         if (OidIsValid(state->heapOid))
    1790         796 :             LockRelationOid(state->heapOid, heap_lockmode);
    1791             :     }
    1792             : 
    1793             :     /*
    1794             :      * Similarly, if the relation is a partition, we must acquire lock on its
    1795             :      * parent before locking the partition.  That's because queries lock the
    1796             :      * parent before its partitions, so we risk deadlock if we do it the other
    1797             :      * way around.
    1798             :      */
    1799       19566 :     if (is_partition && relOid != oldRelOid)
    1800             :     {
    1801         600 :         state->partParentOid = get_partition_parent(relOid, true);
    1802         600 :         if (OidIsValid(state->partParentOid))
    1803         600 :             LockRelationOid(state->partParentOid, AccessExclusiveLock);
    1804             :     }
    1805             : }
    1806             : 
    1807             : /*
    1808             :  * ExecuteTruncate
    1809             :  *      Executes a TRUNCATE command.
    1810             :  *
    1811             :  * This is a multi-relation truncate.  We first open and grab exclusive
    1812             :  * lock on all relations involved, checking permissions and otherwise
    1813             :  * verifying that the relation is OK for truncation.  Note that if relations
    1814             :  * are foreign tables, at this stage, we have not yet checked that their
    1815             :  * foreign data in external data sources are OK for truncation.  These are
    1816             :  * checked when foreign data are actually truncated later.  In CASCADE mode,
    1817             :  * relations having FK references to the targeted relations are automatically
    1818             :  * added to the group; in RESTRICT mode, we check that all FK references are
    1819             :  * internal to the group that's being truncated.  Finally all the relations
    1820             :  * are truncated and reindexed.
    1821             :  */
    1822             : void
    1823        1618 : ExecuteTruncate(TruncateStmt *stmt)
    1824             : {
    1825        1618 :     List       *rels = NIL;
    1826        1618 :     List       *relids = NIL;
    1827        1618 :     List       *relids_logged = NIL;
    1828             :     ListCell   *cell;
    1829             : 
    1830             :     /*
    1831             :      * Open, exclusive-lock, and check all the explicitly-specified relations
    1832             :      */
    1833        3484 :     foreach(cell, stmt->relations)
    1834             :     {
    1835        1916 :         RangeVar   *rv = lfirst(cell);
    1836             :         Relation    rel;
    1837        1916 :         bool        recurse = rv->inh;
    1838             :         Oid         myrelid;
    1839        1916 :         LOCKMODE    lockmode = AccessExclusiveLock;
    1840             : 
    1841        1916 :         myrelid = RangeVarGetRelidExtended(rv, lockmode,
    1842             :                                            0, RangeVarCallbackForTruncate,
    1843             :                                            NULL);
    1844             : 
    1845             :         /* don't throw error for "TRUNCATE foo, foo" */
    1846        1878 :         if (list_member_oid(relids, myrelid))
    1847           2 :             continue;
    1848             : 
    1849             :         /* open the relation, we already hold a lock on it */
    1850        1876 :         rel = table_open(myrelid, NoLock);
    1851             : 
    1852             :         /*
    1853             :          * RangeVarGetRelidExtended() has done most checks with its callback,
    1854             :          * but other checks with the now-opened Relation remain.
    1855             :          */
    1856        1876 :         truncate_check_activity(rel);
    1857             : 
    1858        1876 :         rels = lappend(rels, rel);
    1859        1876 :         relids = lappend_oid(relids, myrelid);
    1860             : 
    1861             :         /* Log this relation only if needed for logical decoding */
    1862        1876 :         if (RelationIsLogicallyLogged(rel))
    1863          68 :             relids_logged = lappend_oid(relids_logged, myrelid);
    1864             : 
    1865        1876 :         if (recurse)
    1866             :         {
    1867             :             ListCell   *child;
    1868             :             List       *children;
    1869             : 
    1870        1814 :             children = find_all_inheritors(myrelid, lockmode, NULL);
    1871             : 
    1872        5456 :             foreach(child, children)
    1873             :             {
    1874        3642 :                 Oid         childrelid = lfirst_oid(child);
    1875             : 
    1876        3642 :                 if (list_member_oid(relids, childrelid))
    1877        1814 :                     continue;
    1878             : 
    1879             :                 /* find_all_inheritors already got lock */
    1880        1828 :                 rel = table_open(childrelid, NoLock);
    1881             : 
    1882             :                 /*
    1883             :                  * It is possible that the parent table has children that are
    1884             :                  * temp tables of other backends.  We cannot safely access
    1885             :                  * such tables (because of buffering issues), and the best
    1886             :                  * thing to do is to silently ignore them.  Note that this
    1887             :                  * check is the same as one of the checks done in
    1888             :                  * truncate_check_activity() called below, still it is kept
    1889             :                  * here for simplicity.
    1890             :                  */
    1891        1828 :                 if (RELATION_IS_OTHER_TEMP(rel))
    1892             :                 {
    1893           8 :                     table_close(rel, lockmode);
    1894           8 :                     continue;
    1895             :                 }
    1896             : 
    1897             :                 /*
    1898             :                  * Inherited TRUNCATE commands perform access permission
    1899             :                  * checks on the parent table only. So we skip checking the
    1900             :                  * children's permissions and don't call
    1901             :                  * truncate_check_perms() here.
    1902             :                  */
    1903        1820 :                 truncate_check_rel(RelationGetRelid(rel), rel->rd_rel);
    1904        1820 :                 truncate_check_activity(rel);
    1905             : 
    1906        1820 :                 rels = lappend(rels, rel);
    1907        1820 :                 relids = lappend_oid(relids, childrelid);
    1908             : 
    1909             :                 /* Log this relation only if needed for logical decoding */
    1910        1820 :                 if (RelationIsLogicallyLogged(rel))
    1911          22 :                     relids_logged = lappend_oid(relids_logged, childrelid);
    1912             :             }
    1913             :         }
    1914          62 :         else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
    1915          12 :             ereport(ERROR,
    1916             :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    1917             :                      errmsg("cannot truncate only a partitioned table"),
    1918             :                      errhint("Do not specify the ONLY keyword, or use TRUNCATE ONLY on the partitions directly.")));
    1919             :     }
    1920             : 
    1921        1568 :     ExecuteTruncateGuts(rels, relids, relids_logged,
    1922        1568 :                         stmt->behavior, stmt->restart_seqs, false);
    1923             : 
    1924             :     /* And close the rels */
    1925        5016 :     foreach(cell, rels)
    1926             :     {
    1927        3530 :         Relation    rel = (Relation) lfirst(cell);
    1928             : 
    1929        3530 :         table_close(rel, NoLock);
    1930             :     }
    1931        1486 : }
    1932             : 
    1933             : /*
    1934             :  * ExecuteTruncateGuts
    1935             :  *
    1936             :  * Internal implementation of TRUNCATE.  This is called by the actual TRUNCATE
    1937             :  * command (see above) as well as replication subscribers that execute a
    1938             :  * replicated TRUNCATE action.
    1939             :  *
    1940             :  * explicit_rels is the list of Relations to truncate that the command
    1941             :  * specified.  relids is the list of Oids corresponding to explicit_rels.
    1942             :  * relids_logged is the list of Oids (a subset of relids) that require
    1943             :  * WAL-logging.  This is all a bit redundant, but the existing callers have
    1944             :  * this information handy in this form.
    1945             :  */
    1946             : void
    1947        1606 : ExecuteTruncateGuts(List *explicit_rels,
    1948             :                     List *relids,
    1949             :                     List *relids_logged,
    1950             :                     DropBehavior behavior, bool restart_seqs,
    1951             :                     bool run_as_table_owner)
    1952             : {
    1953             :     List       *rels;
    1954        1606 :     List       *seq_relids = NIL;
    1955        1606 :     HTAB       *ft_htab = NULL;
    1956             :     EState     *estate;
    1957             :     ResultRelInfo *resultRelInfos;
    1958             :     ResultRelInfo *resultRelInfo;
    1959             :     SubTransactionId mySubid;
    1960             :     ListCell   *cell;
    1961             :     Oid        *logrelids;
    1962             : 
    1963             :     /*
    1964             :      * Check the explicitly-specified relations.
    1965             :      *
    1966             :      * In CASCADE mode, suck in all referencing relations as well.  This
    1967             :      * requires multiple iterations to find indirectly-dependent relations. At
    1968             :      * each phase, we need to exclusive-lock new rels before looking for their
    1969             :      * dependencies, else we might miss something.  Also, we check each rel as
    1970             :      * soon as we open it, to avoid a faux pas such as holding lock for a long
    1971             :      * time on a rel we have no permissions for.
    1972             :      */
    1973        1606 :     rels = list_copy(explicit_rels);
    1974        1606 :     if (behavior == DROP_CASCADE)
    1975             :     {
    1976             :         for (;;)
    1977          40 :         {
    1978             :             List       *newrelids;
    1979             : 
    1980          80 :             newrelids = heap_truncate_find_FKs(relids);
    1981          80 :             if (newrelids == NIL)
    1982          40 :                 break;          /* nothing else to add */
    1983             : 
    1984         134 :             foreach(cell, newrelids)
    1985             :             {
    1986          94 :                 Oid         relid = lfirst_oid(cell);
    1987             :                 Relation    rel;
    1988             : 
    1989          94 :                 rel = table_open(relid, AccessExclusiveLock);
    1990          94 :                 ereport(NOTICE,
    1991             :                         (errmsg("truncate cascades to table \"%s\"",
    1992             :                                 RelationGetRelationName(rel))));
    1993          94 :                 truncate_check_rel(relid, rel->rd_rel);
    1994          94 :                 truncate_check_perms(relid, rel->rd_rel);
    1995          94 :                 truncate_check_activity(rel);
    1996          94 :                 rels = lappend(rels, rel);
    1997          94 :                 relids = lappend_oid(relids, relid);
    1998             : 
    1999             :                 /* Log this relation only if needed for logical decoding */
    2000          94 :                 if (RelationIsLogicallyLogged(rel))
    2001           0 :                     relids_logged = lappend_oid(relids_logged, relid);
    2002             :             }
    2003             :         }
    2004             :     }
    2005             : 
    2006             :     /*
    2007             :      * Check foreign key references.  In CASCADE mode, this should be
    2008             :      * unnecessary since we just pulled in all the references; but as a
    2009             :      * cross-check, do it anyway if in an Assert-enabled build.
    2010             :      */
    2011             : #ifdef USE_ASSERT_CHECKING
    2012             :     heap_truncate_check_FKs(rels, false);
    2013             : #else
    2014        1606 :     if (behavior == DROP_RESTRICT)
    2015        1566 :         heap_truncate_check_FKs(rels, false);
    2016             : #endif
    2017             : 
    2018             :     /*
    2019             :      * If we are asked to restart sequences, find all the sequences, lock them
    2020             :      * (we need AccessExclusiveLock for ResetSequence), and check permissions.
    2021             :      * We want to do this early since it's pointless to do all the truncation
    2022             :      * work only to fail on sequence permissions.
    2023             :      */
    2024        1532 :     if (restart_seqs)
    2025             :     {
    2026          52 :         foreach(cell, rels)
    2027             :         {
    2028          26 :             Relation    rel = (Relation) lfirst(cell);
    2029          26 :             List       *seqlist = getOwnedSequences(RelationGetRelid(rel));
    2030             :             ListCell   *seqcell;
    2031             : 
    2032          62 :             foreach(seqcell, seqlist)
    2033             :             {
    2034          36 :                 Oid         seq_relid = lfirst_oid(seqcell);
    2035             :                 Relation    seq_rel;
    2036             : 
    2037          36 :                 seq_rel = relation_open(seq_relid, AccessExclusiveLock);
    2038             : 
    2039             :                 /* This check must match AlterSequence! */
    2040          36 :                 if (!object_ownercheck(RelationRelationId, seq_relid, GetUserId()))
    2041           0 :                     aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_SEQUENCE,
    2042           0 :                                    RelationGetRelationName(seq_rel));
    2043             : 
    2044          36 :                 seq_relids = lappend_oid(seq_relids, seq_relid);
    2045             : 
    2046          36 :                 relation_close(seq_rel, NoLock);
    2047             :             }
    2048             :         }
    2049             :     }
    2050             : 
    2051             :     /* Prepare to catch AFTER triggers. */
    2052        1532 :     AfterTriggerBeginQuery();
    2053             : 
    2054             :     /*
    2055             :      * To fire triggers, we'll need an EState as well as a ResultRelInfo for
    2056             :      * each relation.  We don't need to call ExecOpenIndices, though.
    2057             :      *
    2058             :      * We put the ResultRelInfos in the es_opened_result_relations list, even
    2059             :      * though we don't have a range table and don't populate the
    2060             :      * es_result_relations array.  That's a bit bogus, but it's enough to make
    2061             :      * ExecGetTriggerResultRel() find them.
    2062             :      */
    2063        1532 :     estate = CreateExecutorState();
    2064             :     resultRelInfos = (ResultRelInfo *)
    2065        1532 :         palloc(list_length(rels) * sizeof(ResultRelInfo));
    2066        1532 :     resultRelInfo = resultRelInfos;
    2067        5234 :     foreach(cell, rels)
    2068             :     {
    2069        3702 :         Relation    rel = (Relation) lfirst(cell);
    2070             : 
    2071        3702 :         InitResultRelInfo(resultRelInfo,
    2072             :                           rel,
    2073             :                           0,    /* dummy rangetable index */
    2074             :                           NULL,
    2075             :                           0);
    2076        3702 :         estate->es_opened_result_relations =
    2077        3702 :             lappend(estate->es_opened_result_relations, resultRelInfo);
    2078        3702 :         resultRelInfo++;
    2079             :     }
    2080             : 
    2081             :     /*
    2082             :      * Process all BEFORE STATEMENT TRUNCATE triggers before we begin
    2083             :      * truncating (this is because one of them might throw an error). Also, if
    2084             :      * we were to allow them to prevent statement execution, that would need
    2085             :      * to be handled here.
    2086             :      */
    2087        1532 :     resultRelInfo = resultRelInfos;
    2088        5234 :     foreach(cell, rels)
    2089             :     {
    2090             :         UserContext ucxt;
    2091             : 
    2092        3702 :         if (run_as_table_owner)
    2093          70 :             SwitchToUntrustedUser(resultRelInfo->ri_RelationDesc->rd_rel->relowner,
    2094             :                                   &ucxt);
    2095        3702 :         ExecBSTruncateTriggers(estate, resultRelInfo);
    2096        3702 :         if (run_as_table_owner)
    2097          70 :             RestoreUserContext(&ucxt);
    2098        3702 :         resultRelInfo++;
    2099             :     }
    2100             : 
    2101             :     /*
    2102             :      * OK, truncate each table.
    2103             :      */
    2104        1532 :     mySubid = GetCurrentSubTransactionId();
    2105             : 
    2106        5234 :     foreach(cell, rels)
    2107             :     {
    2108        3702 :         Relation    rel = (Relation) lfirst(cell);
    2109             : 
    2110             :         /* Skip partitioned tables as there is nothing to do */
    2111        3702 :         if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
    2112         740 :             continue;
    2113             : 
    2114             :         /*
    2115             :          * Build the lists of foreign tables belonging to each foreign server
    2116             :          * and pass each list to the foreign data wrapper's callback function,
    2117             :          * so that each server can truncate its all foreign tables in bulk.
    2118             :          * Each list is saved as a single entry in a hash table that uses the
    2119             :          * server OID as lookup key.
    2120             :          */
    2121        2962 :         if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
    2122             :         {
    2123          34 :             Oid         serverid = GetForeignServerIdByRelId(RelationGetRelid(rel));
    2124             :             bool        found;
    2125             :             ForeignTruncateInfo *ft_info;
    2126             : 
    2127             :             /* First time through, initialize hashtable for foreign tables */
    2128          34 :             if (!ft_htab)
    2129             :             {
    2130             :                 HASHCTL     hctl;
    2131             : 
    2132          30 :                 memset(&hctl, 0, sizeof(HASHCTL));
    2133          30 :                 hctl.keysize = sizeof(Oid);
    2134          30 :                 hctl.entrysize = sizeof(ForeignTruncateInfo);
    2135          30 :                 hctl.hcxt = CurrentMemoryContext;
    2136             : 
    2137          30 :                 ft_htab = hash_create("TRUNCATE for Foreign Tables",
    2138             :                                       32,   /* start small and extend */
    2139             :                                       &hctl,
    2140             :                                       HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);
    2141             :             }
    2142             : 
    2143             :             /* Find or create cached entry for the foreign table */
    2144          34 :             ft_info = hash_search(ft_htab, &serverid, HASH_ENTER, &found);
    2145          34 :             if (!found)
    2146          30 :                 ft_info->rels = NIL;
    2147             : 
    2148             :             /*
    2149             :              * Save the foreign table in the entry of the server that the
    2150             :              * foreign table belongs to.
    2151             :              */
    2152          34 :             ft_info->rels = lappend(ft_info->rels, rel);
    2153          34 :             continue;
    2154             :         }
    2155             : 
    2156             :         /*
    2157             :          * Normally, we need a transaction-safe truncation here.  However, if
    2158             :          * the table was either created in the current (sub)transaction or has
    2159             :          * a new relfilenumber in the current (sub)transaction, then we can
    2160             :          * just truncate it in-place, because a rollback would cause the whole
    2161             :          * table or the current physical file to be thrown away anyway.
    2162             :          */
    2163        2928 :         if (rel->rd_createSubid == mySubid ||
    2164        2902 :             rel->rd_newRelfilelocatorSubid == mySubid)
    2165             :         {
    2166             :             /* Immediate, non-rollbackable truncation is OK */
    2167          90 :             heap_truncate_one_rel(rel);
    2168             :         }
    2169             :         else
    2170             :         {
    2171             :             Oid         heap_relid;
    2172             :             Oid         toast_relid;
    2173        2838 :             ReindexParams reindex_params = {0};
    2174             : 
    2175             :             /*
    2176             :              * This effectively deletes all rows in the table, and may be done
    2177             :              * in a serializable transaction.  In that case we must record a
    2178             :              * rw-conflict in to this transaction from each transaction
    2179             :              * holding a predicate lock on the table.
    2180             :              */
    2181        2838 :             CheckTableForSerializableConflictIn(rel);
    2182             : 
    2183             :             /*
    2184             :              * Need the full transaction-safe pushups.
    2185             :              *
    2186             :              * Create a new empty storage file for the relation, and assign it
    2187             :              * as the relfilenumber value. The old storage file is scheduled
    2188             :              * for deletion at commit.
    2189             :              */
    2190        2838 :             RelationSetNewRelfilenumber(rel, rel->rd_rel->relpersistence);
    2191             : 
    2192        2838 :             heap_relid = RelationGetRelid(rel);
    2193             : 
    2194             :             /*
    2195             :              * The same for the toast table, if any.
    2196             :              */
    2197        2838 :             toast_relid = rel->rd_rel->reltoastrelid;
    2198        2838 :             if (OidIsValid(toast_relid))
    2199             :             {
    2200        1842 :                 Relation    toastrel = relation_open(toast_relid,
    2201             :                                                      AccessExclusiveLock);
    2202             : 
    2203        1842 :                 RelationSetNewRelfilenumber(toastrel,
    2204        1842 :                                             toastrel->rd_rel->relpersistence);
    2205        1842 :                 table_close(toastrel, NoLock);
    2206             :             }
    2207             : 
    2208             :             /*
    2209             :              * Reconstruct the indexes to match, and we're done.
    2210             :              */
    2211        2838 :             reindex_relation(NULL, heap_relid, REINDEX_REL_PROCESS_TOAST,
    2212             :                              &reindex_params);
    2213             :         }
    2214             : 
    2215        2928 :         pgstat_count_truncate(rel);
    2216             :     }
    2217             : 
    2218             :     /* Now go through the hash table, and truncate foreign tables */
    2219        1532 :     if (ft_htab)
    2220             :     {
    2221             :         ForeignTruncateInfo *ft_info;
    2222             :         HASH_SEQ_STATUS seq;
    2223             : 
    2224          30 :         hash_seq_init(&seq, ft_htab);
    2225             : 
    2226          30 :         PG_TRY();
    2227             :         {
    2228          52 :             while ((ft_info = hash_seq_search(&seq)) != NULL)
    2229             :             {
    2230          30 :                 FdwRoutine *routine = GetFdwRoutineByServerId(ft_info->serverid);
    2231             : 
    2232             :                 /* truncate_check_rel() has checked that already */
    2233             :                 Assert(routine->ExecForeignTruncate != NULL);
    2234             : 
    2235          30 :                 routine->ExecForeignTruncate(ft_info->rels,
    2236             :                                              behavior,
    2237             :                                              restart_seqs);
    2238             :             }
    2239             :         }
    2240           8 :         PG_FINALLY();
    2241             :         {
    2242          30 :             hash_destroy(ft_htab);
    2243             :         }
    2244          30 :         PG_END_TRY();
    2245             :     }
    2246             : 
    2247             :     /*
    2248             :      * Restart owned sequences if we were asked to.
    2249             :      */
    2250        1560 :     foreach(cell, seq_relids)
    2251             :     {
    2252          36 :         Oid         seq_relid = lfirst_oid(cell);
    2253             : 
    2254          36 :         ResetSequence(seq_relid);
    2255             :     }
    2256             : 
    2257             :     /*
    2258             :      * Write a WAL record to allow this set of actions to be logically
    2259             :      * decoded.
    2260             :      *
    2261             :      * Assemble an array of relids so we can write a single WAL record for the
    2262             :      * whole action.
    2263             :      */
    2264        1524 :     if (relids_logged != NIL)
    2265             :     {
    2266             :         xl_heap_truncate xlrec;
    2267          54 :         int         i = 0;
    2268             : 
    2269             :         /* should only get here if wal_level >= logical */
    2270             :         Assert(XLogLogicalInfoActive());
    2271             : 
    2272          54 :         logrelids = palloc(list_length(relids_logged) * sizeof(Oid));
    2273         144 :         foreach(cell, relids_logged)
    2274          90 :             logrelids[i++] = lfirst_oid(cell);
    2275             : 
    2276          54 :         xlrec.dbId = MyDatabaseId;
    2277          54 :         xlrec.nrelids = list_length(relids_logged);
    2278          54 :         xlrec.flags = 0;
    2279          54 :         if (behavior == DROP_CASCADE)
    2280           2 :             xlrec.flags |= XLH_TRUNCATE_CASCADE;
    2281          54 :         if (restart_seqs)
    2282           4 :             xlrec.flags |= XLH_TRUNCATE_RESTART_SEQS;
    2283             : 
    2284          54 :         XLogBeginInsert();
    2285          54 :         XLogRegisterData((char *) &xlrec, SizeOfHeapTruncate);
    2286          54 :         XLogRegisterData((char *) logrelids, list_length(relids_logged) * sizeof(Oid));
    2287             : 
    2288          54 :         XLogSetRecordFlags(XLOG_INCLUDE_ORIGIN);
    2289             : 
    2290          54 :         (void) XLogInsert(RM_HEAP_ID, XLOG_HEAP_TRUNCATE);
    2291             :     }
    2292             : 
    2293             :     /*
    2294             :      * Process all AFTER STATEMENT TRUNCATE triggers.
    2295             :      */
    2296        1524 :     resultRelInfo = resultRelInfos;
    2297        5218 :     foreach(cell, rels)
    2298             :     {
    2299             :         UserContext ucxt;
    2300             : 
    2301        3694 :         if (run_as_table_owner)
    2302          70 :             SwitchToUntrustedUser(resultRelInfo->ri_RelationDesc->rd_rel->relowner,
    2303             :                                   &ucxt);
    2304        3694 :         ExecASTruncateTriggers(estate, resultRelInfo);
    2305        3694 :         if (run_as_table_owner)
    2306          70 :             RestoreUserContext(&ucxt);
    2307        3694 :         resultRelInfo++;
    2308             :     }
    2309             : 
    2310             :     /* Handle queued AFTER triggers */
    2311        1524 :     AfterTriggerEndQuery(estate);
    2312             : 
    2313             :     /* We can clean up the EState now */
    2314        1524 :     FreeExecutorState(estate);
    2315             : 
    2316             :     /*
    2317             :      * Close any rels opened by CASCADE (can't do this while EState still
    2318             :      * holds refs)
    2319             :      */
    2320        1524 :     rels = list_difference_ptr(rels, explicit_rels);
    2321        1618 :     foreach(cell, rels)
    2322             :     {
    2323          94 :         Relation    rel = (Relation) lfirst(cell);
    2324             : 
    2325          94 :         table_close(rel, NoLock);
    2326             :     }
    2327        1524 : }
    2328             : 
    2329             : /*
    2330             :  * Check that a given relation is safe to truncate.  Subroutine for
    2331             :  * ExecuteTruncate() and RangeVarCallbackForTruncate().
    2332             :  */
    2333             : static void
    2334        3966 : truncate_check_rel(Oid relid, Form_pg_class reltuple)
    2335             : {
    2336        3966 :     char       *relname = NameStr(reltuple->relname);
    2337             : 
    2338             :     /*
    2339             :      * Only allow truncate on regular tables, foreign tables using foreign
    2340             :      * data wrappers supporting TRUNCATE and partitioned tables (although, the
    2341             :      * latter are only being included here for the following checks; no
    2342             :      * physical truncation will occur in their case.).
    2343             :      */
    2344        3966 :     if (reltuple->relkind == RELKIND_FOREIGN_TABLE)
    2345             :     {
    2346          38 :         Oid         serverid = GetForeignServerIdByRelId(relid);
    2347          38 :         FdwRoutine *fdwroutine = GetFdwRoutineByServerId(serverid);
    2348             : 
    2349          36 :         if (!fdwroutine->ExecForeignTruncate)
    2350           2 :             ereport(ERROR,
    2351             :                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    2352             :                      errmsg("cannot truncate foreign table \"%s\"",
    2353             :                             relname)));
    2354             :     }
    2355        3928 :     else if (reltuple->relkind != RELKIND_RELATION &&
    2356         756 :              reltuple->relkind != RELKIND_PARTITIONED_TABLE)
    2357           0 :         ereport(ERROR,
    2358             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    2359             :                  errmsg("\"%s\" is not a table", relname)));
    2360             : 
    2361             :     /*
    2362             :      * Most system catalogs can't be truncated at all, or at least not unless
    2363             :      * allow_system_table_mods=on. As an exception, however, we allow
    2364             :      * pg_largeobject to be truncated as part of pg_upgrade, because we need
    2365             :      * to change its relfilenode to match the old cluster, and allowing a
    2366             :      * TRUNCATE command to be executed is the easiest way of doing that.
    2367             :      */
    2368        3962 :     if (!allowSystemTableMods && IsSystemClass(relid, reltuple)
    2369          22 :         && (!IsBinaryUpgrade || relid != LargeObjectRelationId))
    2370           2 :         ereport(ERROR,
    2371             :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
    2372             :                  errmsg("permission denied: \"%s\" is a system catalog",
    2373             :                         relname)));
    2374             : 
    2375        3960 :     InvokeObjectTruncateHook(relid);
    2376        3960 : }
    2377             : 
    2378             : /*
    2379             :  * Check that current user has the permission to truncate given relation.
    2380             :  */
    2381             : static void
    2382        2140 : truncate_check_perms(Oid relid, Form_pg_class reltuple)
    2383             : {
    2384        2140 :     char       *relname = NameStr(reltuple->relname);
    2385             :     AclResult   aclresult;
    2386             : 
    2387             :     /* Permissions checks */
    2388        2140 :     aclresult = pg_class_aclcheck(relid, GetUserId(), ACL_TRUNCATE);
    2389        2140 :     if (aclresult != ACLCHECK_OK)
    2390          32 :         aclcheck_error(aclresult, get_relkind_objtype(reltuple->relkind),
    2391             :                        relname);
    2392        2108 : }
    2393             : 
    2394             : /*
    2395             :  * Set of extra sanity checks to check if a given relation is safe to
    2396             :  * truncate.  This is split with truncate_check_rel() as
    2397             :  * RangeVarCallbackForTruncate() cannot open a Relation yet.
    2398             :  */
    2399             : static void
    2400        3790 : truncate_check_activity(Relation rel)
    2401             : {
    2402             :     /*
    2403             :      * Don't allow truncate on temp tables of other backends ... their local
    2404             :      * buffer manager is not going to cope.
    2405             :      */
    2406        3790 :     if (RELATION_IS_OTHER_TEMP(rel))
    2407           0 :         ereport(ERROR,
    2408             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    2409             :                  errmsg("cannot truncate temporary tables of other sessions")));
    2410             : 
    2411             :     /*
    2412             :      * Also check for active uses of the relation in the current transaction,
    2413             :      * including open scans and pending AFTER trigger events.
    2414             :      */
    2415        3790 :     CheckTableNotInUse(rel, "TRUNCATE");
    2416        3790 : }
    2417             : 
    2418             : /*
    2419             :  * storage_name
    2420             :  *    returns the name corresponding to a typstorage/attstorage enum value
    2421             :  */
    2422             : static const char *
    2423          24 : storage_name(char c)
    2424             : {
    2425          24 :     switch (c)
    2426             :     {
    2427           0 :         case TYPSTORAGE_PLAIN:
    2428           0 :             return "PLAIN";
    2429           0 :         case TYPSTORAGE_EXTERNAL:
    2430           0 :             return "EXTERNAL";
    2431          12 :         case TYPSTORAGE_EXTENDED:
    2432          12 :             return "EXTENDED";
    2433          12 :         case TYPSTORAGE_MAIN:
    2434          12 :             return "MAIN";
    2435           0 :         default:
    2436           0 :             return "???";
    2437             :     }
    2438             : }
    2439             : 
    2440             : /*----------
    2441             :  * MergeAttributes
    2442             :  *      Returns new schema given initial schema and superclasses.
    2443             :  *
    2444             :  * Input arguments:
    2445             :  * 'columns' is the column/attribute definition for the table. (It's a list
    2446             :  *      of ColumnDef's.) It is destructively changed.
    2447             :  * 'supers' is a list of OIDs of parent relations, already locked by caller.
    2448             :  * 'relpersistence' is the persistence type of the table.
    2449             :  * 'is_partition' tells if the table is a partition.
    2450             :  *
    2451             :  * Output arguments:
    2452             :  * 'supconstr' receives a list of CookedConstraint representing
    2453             :  *      CHECK constraints belonging to parent relations, updated as
    2454             :  *      necessary to be valid for the child.
    2455             :  * 'supnotnulls' receives a list of CookedConstraint representing
    2456             :  *      not-null constraints based on those from parent relations.
    2457             :  *
    2458             :  * Return value:
    2459             :  * Completed schema list.
    2460             :  *
    2461             :  * Notes:
    2462             :  *    The order in which the attributes are inherited is very important.
    2463             :  *    Intuitively, the inherited attributes should come first. If a table
    2464             :  *    inherits from multiple parents, the order of those attributes are
    2465             :  *    according to the order of the parents specified in CREATE TABLE.
    2466             :  *
    2467             :  *    Here's an example:
    2468             :  *
    2469             :  *      create table person (name text, age int4, location point);
    2470             :  *      create table emp (salary int4, manager text) inherits(person);
    2471             :  *      create table student (gpa float8) inherits (person);
    2472             :  *      create table stud_emp (percent int4) inherits (emp, student);
    2473             :  *
    2474             :  *    The order of the attributes of stud_emp is:
    2475             :  *
    2476             :  *                          person {1:name, 2:age, 3:location}
    2477             :  *                          /    \
    2478             :  *             {6:gpa}  student   emp {4:salary, 5:manager}
    2479             :  *                          \    /
    2480             :  *                         stud_emp {7:percent}
    2481             :  *
    2482             :  *     If the same attribute name appears multiple times, then it appears
    2483             :  *     in the result table in the proper location for its first appearance.
    2484             :  *
    2485             :  *     Constraints (including not-null constraints) for the child table
    2486             :  *     are the union of all relevant constraints, from both the child schema
    2487             :  *     and parent tables.  In addition, in legacy inheritance, each column that
    2488             :  *     appears in a primary key in any of the parents also gets a NOT NULL
    2489             :  *     constraint (partitioning doesn't need this, because the PK itself gets
    2490             :  *     inherited.)
    2491             :  *
    2492             :  *     The default value for a child column is defined as:
    2493             :  *      (1) If the child schema specifies a default, that value is used.
    2494             :  *      (2) If neither the child nor any parent specifies a default, then
    2495             :  *          the column will not have a default.
    2496             :  *      (3) If conflicting defaults are inherited from different parents
    2497             :  *          (and not overridden by the child), an error is raised.
    2498             :  *      (4) Otherwise the inherited default is used.
    2499             :  *
    2500             :  *      Note that the default-value infrastructure is used for generated
    2501             :  *      columns' expressions too, so most of the preceding paragraph applies
    2502             :  *      to generation expressions too.  We insist that a child column be
    2503             :  *      generated if and only if its parent(s) are, but it need not have
    2504             :  *      the same generation expression.
    2505             :  *----------
    2506             :  */
    2507             : static List *
    2508       57124 : MergeAttributes(List *columns, const List *supers, char relpersistence,
    2509             :                 bool is_partition, List **supconstr, List **supnotnulls)
    2510             : {
    2511       57124 :     List       *inh_columns = NIL;
    2512       57124 :     List       *constraints = NIL;
    2513       57124 :     List       *nnconstraints = NIL;
    2514       57124 :     bool        have_bogus_defaults = false;
    2515             :     int         child_attno;
    2516             :     static Node bogus_marker = {0}; /* marks conflicting defaults */
    2517       57124 :     List       *saved_columns = NIL;
    2518             :     ListCell   *lc;
    2519             : 
    2520             :     /*
    2521             :      * Check for and reject tables with too many columns. We perform this
    2522             :      * check relatively early for two reasons: (a) we don't run the risk of
    2523             :      * overflowing an AttrNumber in subsequent code (b) an O(n^2) algorithm is
    2524             :      * okay if we're processing <= 1600 columns, but could take minutes to
    2525             :      * execute if the user attempts to create a table with hundreds of
    2526             :      * thousands of columns.
    2527             :      *
    2528             :      * Note that we also need to check that we do not exceed this figure after
    2529             :      * including columns from inherited relations.
    2530             :      */
    2531       57124 :     if (list_length(columns) > MaxHeapAttributeNumber)
    2532           0 :         ereport(ERROR,
    2533             :                 (errcode(ERRCODE_TOO_MANY_COLUMNS),
    2534             :                  errmsg("tables can have at most %d columns",
    2535             :                         MaxHeapAttributeNumber)));
    2536             : 
    2537             :     /*
    2538             :      * Check for duplicate names in the explicit list of attributes.
    2539             :      *
    2540             :      * Although we might consider merging such entries in the same way that we
    2541             :      * handle name conflicts for inherited attributes, it seems to make more
    2542             :      * sense to assume such conflicts are errors.
    2543             :      *
    2544             :      * We don't use foreach() here because we have two nested loops over the
    2545             :      * columns list, with possible element deletions in the inner one.  If we
    2546             :      * used foreach_delete_current() it could only fix up the state of one of
    2547             :      * the loops, so it seems cleaner to use looping over list indexes for
    2548             :      * both loops.  Note that any deletion will happen beyond where the outer
    2549             :      * loop is, so its index never needs adjustment.
    2550             :      */
    2551      267182 :     for (int coldefpos = 0; coldefpos < list_length(columns); coldefpos++)
    2552             :     {
    2553      210082 :         ColumnDef  *coldef = list_nth_node(ColumnDef, columns, coldefpos);
    2554             : 
    2555      210082 :         if (!is_partition && coldef->typeName == NULL)
    2556             :         {
    2557             :             /*
    2558             :              * Typed table column option that does not belong to a column from
    2559             :              * the type.  This works because the columns from the type come
    2560             :              * first in the list.  (We omit this check for partition column
    2561             :              * lists; those are processed separately below.)
    2562             :              */
    2563           6 :             ereport(ERROR,
    2564             :                     (errcode(ERRCODE_UNDEFINED_COLUMN),
    2565             :                      errmsg("column \"%s\" does not exist",
    2566             :                             coldef->colname)));
    2567             :         }
    2568             : 
    2569             :         /* restpos scans all entries beyond coldef; incr is in loop body */
    2570     6333472 :         for (int restpos = coldefpos + 1; restpos < list_length(columns);)
    2571             :         {
    2572     6123414 :             ColumnDef  *restdef = list_nth_node(ColumnDef, columns, restpos);
    2573             : 
    2574     6123414 :             if (strcmp(coldef->colname, restdef->colname) == 0)
    2575             :             {
    2576          50 :                 if (coldef->is_from_type)
    2577             :                 {
    2578             :                     /*
    2579             :                      * merge the column options into the column from the type
    2580             :                      */
    2581          32 :                     coldef->is_not_null = restdef->is_not_null;
    2582          32 :                     coldef->raw_default = restdef->raw_default;
    2583          32 :                     coldef->cooked_default = restdef->cooked_default;
    2584          32 :                     coldef->constraints = restdef->constraints;
    2585          32 :                     coldef->is_from_type = false;
    2586          32 :                     columns = list_delete_nth_cell(columns, restpos);
    2587             :                 }
    2588             :                 else
    2589          18 :                     ereport(ERROR,
    2590             :                             (errcode(ERRCODE_DUPLICATE_COLUMN),
    2591             :                              errmsg("column \"%s\" specified more than once",
    2592             :                                     coldef->colname)));
    2593             :             }
    2594             :             else
    2595     6123364 :                 restpos++;
    2596             :         }
    2597             :     }
    2598             : 
    2599             :     /*
    2600             :      * In case of a partition, there are no new column definitions, only dummy
    2601             :      * ColumnDefs created for column constraints.  Set them aside for now and
    2602             :      * process them at the end.
    2603             :      */
    2604       57100 :     if (is_partition)
    2605             :     {
    2606        7760 :         saved_columns = columns;
    2607        7760 :         columns = NIL;
    2608             :     }
    2609             : 
    2610             :     /*
    2611             :      * Scan the parents left-to-right, and merge their attributes to form a
    2612             :      * list of inherited columns (inh_columns).
    2613             :      */
    2614       57100 :     child_attno = 0;
    2615       67004 :     foreach(lc, supers)
    2616             :     {
    2617        9976 :         Oid         parent = lfirst_oid(lc);
    2618             :         Relation    relation;
    2619             :         TupleDesc   tupleDesc;
    2620             :         TupleConstr *constr;
    2621             :         AttrMap    *newattmap;
    2622             :         List       *inherited_defaults;
    2623             :         List       *cols_with_defaults;
    2624             :         List       *nnconstrs;
    2625             :         ListCell   *lc1;
    2626             :         ListCell   *lc2;
    2627        9976 :         Bitmapset  *nncols = NULL;
    2628             : 
    2629             :         /* caller already got lock */
    2630        9976 :         relation = table_open(parent, NoLock);
    2631             : 
    2632             :         /*
    2633             :          * Check for active uses of the parent partitioned table in the
    2634             :          * current transaction, such as being used in some manner by an
    2635             :          * enclosing command.
    2636             :          */
    2637        9976 :         if (is_partition)
    2638        7760 :             CheckTableNotInUse(relation, "CREATE TABLE .. PARTITION OF");
    2639             : 
    2640             :         /*
    2641             :          * We do not allow partitioned tables and partitions to participate in
    2642             :          * regular inheritance.
    2643             :          */
    2644        9970 :         if (relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && !is_partition)
    2645           6 :             ereport(ERROR,
    2646             :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    2647             :                      errmsg("cannot inherit from partitioned table \"%s\"",
    2648             :                             RelationGetRelationName(relation))));
    2649        9964 :         if (relation->rd_rel->relispartition && !is_partition)
    2650           6 :             ereport(ERROR,
    2651             :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    2652             :                      errmsg("cannot inherit from partition \"%s\"",
    2653             :                             RelationGetRelationName(relation))));
    2654             : 
    2655        9958 :         if (relation->rd_rel->relkind != RELKIND_RELATION &&
    2656        7756 :             relation->rd_rel->relkind != RELKIND_FOREIGN_TABLE &&
    2657        7736 :             relation->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
    2658           0 :             ereport(ERROR,
    2659             :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    2660             :                      errmsg("inherited relation \"%s\" is not a table or foreign table",
    2661             :                             RelationGetRelationName(relation))));
    2662             : 
    2663             :         /*
    2664             :          * If the parent is permanent, so must be all of its partitions.  Note
    2665             :          * that inheritance allows that case.
    2666             :          */
    2667        9958 :         if (is_partition &&
    2668        7754 :             relation->rd_rel->relpersistence != RELPERSISTENCE_TEMP &&
    2669             :             relpersistence == RELPERSISTENCE_TEMP)
    2670           6 :             ereport(ERROR,
    2671             :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    2672             :                      errmsg("cannot create a temporary relation as partition of permanent relation \"%s\"",
    2673             :                             RelationGetRelationName(relation))));
    2674             : 
    2675             :         /* Permanent rels cannot inherit from temporary ones */
    2676        9952 :         if (relpersistence != RELPERSISTENCE_TEMP &&
    2677        9604 :             relation->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
    2678          24 :             ereport(ERROR,
    2679             :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    2680             :                      errmsg(!is_partition
    2681             :                             ? "cannot inherit from temporary relation \"%s\""
    2682             :                             : "cannot create a permanent relation as partition of temporary relation \"%s\"",
    2683             :                             RelationGetRelationName(relation))));
    2684             : 
    2685             :         /* If existing rel is temp, it must belong to this session */
    2686        9928 :         if (relation->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
    2687         294 :             !relation->rd_islocaltemp)
    2688           0 :             ereport(ERROR,
    2689             :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    2690             :                      errmsg(!is_partition
    2691             :                             ? "cannot inherit from temporary relation of another session"
    2692             :                             : "cannot create as partition of temporary relation of another session")));
    2693             : 
    2694             :         /*
    2695             :          * We should have an UNDER permission flag for this, but for now,
    2696             :          * demand that creator of a child table own the parent.
    2697             :          */
    2698        9928 :         if (!object_ownercheck(RelationRelationId, RelationGetRelid(relation), GetUserId()))
    2699           0 :             aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(relation->rd_rel->relkind),
    2700           0 :                            RelationGetRelationName(relation));
    2701             : 
    2702        9928 :         tupleDesc = RelationGetDescr(relation);
    2703        9928 :         constr = tupleDesc->constr;
    2704             : 
    2705             :         /*
    2706             :          * newattmap->attnums[] will contain the child-table attribute numbers
    2707             :          * for the attributes of this parent table.  (They are not the same
    2708             :          * for parents after the first one, nor if we have dropped columns.)
    2709             :          */
    2710        9928 :         newattmap = make_attrmap(tupleDesc->natts);
    2711             : 
    2712             :         /* We can't process inherited defaults until newattmap is complete. */
    2713        9928 :         inherited_defaults = cols_with_defaults = NIL;
    2714             : 
    2715             :         /*
    2716             :          * Request attnotnull on columns that have a not-null constraint
    2717             :          * that's not marked NO INHERIT.
    2718             :          */
    2719        9928 :         nnconstrs = RelationGetNotNullConstraints(RelationGetRelid(relation),
    2720             :                                                   true, false);
    2721       22032 :         foreach_ptr(CookedConstraint, cc, nnconstrs)
    2722        2176 :             nncols = bms_add_member(nncols, cc->attnum);
    2723             : 
    2724       29824 :         for (AttrNumber parent_attno = 1; parent_attno <= tupleDesc->natts;
    2725       19896 :              parent_attno++)
    2726             :         {
    2727       19920 :             Form_pg_attribute attribute = TupleDescAttr(tupleDesc,
    2728             :                                                         parent_attno - 1);
    2729       19920 :             char       *attributeName = NameStr(attribute->attname);
    2730             :             int         exist_attno;
    2731             :             ColumnDef  *newdef;
    2732             :             ColumnDef  *mergeddef;
    2733             : 
    2734             :             /*
    2735             :              * Ignore dropped columns in the parent.
    2736             :              */
    2737       19920 :             if (attribute->attisdropped)
    2738         192 :                 continue;       /* leave newattmap->attnums entry as zero */
    2739             : 
    2740             :             /*
    2741             :              * Create new column definition
    2742             :              */
    2743       19728 :             newdef = makeColumnDef(attributeName, attribute->atttypid,
    2744             :                                    attribute->atttypmod, attribute->attcollation);
    2745       19728 :             newdef->storage = attribute->attstorage;
    2746       19728 :             newdef->generated = attribute->attgenerated;
    2747       19728 :             if (CompressionMethodIsValid(attribute->attcompression))
    2748          30 :                 newdef->compression =
    2749          30 :                     pstrdup(GetCompressionMethodName(attribute->attcompression));
    2750             : 
    2751             :             /*
    2752             :              * Regular inheritance children are independent enough not to
    2753             :              * inherit identity columns.  But partitions are integral part of
    2754             :              * a partitioned table and inherit identity column.
    2755             :              */
    2756       19728 :             if (is_partition)
    2757       15652 :                 newdef->identity = attribute->attidentity;
    2758             : 
    2759             :             /*
    2760             :              * Does it match some previously considered column from another
    2761             :              * parent?
    2762             :              */
    2763       19728 :             exist_attno = findAttrByName(attributeName, inh_columns);
    2764       19728 :             if (exist_attno > 0)
    2765             :             {
    2766             :                 /*
    2767             :                  * Yes, try to merge the two column definitions.
    2768             :                  */
    2769         314 :                 mergeddef = MergeInheritedAttribute(inh_columns, exist_attno, newdef);
    2770             : 
    2771         290 :                 newattmap->attnums[parent_attno - 1] = exist_attno;
    2772             : 
    2773             :                 /*
    2774             :                  * Partitions have only one parent, so conflict should never
    2775             :                  * occur.
    2776             :                  */
    2777             :                 Assert(!is_partition);
    2778             :             }
    2779             :             else
    2780             :             {
    2781             :                 /*
    2782             :                  * No, create a new inherited column
    2783             :                  */
    2784       19414 :                 newdef->inhcount = 1;
    2785       19414 :                 newdef->is_local = false;
    2786       19414 :                 inh_columns = lappend(inh_columns, newdef);
    2787             : 
    2788       19414 :                 newattmap->attnums[parent_attno - 1] = ++child_attno;
    2789       19414 :                 mergeddef = newdef;
    2790             :             }
    2791             : 
    2792             :             /*
    2793             :              * mark attnotnull if parent has it
    2794             :              */
    2795       19704 :             if (bms_is_member(parent_attno, nncols))
    2796        2176 :                 mergeddef->is_not_null = true;
    2797             : 
    2798             :             /*
    2799             :              * Locate default/generation expression if any
    2800             :              */
    2801       19704 :             if (attribute->atthasdef)
    2802             :             {
    2803             :                 Node       *this_default;
    2804             : 
    2805         556 :                 this_default = TupleDescGetDefault(tupleDesc, parent_attno);
    2806         556 :                 if (this_default == NULL)
    2807           0 :                     elog(ERROR, "default expression not found for attribute %d of relation \"%s\"",
    2808             :                          parent_attno, RelationGetRelationName(relation));
    2809             : 
    2810             :                 /*
    2811             :                  * If it's a GENERATED default, it might contain Vars that
    2812             :                  * need to be mapped to the inherited column(s)' new numbers.
    2813             :                  * We can't do that till newattmap is ready, so just remember
    2814             :                  * all the inherited default expressions for the moment.
    2815             :                  */
    2816         556 :                 inherited_defaults = lappend(inherited_defaults, this_default);
    2817         556 :                 cols_with_defaults = lappend(cols_with_defaults, mergeddef);
    2818             :             }
    2819             :         }
    2820             : 
    2821             :         /*
    2822             :          * Now process any inherited default expressions, adjusting attnos
    2823             :          * using the completed newattmap map.
    2824             :          */
    2825       10460 :         forboth(lc1, inherited_defaults, lc2, cols_with_defaults)
    2826             :         {
    2827         556 :             Node       *this_default = (Node *) lfirst(lc1);
    2828         556 :             ColumnDef  *def = (ColumnDef *) lfirst(lc2);
    2829             :             bool        found_whole_row;
    2830             : 
    2831             :             /* Adjust Vars to match new table's column numbering */
    2832         556 :             this_default = map_variable_attnos(this_default,
    2833             :                                                1, 0,
    2834             :                                                newattmap,
    2835             :                                                InvalidOid, &found_whole_row);
    2836             : 
    2837             :             /*
    2838             :              * For the moment we have to reject whole-row variables.  We could
    2839             :              * convert them, if we knew the new table's rowtype OID, but that
    2840             :              * hasn't been assigned yet.  (A variable could only appear in a
    2841             :              * generation expression, so the error message is correct.)
    2842             :              */
    2843         556 :             if (found_whole_row)
    2844           0 :                 ereport(ERROR,
    2845             :                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    2846             :                          errmsg("cannot convert whole-row table reference"),
    2847             :                          errdetail("Generation expression for column \"%s\" contains a whole-row reference to table \"%s\".",
    2848             :                                    def->colname,
    2849             :                                    RelationGetRelationName(relation))));
    2850             : 
    2851             :             /*
    2852             :              * If we already had a default from some prior parent, check to
    2853             :              * see if they are the same.  If so, no problem; if not, mark the
    2854             :              * column as having a bogus default.  Below, we will complain if
    2855             :              * the bogus default isn't overridden by the child columns.
    2856             :              */
    2857             :             Assert(def->raw_default == NULL);
    2858         556 :             if (def->cooked_default == NULL)
    2859         526 :                 def->cooked_default = this_default;
    2860          30 :             else if (!equal(def->cooked_default, this_default))
    2861             :             {
    2862          24 :                 def->cooked_default = &bogus_marker;
    2863          24 :                 have_bogus_defaults = true;
    2864             :             }
    2865             :         }
    2866             : 
    2867             :         /*
    2868             :          * Now copy the CHECK constraints of this parent, adjusting attnos
    2869             :          * using the completed newattmap map.  Identically named constraints
    2870             :          * are merged if possible, else we throw error.
    2871             :          */
    2872        9904 :         if (constr && constr->num_check > 0)
    2873             :         {
    2874         328 :             ConstrCheck *check = constr->check;
    2875             : 
    2876         956 :             for (int i = 0; i < constr->num_check; i++)
    2877             :             {
    2878         628 :                 char       *name = check[i].ccname;
    2879             :                 Node       *expr;
    2880             :                 bool        found_whole_row;
    2881             : 
    2882             :                 /* ignore if the constraint is non-inheritable */
    2883         628 :                 if (check[i].ccnoinherit)
    2884          48 :                     continue;
    2885             : 
    2886             :                 /* Adjust Vars to match new table's column numbering */
    2887         580 :                 expr = map_variable_attnos(stringToNode(check[i].ccbin),
    2888             :                                            1, 0,
    2889             :                                            newattmap,
    2890             :                                            InvalidOid, &found_whole_row);
    2891             : 
    2892             :                 /*
    2893             :                  * For the moment we have to reject whole-row variables. We
    2894             :                  * could convert them, if we knew the new table's rowtype OID,
    2895             :                  * but that hasn't been assigned yet.
    2896             :                  */
    2897         580 :                 if (found_whole_row)
    2898           0 :                     ereport(ERROR,
    2899             :                             (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    2900             :                              errmsg("cannot convert whole-row table reference"),
    2901             :                              errdetail("Constraint \"%s\" contains a whole-row reference to table \"%s\".",
    2902             :                                        name,
    2903             :                                        RelationGetRelationName(relation))));
    2904             : 
    2905         580 :                 constraints = MergeCheckConstraint(constraints, name, expr,
    2906         580 :                                                    check[i].ccenforced);
    2907             :             }
    2908             :         }
    2909             : 
    2910             :         /*
    2911             :          * Also copy the not-null constraints from this parent.  The
    2912             :          * attnotnull markings were already installed above.
    2913             :          */
    2914       21984 :         foreach_ptr(CookedConstraint, nn, nnconstrs)
    2915             :         {
    2916             :             Assert(nn->contype == CONSTR_NOTNULL);
    2917             : 
    2918        2176 :             nn->attnum = newattmap->attnums[nn->attnum - 1];
    2919             : 
    2920        2176 :             nnconstraints = lappend(nnconstraints, nn);
    2921             :         }
    2922             : 
    2923        9904 :         free_attrmap(newattmap);
    2924             : 
    2925             :         /*
    2926             :          * Close the parent rel, but keep our lock on it until xact commit.
    2927             :          * That will prevent someone else from deleting or ALTERing the parent
    2928             :          * before the child is committed.
    2929             :          */
    2930        9904 :         table_close(relation, NoLock);
    2931             :     }
    2932             : 
    2933             :     /*
    2934             :      * If we had no inherited attributes, the result columns are just the
    2935             :      * explicitly declared columns.  Otherwise, we need to merge the declared
    2936             :      * columns into the inherited column list.  Although, we never have any
    2937             :      * explicitly declared columns if the table is a partition.
    2938             :      */
    2939       57028 :     if (inh_columns != NIL)
    2940             :     {
    2941        9530 :         int         newcol_attno = 0;
    2942             : 
    2943       10386 :         foreach(lc, columns)
    2944             :         {
    2945         904 :             ColumnDef  *newdef = lfirst_node(ColumnDef, lc);
    2946         904 :             char       *attributeName = newdef->colname;
    2947             :             int         exist_attno;
    2948             : 
    2949             :             /*
    2950             :              * Partitions have only one parent and have no column definitions
    2951             :              * of their own, so conflict should never occur.
    2952             :              */
    2953             :             Assert(!is_partition);
    2954             : 
    2955         904 :             newcol_attno++;
    2956             : 
    2957             :             /*
    2958             :              * Does it match some inherited column?
    2959             :              */
    2960         904 :             exist_attno = findAttrByName(attributeName, inh_columns);
    2961         904 :             if (exist_attno > 0)
    2962             :             {
    2963             :                 /*
    2964             :                  * Yes, try to merge the two column definitions.
    2965             :                  */
    2966         304 :                 MergeChildAttribute(inh_columns, exist_attno, newcol_attno, newdef);
    2967             :             }
    2968             :             else
    2969             :             {
    2970             :                 /*
    2971             :                  * No, attach new column unchanged to result columns.
    2972             :                  */
    2973         600 :                 inh_columns = lappend(inh_columns, newdef);
    2974             :             }
    2975             :         }
    2976             : 
    2977        9482 :         columns = inh_columns;
    2978             : 
    2979             :         /*
    2980             :          * Check that we haven't exceeded the legal # of columns after merging
    2981             :          * in inherited columns.
    2982             :          */
    2983        9482 :         if (list_length(columns) > MaxHeapAttributeNumber)
    2984           0 :             ereport(ERROR,
    2985             :                     (errcode(ERRCODE_TOO_MANY_COLUMNS),
    2986             :                      errmsg("tables can have at most %d columns",
    2987             :                             MaxHeapAttributeNumber)));
    2988             :     }
    2989             : 
    2990             :     /*
    2991             :      * Now that we have the column definition list for a partition, we can
    2992             :      * check whether the columns referenced in the column constraint specs
    2993             :      * actually exist.  Also, merge column defaults.
    2994             :      */
    2995       56980 :     if (is_partition)
    2996             :     {
    2997        7930 :         foreach(lc, saved_columns)
    2998             :         {
    2999         212 :             ColumnDef  *restdef = lfirst(lc);
    3000         212 :             bool        found = false;
    3001             :             ListCell   *l;
    3002             : 
    3003         804 :             foreach(l, columns)
    3004             :             {
    3005         604 :                 ColumnDef  *coldef = lfirst(l);
    3006             : 
    3007         604 :                 if (strcmp(coldef->colname, restdef->colname) == 0)
    3008             :                 {
    3009         212 :                     found = true;
    3010             : 
    3011             :                     /*
    3012             :                      * Check for conflicts related to generated columns.
    3013             :                      *
    3014             :                      * Same rules as above: generated-ness has to match the
    3015             :                      * parent, but the contents of the generation expression
    3016             :                      * can be different.
    3017             :                      */
    3018         212 :                     if (coldef->generated)
    3019             :                     {
    3020         110 :                         if (restdef->raw_default && !restdef->generated)
    3021           6 :                             ereport(ERROR,
    3022             :                                     (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
    3023             :                                      errmsg("column \"%s\" inherits from generated column but specifies default",
    3024             :                                             restdef->colname)));
    3025         104 :                         if (restdef->identity)
    3026           0 :                             ereport(ERROR,
    3027             :                                     (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
    3028             :                                      errmsg("column \"%s\" inherits from generated column but specifies identity",
    3029             :                                             restdef->colname)));
    3030             :                     }
    3031             :                     else
    3032             :                     {
    3033         102 :                         if (restdef->generated)
    3034           6 :                             ereport(ERROR,
    3035             :                                     (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
    3036             :                                      errmsg("child column \"%s\" specifies generation expression",
    3037             :                                             restdef->colname),
    3038             :                                      errhint("A child table column cannot be generated unless its parent column is.")));
    3039             :                     }
    3040             : 
    3041             :                     /*
    3042             :                      * Override the parent's default value for this column
    3043             :                      * (coldef->cooked_default) with the partition's local
    3044             :                      * definition (restdef->raw_default), if there's one. It
    3045             :                      * should be physically impossible to get a cooked default
    3046             :                      * in the local definition or a raw default in the
    3047             :                      * inherited definition, but make sure they're nulls, for
    3048             :                      * future-proofing.
    3049             :                      */
    3050             :                     Assert(restdef->cooked_default == NULL);
    3051             :                     Assert(coldef->raw_default == NULL);
    3052         200 :                     if (restdef->raw_default)
    3053             :                     {
    3054         128 :                         coldef->raw_default = restdef->raw_default;
    3055         128 :                         coldef->cooked_default = NULL;
    3056             :                     }
    3057             :                 }
    3058             :             }
    3059             : 
    3060             :             /* complain for constraints on columns not in parent */
    3061         200 :             if (!found)
    3062           0 :                 ereport(ERROR,
    3063             :                         (errcode(ERRCODE_UNDEFINED_COLUMN),
    3064             :                          errmsg("column \"%s\" does not exist",
    3065             :                                 restdef->colname)));
    3066             :         }
    3067             :     }
    3068             : 
    3069             :     /*
    3070             :      * If we found any conflicting parent default values, check to make sure
    3071             :      * they were overridden by the child.
    3072             :      */
    3073       56968 :     if (have_bogus_defaults)
    3074             :     {
    3075          54 :         foreach(lc, columns)
    3076             :         {
    3077          42 :             ColumnDef  *def = lfirst(lc);
    3078             : 
    3079          42 :             if (def->cooked_default == &bogus_marker)
    3080             :             {
    3081          12 :                 if (def->generated)
    3082           6 :                     ereport(ERROR,
    3083             :                             (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
    3084             :                              errmsg("column \"%s\" inherits conflicting generation expressions",
    3085             :                                     def->colname),
    3086             :                              errhint("To resolve the conflict, specify a generation expression explicitly.")));
    3087             :                 else
    3088           6 :                     ereport(ERROR,
    3089             :                             (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
    3090             :                              errmsg("column \"%s\" inherits conflicting default values",
    3091             :                                     def->colname),
    3092             :                              errhint("To resolve the conflict, specify a default explicitly.")));
    3093             :             }
    3094             :         }
    3095             :     }
    3096             : 
    3097       56956 :     *supconstr = constraints;
    3098       56956 :     *supnotnulls = nnconstraints;
    3099             : 
    3100       56956 :     return columns;
    3101             : }
    3102             : 
    3103             : 
    3104             : /*
    3105             :  * MergeCheckConstraint
    3106             :  *      Try to merge an inherited CHECK constraint with previous ones
    3107             :  *
    3108             :  * If we inherit identically-named constraints from multiple parents, we must
    3109             :  * merge them, or throw an error if they don't have identical definitions.
    3110             :  *
    3111             :  * constraints is a list of CookedConstraint structs for previous constraints.
    3112             :  *
    3113             :  * If the new constraint matches an existing one, then the existing
    3114             :  * constraint's inheritance count is updated.  If there is a conflict (same
    3115             :  * name but different expression), throw an error.  If the constraint neither
    3116             :  * matches nor conflicts with an existing one, a new constraint is appended to
    3117             :  * the list.
    3118             :  */
    3119             : static List *
    3120         580 : MergeCheckConstraint(List *constraints, const char *name, Node *expr, bool is_enforced)
    3121             : {
    3122             :     ListCell   *lc;
    3123             :     CookedConstraint *newcon;
    3124             : 
    3125        1480 :     foreach(lc, constraints)
    3126             :     {
    3127        1026 :         CookedConstraint *ccon = (CookedConstraint *) lfirst(lc);
    3128             : 
    3129             :         Assert(ccon->contype == CONSTR_CHECK);
    3130             : 
    3131             :         /* Non-matching names never conflict */
    3132        1026 :         if (strcmp(ccon->name, name) != 0)
    3133         900 :             continue;
    3134             : 
    3135         126 :         if (equal(expr, ccon->expr))
    3136             :         {
    3137             :             /* OK to merge constraint with existing */
    3138         126 :             if (pg_add_s16_overflow(ccon->inhcount, 1,
    3139             :                                     &ccon->inhcount))
    3140           0 :                 ereport(ERROR,
    3141             :                         errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
    3142             :                         errmsg("too many inheritance parents"));
    3143             : 
    3144             :             /*
    3145             :              * When enforceability differs, the merged constraint should be
    3146             :              * marked as ENFORCED because one of the parents is ENFORCED.
    3147             :              */
    3148         126 :             if (!ccon->is_enforced && is_enforced)
    3149             :             {
    3150          24 :                 ccon->is_enforced = true;
    3151          24 :                 ccon->skip_validation = false;
    3152             :             }
    3153             : 
    3154         126 :             return constraints;
    3155             :         }
    3156             : 
    3157           0 :         ereport(ERROR,
    3158             :                 (errcode(ERRCODE_DUPLICATE_OBJECT),
    3159             :                  errmsg("check constraint name \"%s\" appears multiple times but with different expressions",
    3160             :                         name)));
    3161             :     }
    3162             : 
    3163             :     /*
    3164             :      * Constraint couldn't be merged with an existing one and also didn't
    3165             :      * conflict with an existing one, so add it as a new one to the list.
    3166             :      */
    3167         454 :     newcon = palloc0_object(CookedConstraint);
    3168         454 :     newcon->contype = CONSTR_CHECK;
    3169         454 :     newcon->name = pstrdup(name);
    3170         454 :     newcon->expr = expr;
    3171         454 :     newcon->inhcount = 1;
    3172         454 :     newcon->is_enforced = is_enforced;
    3173         454 :     newcon->skip_validation = !is_enforced;
    3174         454 :     return lappend(constraints, newcon);
    3175             : }
    3176             : 
    3177             : /*
    3178             :  * MergeChildAttribute
    3179             :  *      Merge given child attribute definition into given inherited attribute.
    3180             :  *
    3181             :  * Input arguments:
    3182             :  * 'inh_columns' is the list of inherited ColumnDefs.
    3183             :  * 'exist_attno' is the number of the inherited attribute in inh_columns
    3184             :  * 'newcol_attno' is the attribute number in child table's schema definition
    3185             :  * 'newdef' is the column/attribute definition from the child table.
    3186             :  *
    3187             :  * The ColumnDef in 'inh_columns' list is modified.  The child attribute's
    3188             :  * ColumnDef remains unchanged.
    3189             :  *
    3190             :  * Notes:
    3191             :  * - The attribute is merged according to the rules laid out in the prologue
    3192             :  *   of MergeAttributes().
    3193             :  * - If matching inherited attribute exists but the child attribute can not be
    3194             :  *   merged into it, the function throws respective errors.
    3195             :  * - A partition can not have its own column definitions. Hence this function
    3196             :  *   is applicable only to a regular inheritance child.
    3197             :  */
    3198             : static void
    3199         304 : MergeChildAttribute(List *inh_columns, int exist_attno, int newcol_attno, const ColumnDef *newdef)
    3200             : {
    3201         304 :     char       *attributeName = newdef->colname;
    3202             :     ColumnDef  *inhdef;
    3203             :     Oid         inhtypeid,
    3204             :                 newtypeid;
    3205             :     int32       inhtypmod,
    3206             :                 newtypmod;
    3207             :     Oid         inhcollid,
    3208             :                 newcollid;
    3209             : 
    3210         304 :     if (exist_attno == newcol_attno)
    3211         276 :         ereport(NOTICE,
    3212             :                 (errmsg("merging column \"%s\" with inherited definition",
    3213             :                         attributeName)));
    3214             :     else
    3215          28 :         ereport(NOTICE,
    3216             :                 (errmsg("moving and merging column \"%s\" with inherited definition", attributeName),
    3217             :                  errdetail("User-specified column moved to the position of the inherited column.")));
    3218             : 
    3219         304 :     inhdef = list_nth_node(ColumnDef, inh_columns, exist_attno - 1);
    3220             : 
    3221             :     /*
    3222             :      * Must have the same type and typmod
    3223             :      */
    3224         304 :     typenameTypeIdAndMod(NULL, inhdef->typeName, &inhtypeid, &inhtypmod);
    3225         304 :     typenameTypeIdAndMod(NULL, newdef->typeName, &newtypeid, &newtypmod);
    3226         304 :     if (inhtypeid != newtypeid || inhtypmod != newtypmod)
    3227          12 :         ereport(ERROR,
    3228             :                 (errcode(ERRCODE_DATATYPE_MISMATCH),
    3229             :                  errmsg("column \"%s\" has a type conflict",
    3230             :                         attributeName),
    3231             :                  errdetail("%s versus %s",
    3232             :                            format_type_with_typemod(inhtypeid, inhtypmod),
    3233             :                            format_type_with_typemod(newtypeid, newtypmod))));
    3234             : 
    3235             :     /*
    3236             :      * Must have the same collation
    3237             :      */
    3238         292 :     inhcollid = GetColumnDefCollation(NULL, inhdef, inhtypeid);
    3239         292 :     newcollid = GetColumnDefCollation(NULL, newdef, newtypeid);
    3240         292 :     if (inhcollid != newcollid)
    3241           6 :         ereport(ERROR,
    3242             :                 (errcode(ERRCODE_COLLATION_MISMATCH),
    3243             :                  errmsg("column \"%s\" has a collation conflict",
    3244             :                         attributeName),
    3245             :                  errdetail("\"%s\" versus \"%s\"",
    3246             :                            get_collation_name(inhcollid),
    3247             :                            get_collation_name(newcollid))));
    3248             : 
    3249             :     /*
    3250             :      * Identity is never inherited by a regular inheritance child. Pick
    3251             :      * child's identity definition if there's one.
    3252             :      */
    3253         286 :     inhdef->identity = newdef->identity;
    3254             : 
    3255             :     /*
    3256             :      * Copy storage parameter
    3257             :      */
    3258         286 :     if (inhdef->storage == 0)
    3259           0 :         inhdef->storage = newdef->storage;
    3260         286 :     else if (newdef->storage != 0 && inhdef->storage != newdef->storage)
    3261           6 :         ereport(ERROR,
    3262             :                 (errcode(ERRCODE_DATATYPE_MISMATCH),
    3263             :                  errmsg("column \"%s\" has a storage parameter conflict",
    3264             :                         attributeName),
    3265             :                  errdetail("%s versus %s",
    3266             :                            storage_name(inhdef->storage),
    3267             :                            storage_name(newdef->storage))));
    3268             : 
    3269             :     /*
    3270             :      * Copy compression parameter
    3271             :      */
    3272         280 :     if (inhdef->compression == NULL)
    3273         274 :         inhdef->compression = newdef->compression;
    3274           6 :     else if (newdef->compression != NULL)
    3275             :     {
    3276           6 :         if (strcmp(inhdef->compression, newdef->compression) != 0)
    3277           6 :             ereport(ERROR,
    3278             :                     (errcode(ERRCODE_DATATYPE_MISMATCH),
    3279             :                      errmsg("column \"%s\" has a compression method conflict",
    3280             :                             attributeName),
    3281             :                      errdetail("%s versus %s", inhdef->compression, newdef->compression)));
    3282             :     }
    3283             : 
    3284             :     /*
    3285             :      * Merge of not-null constraints = OR 'em together
    3286             :      */
    3287         274 :     inhdef->is_not_null |= newdef->is_not_null;
    3288             : 
    3289             :     /*
    3290             :      * Check for conflicts related to generated columns.
    3291             :      *
    3292             :      * If the parent column is generated, the child column will be made a
    3293             :      * generated column if it isn't already.  If it is a generated column,
    3294             :      * we'll take its generation expression in preference to the parent's.  We
    3295             :      * must check that the child column doesn't specify a default value or
    3296             :      * identity, which matches the rules for a single column in
    3297             :      * parse_utilcmd.c.
    3298             :      *
    3299             :      * Conversely, if the parent column is not generated, the child column
    3300             :      * can't be either.  (We used to allow that, but it results in being able
    3301             :      * to override the generation expression via UPDATEs through the parent.)
    3302             :      */
    3303         274 :     if (inhdef->generated)
    3304             :     {
    3305          26 :         if (newdef->raw_default && !newdef->generated)
    3306           6 :             ereport(ERROR,
    3307             :                     (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
    3308             :                      errmsg("column \"%s\" inherits from generated column but specifies default",
    3309             :                             inhdef->colname)));
    3310          20 :         if (newdef->identity)
    3311           6 :             ereport(ERROR,
    3312             :                     (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
    3313             :                      errmsg("column \"%s\" inherits from generated column but specifies identity",
    3314             :                             inhdef->colname)));
    3315             :     }
    3316             :     else
    3317             :     {
    3318         248 :         if (newdef->generated)
    3319           6 :             ereport(ERROR,
    3320             :                     (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
    3321             :                      errmsg("child column \"%s\" specifies generation expression",
    3322             :                             inhdef->colname),
    3323             :                      errhint("A child table column cannot be generated unless its parent column is.")));
    3324             :     }
    3325             : 
    3326             :     /*
    3327             :      * If new def has a default, override previous default
    3328             :      */
    3329         256 :     if (newdef->raw_default != NULL)
    3330             :     {
    3331          18 :         inhdef->raw_default = newdef->raw_default;
    3332          18 :         inhdef->cooked_default = newdef->cooked_default;
    3333             :     }
    3334             : 
    3335             :     /* Mark the column as locally defined */
    3336         256 :     inhdef->is_local = true;
    3337         256 : }
    3338             : 
    3339             : /*
    3340             :  * MergeInheritedAttribute
    3341             :  *      Merge given parent attribute definition into specified attribute
    3342             :  *      inherited from the previous parents.
    3343             :  *
    3344             :  * Input arguments:
    3345             :  * 'inh_columns' is the list of previously inherited ColumnDefs.
    3346             :  * 'exist_attno' is the number the existing matching attribute in inh_columns.
    3347             :  * 'newdef' is the new parent column/attribute definition to be merged.
    3348             :  *
    3349             :  * The matching ColumnDef in 'inh_columns' list is modified and returned.
    3350             :  *
    3351             :  * Notes:
    3352             :  * - The attribute is merged according to the rules laid out in the prologue
    3353             :  *   of MergeAttributes().
    3354             :  * - If matching inherited attribute exists but the new attribute can not be
    3355             :  *   merged into it, the function throws respective errors.
    3356             :  * - A partition inherits from only a single parent. Hence this function is
    3357             :  *   applicable only to a regular inheritance.
    3358             :  */
    3359             : static ColumnDef *
    3360         314 : MergeInheritedAttribute(List *inh_columns,
    3361             :                         int exist_attno,
    3362             :                         const ColumnDef *newdef)
    3363             : {
    3364         314 :     char       *attributeName = newdef->colname;
    3365             :     ColumnDef  *prevdef;
    3366             :     Oid         prevtypeid,
    3367             :                 newtypeid;
    3368             :     int32       prevtypmod,
    3369             :                 newtypmod;
    3370             :     Oid         prevcollid,
    3371             :                 newcollid;
    3372             : 
    3373         314 :     ereport(NOTICE,
    3374             :             (errmsg("merging multiple inherited definitions of column \"%s\"",
    3375             :                     attributeName)));
    3376         314 :     prevdef = list_nth_node(ColumnDef, inh_columns, exist_attno - 1);
    3377             : 
    3378             :     /*
    3379             :      * Must have the same type and typmod
    3380             :      */
    3381         314 :     typenameTypeIdAndMod(NULL, prevdef->typeName, &prevtypeid, &prevtypmod);
    3382         314 :     typenameTypeIdAndMod(NULL, newdef->typeName, &newtypeid, &newtypmod);
    3383         314 :     if (prevtypeid != newtypeid || prevtypmod != newtypmod)
    3384           0 :         ereport(ERROR,
    3385             :                 (errcode(ERRCODE_DATATYPE_MISMATCH),
    3386             :                  errmsg("inherited column \"%s\" has a type conflict",
    3387             :                         attributeName),
    3388             :                  errdetail("%s versus %s",
    3389             :                            format_type_with_typemod(prevtypeid, prevtypmod),
    3390             :                            format_type_with_typemod(newtypeid, newtypmod))));
    3391             : 
    3392             :     /*
    3393             :      * Must have the same collation
    3394             :      */
    3395         314 :     prevcollid = GetColumnDefCollation(NULL, prevdef, prevtypeid);
    3396         314 :     newcollid = GetColumnDefCollation(NULL, newdef, newtypeid);
    3397         314 :     if (prevcollid != newcollid)
    3398           0 :         ereport(ERROR,
    3399             :                 (errcode(ERRCODE_COLLATION_MISMATCH),
    3400             :                  errmsg("inherited column \"%s\" has a collation conflict",
    3401             :                         attributeName),
    3402             :                  errdetail("\"%s\" versus \"%s\"",
    3403             :                            get_collation_name(prevcollid),
    3404             :                            get_collation_name(newcollid))));
    3405             : 
    3406             :     /*
    3407             :      * Copy/check storage parameter
    3408             :      */
    3409         314 :     if (prevdef->storage == 0)
    3410           0 :         prevdef->storage = newdef->storage;
    3411         314 :     else if (prevdef->storage != newdef->storage)
    3412           6 :         ereport(ERROR,
    3413             :                 (errcode(ERRCODE_DATATYPE_MISMATCH),
    3414             :                  errmsg("inherited column \"%s\" has a storage parameter conflict",
    3415             :                         attributeName),
    3416             :                  errdetail("%s versus %s",
    3417             :                            storage_name(prevdef->storage),
    3418             :                            storage_name(newdef->storage))));
    3419             : 
    3420             :     /*
    3421             :      * Copy/check compression parameter
    3422             :      */
    3423         308 :     if (prevdef->compression == NULL)
    3424         296 :         prevdef->compression = newdef->compression;
    3425          12 :     else if (newdef->compression != NULL)
    3426             :     {
    3427           6 :         if (strcmp(prevdef->compression, newdef->compression) != 0)
    3428           6 :             ereport(ERROR,
    3429             :                     (errcode(ERRCODE_DATATYPE_MISMATCH),
    3430             :                      errmsg("column \"%s\" has a compression method conflict",
    3431             :                             attributeName),
    3432             :                      errdetail("%s versus %s",
    3433             :                                prevdef->compression, newdef->compression)));
    3434             :     }
    3435             : 
    3436             :     /*
    3437             :      * Check for GENERATED conflicts
    3438             :      */
    3439         302 :     if (prevdef->generated != newdef->generated)
    3440          12 :         ereport(ERROR,
    3441             :                 (errcode(ERRCODE_DATATYPE_MISMATCH),
    3442             :                  errmsg("inherited column \"%s\" has a generation conflict",
    3443             :                         attributeName)));
    3444             : 
    3445             :     /*
    3446             :      * Default and other constraints are handled by the caller.
    3447             :      */
    3448             : 
    3449         290 :     if (pg_add_s16_overflow(prevdef->inhcount, 1,
    3450             :                             &prevdef->inhcount))
    3451           0 :         ereport(ERROR,
    3452             :                 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
    3453             :                 errmsg("too many inheritance parents"));
    3454             : 
    3455         290 :     return prevdef;
    3456             : }
    3457             : 
    3458             : /*
    3459             :  * StoreCatalogInheritance
    3460             :  *      Updates the system catalogs with proper inheritance information.
    3461             :  *
    3462             :  * supers is a list of the OIDs of the new relation's direct ancestors.
    3463             :  */
    3464             : static void
    3465       56410 : StoreCatalogInheritance(Oid relationId, List *supers,
    3466             :                         bool child_is_partition)
    3467             : {
    3468             :     Relation    relation;
    3469             :     int32       seqNumber;
    3470             :     ListCell   *entry;
    3471             : 
    3472             :     /*
    3473             :      * sanity checks
    3474             :      */
    3475             :     Assert(OidIsValid(relationId));
    3476             : 
    3477       56410 :     if (supers == NIL)
    3478       47264 :         return;
    3479             : 
    3480             :     /*
    3481             :      * Store INHERITS information in pg_inherits using direct ancestors only.
    3482             :      * Also enter dependencies on the direct ancestors, and make sure they are
    3483             :      * marked with relhassubclass = true.
    3484             :      *
    3485             :      * (Once upon a time, both direct and indirect ancestors were found here
    3486             :      * and then entered into pg_ipl.  Since that catalog doesn't exist
    3487             :      * anymore, there's no need to look for indirect ancestors.)
    3488             :      */
    3489        9146 :     relation = table_open(InheritsRelationId, RowExclusiveLock);
    3490             : 
    3491        9146 :     seqNumber = 1;
    3492       18588 :     foreach(entry, supers)
    3493             :     {
    3494        9442 :         Oid         parentOid = lfirst_oid(entry);
    3495             : 
    3496        9442 :         StoreCatalogInheritance1(relationId, parentOid, seqNumber, relation,
    3497             :                                  child_is_partition);
    3498        9442 :         seqNumber++;
    3499             :     }
    3500             : 
    3501        9146 :     table_close(relation, RowExclusiveLock);
    3502             : }
    3503             : 
    3504             : /*
    3505             :  * Make catalog entries showing relationId as being an inheritance child
    3506             :  * of parentOid.  inhRelation is the already-opened pg_inherits catalog.
    3507             :  */
    3508             : static void
    3509       11656 : StoreCatalogInheritance1(Oid relationId, Oid parentOid,
    3510             :                          int32 seqNumber, Relation inhRelation,
    3511             :                          bool child_is_partition)
    3512             : {
    3513             :     ObjectAddress childobject,
    3514             :                 parentobject;
    3515             : 
    3516             :     /* store the pg_inherits row */
    3517       11656 :     StoreSingleInheritance(relationId, parentOid, seqNumber);
    3518             : 
    3519             :     /*
    3520             :      * Store a dependency too
    3521             :      */
    3522       11656 :     parentobject.classId = RelationRelationId;
    3523       11656 :     parentobject.objectId = parentOid;
    3524       11656 :     parentobject.objectSubId = 0;
    3525       11656 :     childobject.classId = RelationRelationId;
    3526       11656 :     childobject.objectId = relationId;
    3527       11656 :     childobject.objectSubId = 0;
    3528             : 
    3529       11656 :     recordDependencyOn(&childobject, &parentobject,
    3530             :                        child_dependency_type(child_is_partition));
    3531             : 
    3532             :     /*
    3533             :      * Post creation hook of this inheritance. Since object_access_hook
    3534             :      * doesn't take multiple object identifiers, we relay oid of parent
    3535             :      * relation using auxiliary_id argument.
    3536             :      */
    3537       11656 :     InvokeObjectPostAlterHookArg(InheritsRelationId,
    3538             :                                  relationId, 0,
    3539             :                                  parentOid, false);
    3540             : 
    3541             :     /*
    3542             :      * Mark the parent as having subclasses.
    3543             :      */
    3544       11656 :     SetRelationHasSubclass(parentOid, true);
    3545       11656 : }
    3546             : 
    3547             : /*
    3548             :  * Look for an existing column entry with the given name.
    3549             :  *
    3550             :  * Returns the index (starting with 1) if attribute already exists in columns,
    3551             :  * 0 if it doesn't.
    3552             :  */
    3553             : static int
    3554       20632 : findAttrByName(const char *attributeName, const List *columns)
    3555             : {
    3556             :     ListCell   *lc;
    3557       20632 :     int         i = 1;
    3558             : 
    3559       36778 :     foreach(lc, columns)
    3560             :     {
    3561       16764 :         if (strcmp(attributeName, lfirst_node(ColumnDef, lc)->colname) == 0)
    3562         618 :             return i;
    3563             : 
    3564       16146 :         i++;
    3565             :     }
    3566       20014 :     return 0;
    3567             : }
    3568             : 
    3569             : 
    3570             : /*
    3571             :  * SetRelationHasSubclass
    3572             :  *      Set the value of the relation's relhassubclass field in pg_class.
    3573             :  *
    3574             :  * It's always safe to set this field to true, because all SQL commands are
    3575             :  * ready to see true and then find no children.  On the other hand, commands
    3576             :  * generally assume zero children if this is false.
    3577             :  *
    3578             :  * Caller must hold any self-exclusive lock until end of transaction.  If the
    3579             :  * new value is false, caller must have acquired that lock before reading the
    3580             :  * evidence that justified the false value.  That way, it properly waits if
    3581             :  * another backend is simultaneously concluding no need to change the tuple
    3582             :  * (new and old values are true).
    3583             :  *
    3584             :  * NOTE: an important side-effect of this operation is that an SI invalidation
    3585             :  * message is sent out to all backends --- including me --- causing plans
    3586             :  * referencing the relation to be rebuilt with the new list of children.
    3587             :  * This must happen even if we find that no change is needed in the pg_class
    3588             :  * row.
    3589             :  */
    3590             : void
    3591       14580 : SetRelationHasSubclass(Oid relationId, bool relhassubclass)
    3592             : {
    3593             :     Relation    relationRelation;
    3594             :     HeapTuple   tuple;
    3595             :     Form_pg_class classtuple;
    3596             : 
    3597             :     Assert(CheckRelationOidLockedByMe(relationId,
    3598             :                                       ShareUpdateExclusiveLock, false) ||
    3599             :            CheckRelationOidLockedByMe(relationId,
    3600             :                                       ShareRowExclusiveLock, true));
    3601             : 
    3602             :     /*
    3603             :      * Fetch a modifiable copy of the tuple, modify it, update pg_class.
    3604             :      */
    3605       14580 :     relationRelation = table_open(RelationRelationId, RowExclusiveLock);
    3606       14580 :     tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relationId));
    3607       14580 :     if (!HeapTupleIsValid(tuple))
    3608           0 :         elog(ERROR, "cache lookup failed for relation %u", relationId);
    3609       14580 :     classtuple = (Form_pg_class) GETSTRUCT(tuple);
    3610             : 
    3611       14580 :     if (classtuple->relhassubclass != relhassubclass)
    3612             :     {
    3613        7358 :         classtuple->relhassubclass = relhassubclass;
    3614        7358 :         CatalogTupleUpdate(relationRelation, &tuple->t_self, tuple);
    3615             :     }
    3616             :     else
    3617             :     {
    3618             :         /* no need to change tuple, but force relcache rebuild anyway */
    3619        7222 :         CacheInvalidateRelcacheByTuple(tuple);
    3620             :     }
    3621             : 
    3622       14580 :     heap_freetuple(tuple);
    3623       14580 :     table_close(relationRelation, RowExclusiveLock);
    3624       14580 : }
    3625             : 
    3626             : /*
    3627             :  * CheckRelationTableSpaceMove
    3628             :  *      Check if relation can be moved to new tablespace.
    3629             :  *
    3630             :  * NOTE: The caller must hold AccessExclusiveLock on the relation.
    3631             :  *
    3632             :  * Returns true if the relation can be moved to the new tablespace; raises
    3633             :  * an error if it is not possible to do the move; returns false if the move
    3634             :  * would have no effect.
    3635             :  */
    3636             : bool
    3637         226 : CheckRelationTableSpaceMove(Relation rel, Oid newTableSpaceId)
    3638             : {
    3639             :     Oid         oldTableSpaceId;
    3640             : 
    3641             :     /*
    3642             :      * No work if no change in tablespace.  Note that MyDatabaseTableSpace is
    3643             :      * stored as 0.
    3644             :      */
    3645         226 :     oldTableSpaceId = rel->rd_rel->reltablespace;
    3646         226 :     if (newTableSpaceId == oldTableSpaceId ||
    3647         218 :         (newTableSpaceId == MyDatabaseTableSpace && oldTableSpaceId == 0))
    3648          10 :         return false;
    3649             : 
    3650             :     /*
    3651             :      * We cannot support moving mapped relations into different tablespaces.
    3652             :      * (In particular this eliminates all shared catalogs.)
    3653             :      */
    3654         216 :     if (RelationIsMapped(rel))
    3655           0 :         ereport(ERROR,
    3656             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    3657             :                  errmsg("cannot move system relation \"%s\"",
    3658             :                         RelationGetRelationName(rel))));
    3659             : 
    3660             :     /* Cannot move a non-shared relation into pg_global */
    3661         216 :     if (newTableSpaceId == GLOBALTABLESPACE_OID)
    3662          12 :         ereport(ERROR,
    3663             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    3664             :                  errmsg("only shared relations can be placed in pg_global tablespace")));
    3665             : 
    3666             :     /*
    3667             :      * Do not allow moving temp tables of other backends ... their local
    3668             :      * buffer manager is not going to cope.
    3669             :      */
    3670         204 :     if (RELATION_IS_OTHER_TEMP(rel))
    3671           0 :         ereport(ERROR,
    3672             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    3673             :                  errmsg("cannot move temporary tables of other sessions")));
    3674             : 
    3675         204 :     return true;
    3676             : }
    3677             : 
    3678             : /*
    3679             :  * SetRelationTableSpace
    3680             :  *      Set new reltablespace and relfilenumber in pg_class entry.
    3681             :  *
    3682             :  * newTableSpaceId is the new tablespace for the relation, and
    3683             :  * newRelFilenumber its new filenumber.  If newRelFilenumber is
    3684             :  * InvalidRelFileNumber, this field is not updated.
    3685             :  *
    3686             :  * NOTE: The caller must hold AccessExclusiveLock on the relation.
    3687             :  *
    3688             :  * The caller of this routine had better check if a relation can be
    3689             :  * moved to this new tablespace by calling CheckRelationTableSpaceMove()
    3690             :  * first, and is responsible for making the change visible with
    3691             :  * CommandCounterIncrement().
    3692             :  */
    3693             : void
    3694         204 : SetRelationTableSpace(Relation rel,
    3695             :                       Oid newTableSpaceId,
    3696             :                       RelFileNumber newRelFilenumber)
    3697             : {
    3698             :     Relation    pg_class;
    3699             :     HeapTuple   tuple;
    3700             :     ItemPointerData otid;
    3701             :     Form_pg_class rd_rel;
    3702         204 :     Oid         reloid = RelationGetRelid(rel);
    3703             : 
    3704             :     Assert(CheckRelationTableSpaceMove(rel, newTableSpaceId));
    3705             : 
    3706             :     /* Get a modifiable copy of the relation's pg_class row. */
    3707         204 :     pg_class = table_open(RelationRelationId, RowExclusiveLock);
    3708             : 
    3709         204 :     tuple = SearchSysCacheLockedCopy1(RELOID, ObjectIdGetDatum(reloid));
    3710         204 :     if (!HeapTupleIsValid(tuple))
    3711           0 :         elog(ERROR, "cache lookup failed for relation %u", reloid);
    3712         204 :     otid = tuple->t_self;
    3713         204 :     rd_rel = (Form_pg_class) GETSTRUCT(tuple);
    3714             : 
    3715             :     /* Update the pg_class row. */
    3716         408 :     rd_rel->reltablespace = (newTableSpaceId == MyDatabaseTableSpace) ?
    3717         204 :         InvalidOid : newTableSpaceId;
    3718         204 :     if (RelFileNumberIsValid(newRelFilenumber))
    3719         160 :         rd_rel->relfilenode = newRelFilenumber;
    3720         204 :     CatalogTupleUpdate(pg_class, &otid, tuple);
    3721         204 :     UnlockTuple(pg_class, &otid, InplaceUpdateTupleLock);
    3722             : 
    3723             :     /*
    3724             :      * Record dependency on tablespace.  This is only required for relations
    3725             :      * that have no physical storage.
    3726             :      */
    3727         204 :     if (!RELKIND_HAS_STORAGE(rel->rd_rel->relkind))
    3728          30 :         changeDependencyOnTablespace(RelationRelationId, reloid,
    3729             :                                      rd_rel->reltablespace);
    3730             : 
    3731         204 :     heap_freetuple(tuple);
    3732         204 :     table_close(pg_class, RowExclusiveLock);
    3733         204 : }
    3734             : 
    3735             : /*
    3736             :  *      renameatt_check         - basic sanity checks before attribute rename
    3737             :  */
    3738             : static void
    3739        1026 : renameatt_check(Oid myrelid, Form_pg_class classform, bool recursing)
    3740             : {
    3741        1026 :     char        relkind = classform->relkind;
    3742             : 
    3743        1026 :     if (classform->reloftype && !recursing)
    3744           6 :         ereport(ERROR,
    3745             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    3746             :                  errmsg("cannot rename column of typed table")));
    3747             : 
    3748             :     /*
    3749             :      * Renaming the columns of sequences or toast tables doesn't actually
    3750             :      * break anything from the system's point of view, since internal
    3751             :      * references are by attnum.  But it doesn't seem right to allow users to
    3752             :      * change names that are hardcoded into the system, hence the following
    3753             :      * restriction.
    3754             :      */
    3755        1020 :     if (relkind != RELKIND_RELATION &&
    3756          84 :         relkind != RELKIND_VIEW &&
    3757          84 :         relkind != RELKIND_MATVIEW &&
    3758          36 :         relkind != RELKIND_COMPOSITE_TYPE &&
    3759          36 :         relkind != RELKIND_INDEX &&
    3760          36 :         relkind != RELKIND_PARTITIONED_INDEX &&
    3761           0 :         relkind != RELKIND_FOREIGN_TABLE &&
    3762             :         relkind != RELKIND_PARTITIONED_TABLE)
    3763           0 :         ereport(ERROR,
    3764             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    3765             :                  errmsg("cannot rename columns of relation \"%s\"",
    3766             :                         NameStr(classform->relname)),
    3767             :                  errdetail_relkind_not_supported(relkind)));
    3768             : 
    3769             :     /*
    3770             :      * permissions checking.  only the owner of a class can change its schema.
    3771             :      */
    3772        1020 :     if (!object_ownercheck(RelationRelationId, myrelid, GetUserId()))
    3773           0 :         aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(myrelid)),
    3774           0 :                        NameStr(classform->relname));
    3775        1020 :     if (!allowSystemTableMods && IsSystemClass(myrelid, classform))
    3776           2 :         ereport(ERROR,
    3777             :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
    3778             :                  errmsg("permission denied: \"%s\" is a system catalog",
    3779             :                         NameStr(classform->relname))));
    3780        1018 : }
    3781             : 
    3782             : /*
    3783             :  *      renameatt_internal      - workhorse for renameatt
    3784             :  *
    3785             :  * Return value is the attribute number in the 'myrelid' relation.
    3786             :  */
    3787             : static AttrNumber
    3788         552 : renameatt_internal(Oid myrelid,
    3789             :                    const char *oldattname,
    3790             :                    const char *newattname,
    3791             :                    bool recurse,
    3792             :                    bool recursing,
    3793             :                    int expected_parents,
    3794             :                    DropBehavior behavior)
    3795             : {
    3796             :     Relation    targetrelation;
    3797             :     Relation    attrelation;
    3798             :     HeapTuple   atttup;
    3799             :     Form_pg_attribute attform;
    3800             :     AttrNumber  attnum;
    3801             : 
    3802             :     /*
    3803             :      * Grab an exclusive lock on the target table, which we will NOT release
    3804             :      * until end of transaction.
    3805             :      */
    3806         552 :     targetrelation = relation_open(myrelid, AccessExclusiveLock);
    3807         552 :     renameatt_check(myrelid, RelationGetForm(targetrelation), recursing);
    3808             : 
    3809             :     /*
    3810             :      * if the 'recurse' flag is set then we are supposed to rename this
    3811             :      * attribute in all classes that inherit from 'relname' (as well as in
    3812             :      * 'relname').
    3813             :      *
    3814             :      * any permissions or problems with duplicate attributes will cause the
    3815             :      * whole transaction to abort, which is what we want -- all or nothing.
    3816             :      */
    3817         552 :     if (recurse)
    3818             :     {
    3819             :         List       *child_oids,
    3820             :                    *child_numparents;
    3821             :         ListCell   *lo,
    3822             :                    *li;
    3823             : 
    3824             :         /*
    3825             :          * we need the number of parents for each child so that the recursive
    3826             :          * calls to renameatt() can determine whether there are any parents
    3827             :          * outside the inheritance hierarchy being processed.
    3828             :          */
    3829         248 :         child_oids = find_all_inheritors(myrelid, AccessExclusiveLock,
    3830             :                                          &child_numparents);
    3831             : 
    3832             :         /*
    3833             :          * find_all_inheritors does the recursive search of the inheritance
    3834             :          * hierarchy, so all we have to do is process all of the relids in the
    3835             :          * list that it returns.
    3836             :          */
    3837         734 :         forboth(lo, child_oids, li, child_numparents)
    3838             :         {
    3839         516 :             Oid         childrelid = lfirst_oid(lo);
    3840         516 :             int         numparents = lfirst_int(li);
    3841             : 
    3842         516 :             if (childrelid == myrelid)
    3843         248 :                 continue;
    3844             :             /* note we need not recurse again */
    3845         268 :             renameatt_internal(childrelid, oldattname, newattname, false, true, numparents, behavior);
    3846             :         }
    3847             :     }
    3848             :     else
    3849             :     {
    3850             :         /*
    3851             :          * If we are told not to recurse, there had better not be any child
    3852             :          * tables; else the rename would put them out of step.
    3853             :          *
    3854             :          * expected_parents will only be 0 if we are not already recursing.
    3855             :          */
    3856         340 :         if (expected_parents == 0 &&
    3857          36 :             find_inheritance_children(myrelid, NoLock) != NIL)
    3858          12 :             ereport(ERROR,
    3859             :                     (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    3860             :                      errmsg("inherited column \"%s\" must be renamed in child tables too",
    3861             :                             oldattname)));
    3862             :     }
    3863             : 
    3864             :     /* rename attributes in typed tables of composite type */
    3865         510 :     if (targetrelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
    3866             :     {
    3867             :         List       *child_oids;
    3868             :         ListCell   *lo;
    3869             : 
    3870          24 :         child_oids = find_typed_table_dependencies(targetrelation->rd_rel->reltype,
    3871          24 :                                                    RelationGetRelationName(targetrelation),
    3872             :                                                    behavior);
    3873             : 
    3874          24 :         foreach(lo, child_oids)
    3875           6 :             renameatt_internal(lfirst_oid(lo), oldattname, newattname, true, true, 0, behavior);
    3876             :     }
    3877             : 
    3878         504 :     attrelation = table_open(AttributeRelationId, RowExclusiveLock);
    3879             : 
    3880         504 :     atttup = SearchSysCacheCopyAttName(myrelid, oldattname);
    3881         504 :     if (!HeapTupleIsValid(atttup))
    3882          24 :         ereport(ERROR,
    3883             :                 (errcode(ERRCODE_UNDEFINED_COLUMN),
    3884             :                  errmsg("column \"%s\" does not exist",
    3885             :                         oldattname)));
    3886         480 :     attform = (Form_pg_attribute) GETSTRUCT(atttup);
    3887             : 
    3888         480 :     attnum = attform->attnum;
    3889         480 :     if (attnum <= 0)
    3890           0 :         ereport(ERROR,
    3891             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    3892             :                  errmsg("cannot rename system column \"%s\"",
    3893             :                         oldattname)));
    3894             : 
    3895             :     /*
    3896             :      * if the attribute is inherited, forbid the renaming.  if this is a
    3897             :      * top-level call to renameatt(), then expected_parents will be 0, so the
    3898             :      * effect of this code will be to prohibit the renaming if the attribute
    3899             :      * is inherited at all.  if this is a recursive call to renameatt(),
    3900             :      * expected_parents will be the number of parents the current relation has
    3901             :      * within the inheritance hierarchy being processed, so we'll prohibit the
    3902             :      * renaming only if there are additional parents from elsewhere.
    3903             :      */
    3904         480 :     if (attform->attinhcount > expected_parents)
    3905          30 :         ereport(ERROR,
    3906             :                 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    3907             :                  errmsg("cannot rename inherited column \"%s\"",
    3908             :                         oldattname)));
    3909             : 
    3910             :     /* new name should not already exist */
    3911         450 :     (void) check_for_column_name_collision(targetrelation, newattname, false);
    3912             : 
    3913             :     /* apply the update */
    3914         438 :     namestrcpy(&(attform->attname), newattname);
    3915             : 
    3916         438 :     CatalogTupleUpdate(attrelation, &atttup->t_self, atttup);
    3917             : 
    3918         438 :     InvokeObjectPostAlterHook(RelationRelationId, myrelid, attnum);
    3919             : 
    3920         438 :     heap_freetuple(atttup);
    3921             : 
    3922         438 :     table_close(attrelation, RowExclusiveLock);
    3923             : 
    3924         438 :     relation_close(targetrelation, NoLock); /* close rel but keep lock */
    3925             : 
    3926         438 :     return attnum;
    3927             : }
    3928             : 
    3929             : /*
    3930             :  * Perform permissions and integrity checks before acquiring a relation lock.
    3931             :  */
    3932             : static void
    3933         430 : RangeVarCallbackForRenameAttribute(const RangeVar *rv, Oid relid, Oid oldrelid,
    3934             :                                    void *arg)
    3935             : {
    3936             :     HeapTuple   tuple;
    3937             :     Form_pg_class form;
    3938             : 
    3939         430 :     tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
    3940         430 :     if (!HeapTupleIsValid(tuple))
    3941          40 :         return;                 /* concurrently dropped */
    3942         390 :     form = (Form_pg_class) GETSTRUCT(tuple);
    3943         390 :     renameatt_check(relid, form, false);
    3944         382 :     ReleaseSysCache(tuple);
    3945             : }
    3946             : 
    3947             : /*
    3948             :  *      renameatt       - changes the name of an attribute in a relation
    3949             :  *
    3950             :  * The returned ObjectAddress is that of the renamed column.
    3951             :  */
    3952             : ObjectAddress
    3953         316 : renameatt(RenameStmt *stmt)
    3954             : {
    3955             :     Oid         relid;
    3956             :     AttrNumber  attnum;
    3957             :     ObjectAddress address;
    3958             : 
    3959             :     /* lock level taken here should match renameatt_internal */
    3960         316 :     relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
    3961         316 :                                      stmt->missing_ok ? RVR_MISSING_OK : 0,
    3962             :                                      RangeVarCallbackForRenameAttribute,
    3963             :                                      NULL);
    3964             : 
    3965         302 :     if (!OidIsValid(relid))
    3966             :     {
    3967          24 :         ereport(NOTICE,
    3968             :                 (errmsg("relation \"%s\" does not exist, skipping",
    3969             :                         stmt->relation->relname)));
    3970          24 :         return InvalidObjectAddress;
    3971             :     }
    3972             : 
    3973             :     attnum =
    3974         278 :         renameatt_internal(relid,
    3975         278 :                            stmt->subname,    /* old att name */
    3976         278 :                            stmt->newname,    /* new att name */
    3977         278 :                            stmt->relation->inh, /* recursive? */
    3978             :                            false,   /* recursing? */
    3979             :                            0,   /* expected inhcount */
    3980             :                            stmt->behavior);
    3981             : 
    3982         194 :     ObjectAddressSubSet(address, RelationRelationId, relid, attnum);
    3983             : 
    3984         194 :     return address;
    3985             : }
    3986             : 
    3987             : /*
    3988             :  * same logic as renameatt_internal
    3989             :  */
    3990             : static ObjectAddress
    3991          90 : rename_constraint_internal(Oid myrelid,
    3992             :                            Oid mytypid,
    3993             :                            const char *oldconname,
    3994             :                            const char *newconname,
    3995             :                            bool recurse,
    3996             :                            bool recursing,
    3997             :                            int expected_parents)
    3998             : {
    3999          90 :     Relation    targetrelation = NULL;
    4000             :     Oid         constraintOid;
    4001             :     HeapTuple   tuple;
    4002             :     Form_pg_constraint con;
    4003             :     ObjectAddress address;
    4004             : 
    4005             :     Assert(!myrelid || !mytypid);
    4006             : 
    4007          90 :     if (mytypid)
    4008             :     {
    4009           6 :         constraintOid = get_domain_constraint_oid(mytypid, oldconname, false);
    4010             :     }
    4011             :     else
    4012             :     {
    4013          84 :         targetrelation = relation_open(myrelid, AccessExclusiveLock);
    4014             : 
    4015             :         /*
    4016             :          * don't tell it whether we're recursing; we allow changing typed
    4017             :          * tables here
    4018             :          */
    4019          84 :         renameatt_check(myrelid, RelationGetForm(targetrelation), false);
    4020             : 
    4021          84 :         constraintOid = get_relation_constraint_oid(myrelid, oldconname, false);
    4022             :     }
    4023             : 
    4024          90 :     tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constraintOid));
    4025          90 :     if (!HeapTupleIsValid(tuple))
    4026           0 :         elog(ERROR, "cache lookup failed for constraint %u",
    4027             :              constraintOid);
    4028          90 :     con = (Form_pg_constraint) GETSTRUCT(tuple);
    4029             : 
    4030          90 :     if (myrelid &&
    4031          84 :         (con->contype == CONSTRAINT_CHECK ||
    4032          24 :          con->contype == CONSTRAINT_NOTNULL) &&
    4033          66 :         !con->connoinherit)
    4034             :     {
    4035          54 :         if (recurse)
    4036             :         {
    4037             :             List       *child_oids,
    4038             :                        *child_numparents;
    4039             :             ListCell   *lo,
    4040             :                        *li;
    4041             : 
    4042          36 :             child_oids = find_all_inheritors(myrelid, AccessExclusiveLock,
    4043             :                                              &child_numparents);
    4044             : 
    4045          84 :             forboth(lo, child_oids, li, child_numparents)
    4046             :             {
    4047          48 :                 Oid         childrelid = lfirst_oid(lo);
    4048          48 :                 int         numparents = lfirst_int(li);
    4049             : 
    4050          48 :                 if (childrelid == myrelid)
    4051          36 :                     continue;
    4052             : 
    4053          12 :                 rename_constraint_internal(childrelid, InvalidOid, oldconname, newconname, false, true, numparents);
    4054             :             }
    4055             :         }
    4056             :         else
    4057             :         {
    4058          24 :             if (expected_parents == 0 &&
    4059           6 :                 find_inheritance_children(myrelid, NoLock) != NIL)
    4060           6 :                 ereport(ERROR,
    4061             :                         (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    4062             :                          errmsg("inherited constraint \"%s\" must be renamed in child tables too",
    4063             :                                 oldconname)));
    4064             :         }
    4065             : 
    4066          48 :         if (con->coninhcount > expected_parents)
    4067           6 :             ereport(ERROR,
    4068             :                     (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    4069             :                      errmsg("cannot rename inherited constraint \"%s\"",
    4070             :                             oldconname)));
    4071             :     }
    4072             : 
    4073          78 :     if (con->conindid
    4074          18 :         && (con->contype == CONSTRAINT_PRIMARY
    4075           6 :             || con->contype == CONSTRAINT_UNIQUE
    4076           0 :             || con->contype == CONSTRAINT_EXCLUSION))
    4077             :         /* rename the index; this renames the constraint as well */
    4078          18 :         RenameRelationInternal(con->conindid, newconname, false, true);
    4079             :     else
    4080          60 :         RenameConstraintById(constraintOid, newconname);
    4081             : 
    4082          78 :     ObjectAddressSet(address, ConstraintRelationId, constraintOid);
    4083             : 
    4084          78 :     ReleaseSysCache(tuple);
    4085             : 
    4086          78 :     if (targetrelation)
    4087             :     {
    4088             :         /*
    4089             :          * Invalidate relcache so as others can see the new constraint name.
    4090             :          */
    4091          72 :         CacheInvalidateRelcache(targetrelation);
    4092             : 
    4093          72 :         relation_close(targetrelation, NoLock); /* close rel but keep lock */
    4094             :     }
    4095             : 
    4096          78 :     return address;
    4097             : }
    4098             : 
    4099             : ObjectAddress
    4100          84 : RenameConstraint(RenameStmt *stmt)
    4101             : {
    4102          84 :     Oid         relid = InvalidOid;
    4103          84 :     Oid         typid = InvalidOid;
    4104             : 
    4105          84 :     if (stmt->renameType == OBJECT_DOMCONSTRAINT)
    4106             :     {
    4107             :         Relation    rel;
    4108             :         HeapTuple   tup;
    4109             : 
    4110           6 :         typid = typenameTypeId(NULL, makeTypeNameFromNameList(castNode(List, stmt->object)));
    4111           6 :         rel = table_open(TypeRelationId, RowExclusiveLock);
    4112           6 :         tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
    4113           6 :         if (!HeapTupleIsValid(tup))
    4114           0 :             elog(ERROR, "cache lookup failed for type %u", typid);
    4115           6 :         checkDomainOwner(tup);
    4116           6 :         ReleaseSysCache(tup);
    4117           6 :         table_close(rel, NoLock);
    4118             :     }
    4119             :     else
    4120             :     {
    4121             :         /* lock level taken here should match rename_constraint_internal */
    4122          78 :         relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
    4123          78 :                                          stmt->missing_ok ? RVR_MISSING_OK : 0,
    4124             :                                          RangeVarCallbackForRenameAttribute,
    4125             :                                          NULL);
    4126          78 :         if (!OidIsValid(relid))
    4127             :         {
    4128           6 :             ereport(NOTICE,
    4129             :                     (errmsg("relation \"%s\" does not exist, skipping",
    4130             :                             stmt->relation->relname)));
    4131           6 :             return InvalidObjectAddress;
    4132             :         }
    4133             :     }
    4134             : 
    4135             :     return
    4136          78 :         rename_constraint_internal(relid, typid,
    4137          78 :                                    stmt->subname,
    4138          78 :                                    stmt->newname,
    4139         150 :                                    (stmt->relation &&
    4140          72 :                                     stmt->relation->inh), /* recursive? */
    4141             :                                    false,   /* recursing? */
    4142             :                                    0 /* expected inhcount */ );
    4143             : }
    4144             : 
    4145             : /*
    4146             :  * Execute ALTER TABLE/INDEX/SEQUENCE/VIEW/MATERIALIZED VIEW/FOREIGN TABLE
    4147             :  * RENAME
    4148             :  */
    4149             : ObjectAddress
    4150         510 : RenameRelation(RenameStmt *stmt)
    4151             : {
    4152         510 :     bool        is_index_stmt = stmt->renameType == OBJECT_INDEX;
    4153             :     Oid         relid;
    4154             :     ObjectAddress address;
    4155             : 
    4156             :     /*
    4157             :      * Grab an exclusive lock on the target table, index, sequence, view,
    4158             :      * materialized view, or foreign table, which we will NOT release until
    4159             :      * end of transaction.
    4160             :      *
    4161             :      * Lock level used here should match RenameRelationInternal, to avoid lock
    4162             :      * escalation.  However, because ALTER INDEX can be used with any relation
    4163             :      * type, we mustn't believe without verification.
    4164             :      */
    4165             :     for (;;)
    4166          12 :     {
    4167             :         LOCKMODE    lockmode;
    4168             :         char        relkind;
    4169             :         bool        obj_is_index;
    4170             : 
    4171         522 :         lockmode = is_index_stmt ? ShareUpdateExclusiveLock : AccessExclusiveLock;
    4172             : 
    4173         522 :         relid = RangeVarGetRelidExtended(stmt->relation, lockmode,
    4174         522 :                                          stmt->missing_ok ? RVR_MISSING_OK : 0,
    4175             :                                          RangeVarCallbackForAlterRelation,
    4176             :                                          stmt);
    4177             : 
    4178         472 :         if (!OidIsValid(relid))
    4179             :         {
    4180          18 :             ereport(NOTICE,
    4181             :                     (errmsg("relation \"%s\" does not exist, skipping",
    4182             :                             stmt->relation->relname)));
    4183          18 :             return InvalidObjectAddress;
    4184             :         }
    4185             : 
    4186             :         /*
    4187             :          * We allow mismatched statement and object types (e.g., ALTER INDEX
    4188             :          * to rename a table), but we might've used the wrong lock level.  If
    4189             :          * that happens, retry with the correct lock level.  We don't bother
    4190             :          * if we already acquired AccessExclusiveLock with an index, however.
    4191             :          */
    4192         454 :         relkind = get_rel_relkind(relid);
    4193         454 :         obj_is_index = (relkind == RELKIND_INDEX ||
    4194             :                         relkind == RELKIND_PARTITIONED_INDEX);
    4195         454 :         if (obj_is_index || is_index_stmt == obj_is_index)
    4196             :             break;
    4197             : 
    4198          12 :         UnlockRelationOid(relid, lockmode);
    4199          12 :         is_index_stmt = obj_is_index;
    4200             :     }
    4201             : 
    4202             :     /* Do the work */
    4203         442 :     RenameRelationInternal(relid, stmt->newname, false, is_index_stmt);
    4204             : 
    4205         430 :     ObjectAddressSet(address, RelationRelationId, relid);
    4206             : 
    4207         430 :     return address;
    4208             : }
    4209             : 
    4210             : /*
    4211             :  *      RenameRelationInternal - change the name of a relation
    4212             :  */
    4213             : void
    4214        1638 : RenameRelationInternal(Oid myrelid, const char *newrelname, bool is_internal, bool is_index)
    4215             : {
    4216             :     Relation    targetrelation;
    4217             :     Relation    relrelation;    /* for RELATION relation */
    4218             :     ItemPointerData otid;
    4219             :     HeapTuple   reltup;
    4220             :     Form_pg_class relform;
    4221             :     Oid         namespaceId;
    4222             : 
    4223             :     /*
    4224             :      * Grab a lock on the target relation, which we will NOT release until end
    4225             :      * of transaction.  We need at least a self-exclusive lock so that
    4226             :      * concurrent DDL doesn't overwrite the rename if they start updating
    4227             :      * while still seeing the old version.  The lock also guards against
    4228             :      * triggering relcache reloads in concurrent sessions, which might not
    4229             :      * handle this information changing under them.  For indexes, we can use a
    4230             :      * reduced lock level because RelationReloadIndexInfo() handles indexes
    4231             :      * specially.
    4232             :      */
    4233        1638 :     targetrelation = relation_open(myrelid, is_index ? ShareUpdateExclusiveLock : AccessExclusiveLock);
    4234        1638 :     namespaceId = RelationGetNamespace(targetrelation);
    4235             : 
    4236             :     /*
    4237             :      * Find relation's pg_class tuple, and make sure newrelname isn't in use.
    4238             :      */
    4239        1638 :     relrelation = table_open(RelationRelationId, RowExclusiveLock);
    4240             : 
    4241        1638 :     reltup = SearchSysCacheLockedCopy1(RELOID, ObjectIdGetDatum(myrelid));
    4242        1638 :     if (!HeapTupleIsValid(reltup))  /* shouldn't happen */
    4243           0 :         elog(ERROR, "cache lookup failed for relation %u", myrelid);
    4244        1638 :     otid = reltup->t_self;
    4245        1638 :     relform = (Form_pg_class) GETSTRUCT(reltup);
    4246             : 
    4247        1638 :     if (get_relname_relid(newrelname, namespaceId) != InvalidOid)
    4248          12 :         ereport(ERROR,
    4249             :                 (errcode(ERRCODE_DUPLICATE_TABLE),
    4250             :                  errmsg("relation \"%s\" already exists",
    4251             :                         newrelname)));
    4252             : 
    4253             :     /*
    4254             :      * RenameRelation is careful not to believe the caller's idea of the
    4255             :      * relation kind being handled.  We don't have to worry about this, but
    4256             :      * let's not be totally oblivious to it.  We can process an index as
    4257             :      * not-an-index, but not the other way around.
    4258             :      */
    4259             :     Assert(!is_index ||
    4260             :            is_index == (targetrelation->rd_rel->relkind == RELKIND_INDEX ||
    4261             :                         targetrelation->rd_rel->relkind == RELKIND_PARTITIONED_INDEX));
    4262             : 
    4263             :     /*
    4264             :      * Update pg_class tuple with new relname.  (Scribbling on reltup is OK
    4265             :      * because it's a copy...)
    4266             :      */
    4267        1626 :     namestrcpy(&(relform->relname), newrelname);
    4268             : 
    4269        1626 :     CatalogTupleUpdate(relrelation, &otid, reltup);
    4270        1626 :     UnlockTuple(relrelation, &otid, InplaceUpdateTupleLock);
    4271             : 
    4272        1626 :     InvokeObjectPostAlterHookArg(RelationRelationId, myrelid, 0,
    4273             :                                  InvalidOid, is_internal);
    4274             : 
    4275        1626 :     heap_freetuple(reltup);
    4276        1626 :     table_close(relrelation, RowExclusiveLock);
    4277             : 
    4278             :     /*
    4279             :      * Also rename the associated type, if any.
    4280             :      */
    4281        1626 :     if (OidIsValid(targetrelation->rd_rel->reltype))
    4282         124 :         RenameTypeInternal(targetrelation->rd_rel->reltype,
    4283             :                            newrelname, namespaceId);
    4284             : 
    4285             :     /*
    4286             :      * Also rename the associated constraint, if any.
    4287             :      */
    4288        1626 :     if (targetrelation->rd_rel->relkind == RELKIND_INDEX ||
    4289         854 :         targetrelation->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
    4290             :     {
    4291         790 :         Oid         constraintId = get_index_constraint(myrelid);
    4292             : 
    4293         790 :         if (OidIsValid(constraintId))
    4294          36 :             RenameConstraintById(constraintId, newrelname);
    4295             :     }
    4296             : 
    4297             :     /*
    4298             :      * Close rel, but keep lock!
    4299             :      */
    4300        1626 :     relation_close(targetrelation, NoLock);
    4301        1626 : }
    4302             : 
    4303             : /*
    4304             :  *      ResetRelRewrite - reset relrewrite
    4305             :  */
    4306             : void
    4307         578 : ResetRelRewrite(Oid myrelid)
    4308             : {
    4309             :     Relation    relrelation;    /* for RELATION relation */
    4310             :     HeapTuple   reltup;
    4311             :     Form_pg_class relform;
    4312             : 
    4313             :     /*
    4314             :      * Find relation's pg_class tuple.
    4315             :      */
    4316         578 :     relrelation = table_open(RelationRelationId, RowExclusiveLock);
    4317             : 
    4318         578 :     reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(myrelid));
    4319         578 :     if (!HeapTupleIsValid(reltup))  /* shouldn't happen */
    4320           0 :         elog(ERROR, "cache lookup failed for relation %u", myrelid);
    4321         578 :     relform = (Form_pg_class) GETSTRUCT(reltup);
    4322             : 
    4323             :     /*
    4324             :      * Update pg_class tuple.
    4325             :      */
    4326         578 :     relform->relrewrite = InvalidOid;
    4327             : 
    4328         578 :     CatalogTupleUpdate(relrelation, &reltup->t_self, reltup);
    4329             : 
    4330         578 :     heap_freetuple(reltup);
    4331         578 :     table_close(relrelation, RowExclusiveLock);
    4332         578 : }
    4333             : 
    4334             : /*
    4335             :  * Disallow ALTER TABLE (and similar commands) when the current backend has
    4336             :  * any open reference to the target table besides the one just acquired by
    4337             :  * the calling command; this implies there's an open cursor or active plan.
    4338             :  * We need this check because our lock doesn't protect us against stomping
    4339             :  * on our own foot, only other people's feet!
    4340             :  *
    4341             :  * For ALTER TABLE, the only case known to cause serious trouble is ALTER
    4342             :  * COLUMN TYPE, and some changes are obviously pretty benign, so this could
    4343             :  * possibly be relaxed to only error out for certain types of alterations.
    4344             :  * But the use-case for allowing any of these things is not obvious, so we
    4345             :  * won't work hard at it for now.
    4346             :  *
    4347             :  * We also reject these commands if there are any pending AFTER trigger events
    4348             :  * for the rel.  This is certainly necessary for the rewriting variants of
    4349             :  * ALTER TABLE, because they don't preserve tuple TIDs and so the pending
    4350             :  * events would try to fetch the wrong tuples.  It might be overly cautious
    4351             :  * in other cases, but again it seems better to err on the side of paranoia.
    4352             :  *
    4353             :  * REINDEX calls this with "rel" referencing the index to be rebuilt; here
    4354             :  * we are worried about active indexscans on the index.  The trigger-event
    4355             :  * check can be skipped, since we are doing no damage to the parent table.
    4356             :  *
    4357             :  * The statement name (eg, "ALTER TABLE") is passed for use in error messages.
    4358             :  */
    4359             : void
    4360      158712 : CheckTableNotInUse(Relation rel, const char *stmt)
    4361             : {
    4362             :     int         expected_refcnt;
    4363             : 
    4364      158712 :     expected_refcnt = rel->rd_isnailed ? 2 : 1;
    4365      158712 :     if (rel->rd_refcnt != expected_refcnt)
    4366          30 :         ereport(ERROR,
    4367             :                 (errcode(ERRCODE_OBJECT_IN_USE),
    4368             :         /* translator: first %s is a SQL command, eg ALTER TABLE */
    4369             :                  errmsg("cannot %s \"%s\" because it is being used by active queries in this session",
    4370             :                         stmt, RelationGetRelationName(rel))));
    4371             : 
    4372      158682 :     if (rel->rd_rel->relkind != RELKIND_INDEX &&
    4373      256566 :         rel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX &&
    4374      127260 :         AfterTriggerPendingOnRel(RelationGetRelid(rel)))
    4375          18 :         ereport(ERROR,
    4376             :                 (errcode(ERRCODE_OBJECT_IN_USE),
    4377             :         /* translator: first %s is a SQL command, eg ALTER TABLE */
    4378             :                  errmsg("cannot %s \"%s\" because it has pending trigger events",
    4379             :                         stmt, RelationGetRelationName(rel))));
    4380      158664 : }
    4381             : 
    4382             : /*
    4383             :  * CheckAlterTableIsSafe
    4384             :  *      Verify that it's safe to allow ALTER TABLE on this relation.
    4385             :  *
    4386             :  * This consists of CheckTableNotInUse() plus a check that the relation
    4387             :  * isn't another session's temp table.  We must split out the temp-table
    4388             :  * check because there are callers of CheckTableNotInUse() that don't want
    4389             :  * that, notably DROP TABLE.  (We must allow DROP or we couldn't clean out
    4390             :  * an orphaned temp schema.)  Compare truncate_check_activity().
    4391             :  */
    4392             : static void
    4393       55708 : CheckAlterTableIsSafe(Relation rel)
    4394             : {
    4395             :     /*
    4396             :      * Don't allow ALTER on temp tables of other backends.  Their local buffer
    4397             :      * manager is not going to cope if we need to change the table's contents.
    4398             :      * Even if we don't, there may be optimizations that assume temp tables
    4399             :      * aren't subject to such interference.
    4400             :      */
    4401       55708 :     if (RELATION_IS_OTHER_TEMP(rel))
    4402           0 :         ereport(ERROR,
    4403             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    4404             :                  errmsg("cannot alter temporary tables of other sessions")));
    4405             : 
    4406             :     /*
    4407             :      * Also check for active uses of the relation in the current transaction,
    4408             :      * including open scans and pending AFTER trigger events.
    4409             :      */
    4410       55708 :     CheckTableNotInUse(rel, "ALTER TABLE");
    4411       55672 : }
    4412             : 
    4413             : /*
    4414             :  * AlterTableLookupRelation
    4415             :  *      Look up, and lock, the OID for the relation named by an alter table
    4416             :  *      statement.
    4417             :  */
    4418             : Oid
    4419       29284 : AlterTableLookupRelation(AlterTableStmt *stmt, LOCKMODE lockmode)
    4420             : {
    4421       58482 :     return RangeVarGetRelidExtended(stmt->relation, lockmode,
    4422       29284 :                                     stmt->missing_ok ? RVR_MISSING_OK : 0,
    4423             :                                     RangeVarCallbackForAlterRelation,
    4424             :                                     stmt);
    4425             : }
    4426             : 
    4427             : /*
    4428             :  * AlterTable
    4429             :  *      Execute ALTER TABLE, which can be a list of subcommands
    4430             :  *
    4431             :  * ALTER TABLE is performed in three phases:
    4432             :  *      1. Examine subcommands and perform pre-transformation checking.
    4433             :  *      2. Validate and transform subcommands, and update system catalogs.
    4434             :  *      3. Scan table(s) to check new constraints, and optionally recopy
    4435             :  *         the data into new table(s).
    4436             :  * Phase 3 is not performed unless one or more of the subcommands requires
    4437             :  * it.  The intention of this design is to allow multiple independent
    4438             :  * updates of the table schema to be performed with only one pass over the
    4439             :  * data.
    4440             :  *
    4441             :  * ATPrepCmd performs phase 1.  A "work queue" entry is created for
    4442             :  * each table to be affected (there may be multiple affected tables if the
    4443             :  * commands traverse a table inheritance hierarchy).  Also we do preliminary
    4444             :  * validation of the subcommands.  Because earlier subcommands may change
    4445             :  * the catalog state seen by later commands, there are limits to what can
    4446             :  * be done in this phase.  Generally, this phase acquires table locks,
    4447             :  * checks permissions and relkind, and recurses to find child tables.
    4448             :  *
    4449             :  * ATRewriteCatalogs performs phase 2 for each affected table.
    4450             :  * Certain subcommands need to be performed before others to avoid
    4451             :  * unnecessary conflicts; for example, DROP COLUMN should come before
    4452             :  * ADD COLUMN.  Therefore phase 1 divides the subcommands into multiple
    4453             :  * lists, one for each logical "pass" of phase 2.
    4454             :  *
    4455             :  * ATRewriteTables performs phase 3 for those tables that need it.
    4456             :  *
    4457             :  * For most subcommand types, phases 2 and 3 do no explicit recursion,
    4458             :  * since phase 1 already does it.  However, for certain subcommand types
    4459             :  * it is only possible to determine how to recurse at phase 2 time; for
    4460             :  * those cases, phase 1 sets the cmd->recurse flag.
    4461             :  *
    4462             :  * Thanks to the magic of MVCC, an error anywhere along the way rolls back
    4463             :  * the whole operation; we don't have to do anything special to clean up.
    4464             :  *
    4465             :  * The caller must lock the relation, with an appropriate lock level
    4466             :  * for the subcommands requested, using AlterTableGetLockLevel(stmt->cmds)
    4467             :  * or higher. We pass the lock level down
    4468             :  * so that we can apply it recursively to inherited tables. Note that the
    4469             :  * lock level we want as we recurse might well be higher than required for
    4470             :  * that specific subcommand. So we pass down the overall lock requirement,
    4471             :  * rather than reassess it at lower levels.
    4472             :  *
    4473             :  * The caller also provides a "context" which is to be passed back to
    4474             :  * utility.c when we need to execute a subcommand such as CREATE INDEX.
    4475             :  * Some of the fields therein, such as the relid, are used here as well.
    4476             :  */
    4477             : void
    4478       29060 : AlterTable(AlterTableStmt *stmt, LOCKMODE lockmode,
    4479             :            AlterTableUtilityContext *context)
    4480             : {
    4481             :     Relation    rel;
    4482             : 
    4483             :     /* Caller is required to provide an adequate lock. */
    4484       29060 :     rel = relation_open(context->relid, NoLock);
    4485             : 
    4486       29060 :     CheckAlterTableIsSafe(rel);
    4487             : 
    4488       29042 :     ATController(stmt, rel, stmt->cmds, stmt->relation->inh, lockmode, context);
    4489       25816 : }
    4490             : 
    4491             : /*
    4492             :  * AlterTableInternal
    4493             :  *
    4494             :  * ALTER TABLE with target specified by OID
    4495             :  *
    4496             :  * We do not reject if the relation is already open, because it's quite
    4497             :  * likely that one or more layers of caller have it open.  That means it
    4498             :  * is unsafe to use this entry point for alterations that could break
    4499             :  * existing query plans.  On the assumption it's not used for such, we
    4500             :  * don't have to reject pending AFTER triggers, either.
    4501             :  *
    4502             :  * Also, since we don't have an AlterTableUtilityContext, this cannot be
    4503             :  * used for any subcommand types that require parse transformation or
    4504             :  * could generate subcommands that have to be passed to ProcessUtility.
    4505             :  */
    4506             : void
    4507         278 : AlterTableInternal(Oid relid, List *cmds, bool recurse)
    4508             : {
    4509             :     Relation    rel;
    4510         278 :     LOCKMODE    lockmode = AlterTableGetLockLevel(cmds);
    4511             : 
    4512         278 :     rel = relation_open(relid, lockmode);
    4513             : 
    4514         278 :     EventTriggerAlterTableRelid(relid);
    4515             : 
    4516         278 :     ATController(NULL, rel, cmds, recurse, lockmode, NULL);
    4517         278 : }
    4518             : 
    4519             : /*
    4520             :  * AlterTableGetLockLevel
    4521             :  *
    4522             :  * Sets the overall lock level required for the supplied list of subcommands.
    4523             :  * Policy for doing this set according to needs of AlterTable(), see
    4524             :  * comments there for overall explanation.
    4525             :  *
    4526             :  * Function is called before and after parsing, so it must give same
    4527             :  * answer each time it is called. Some subcommands are transformed
    4528             :  * into other subcommand types, so the transform must never be made to a
    4529             :  * lower lock level than previously assigned. All transforms are noted below.
    4530             :  *
    4531             :  * Since this is called before we lock the table we cannot use table metadata
    4532             :  * to influence the type of lock we acquire.
    4533             :  *
    4534             :  * There should be no lockmodes hardcoded into the subcommand functions. All
    4535             :  * lockmode decisions for ALTER TABLE are made here only. The one exception is
    4536             :  * ALTER TABLE RENAME which is treated as a different statement type T_RenameStmt
    4537             :  * and does not travel through this section of code and cannot be combined with
    4538             :  * any of the subcommands given here.
    4539             :  *
    4540             :  * Note that Hot Standby only knows about AccessExclusiveLocks on the primary
    4541             :  * so any changes that might affect SELECTs running on standbys need to use
    4542             :  * AccessExclusiveLocks even if you think a lesser lock would do, unless you
    4543             :  * have a solution for that also.
    4544             :  *
    4545             :  * Also note that pg_dump uses only an AccessShareLock, meaning that anything
    4546             :  * that takes a lock less than AccessExclusiveLock can change object definitions
    4547             :  * while pg_dump is running. Be careful to check that the appropriate data is
    4548             :  * derived by pg_dump using an MVCC snapshot, rather than syscache lookups,
    4549             :  * otherwise we might end up with an inconsistent dump that can't restore.
    4550             :  */
    4551             : LOCKMODE
    4552       29562 : AlterTableGetLockLevel(List *cmds)
    4553             : {
    4554             :     /*
    4555             :      * This only works if we read catalog tables using MVCC snapshots.
    4556             :      */
    4557             :     ListCell   *lcmd;
    4558       29562 :     LOCKMODE    lockmode = ShareUpdateExclusiveLock;
    4559             : 
    4560       60194 :     foreach(lcmd, cmds)
    4561             :     {
    4562       30632 :         AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);
    4563       30632 :         LOCKMODE    cmd_lockmode = AccessExclusiveLock; /* default for compiler */
    4564             : 
    4565       30632 :         switch (cmd->subtype)
    4566             :         {
    4567             :                 /*
    4568             :                  * These subcommands rewrite the heap, so require full locks.
    4569             :                  */
    4570        3278 :             case AT_AddColumn:  /* may rewrite heap, in some cases and visible
    4571             :                                  * to SELECT */
    4572             :             case AT_SetAccessMethod:    /* must rewrite heap */
    4573             :             case AT_SetTableSpace:  /* must rewrite heap */
    4574             :             case AT_AlterColumnType:    /* must rewrite heap */
    4575        3278 :                 cmd_lockmode = AccessExclusiveLock;
    4576        3278 :                 break;
    4577             : 
    4578             :                 /*
    4579             :                  * These subcommands may require addition of toast tables. If
    4580             :                  * we add a toast table to a table currently being scanned, we
    4581             :                  * might miss data added to the new toast table by concurrent
    4582             :                  * insert transactions.
    4583             :                  */
    4584         212 :             case AT_SetStorage: /* may add toast tables, see
    4585             :                                  * ATRewriteCatalogs() */
    4586         212 :                 cmd_lockmode = AccessExclusiveLock;
    4587         212 :                 break;
    4588             : 
    4589             :                 /*
    4590             :                  * Removing constraints can affect SELECTs that have been
    4591             :                  * optimized assuming the constraint holds true. See also
    4592             :                  * CloneFkReferenced.
    4593             :                  */
    4594        1122 :             case AT_DropConstraint: /* as DROP INDEX */
    4595             :             case AT_DropNotNull:    /* may change some SQL plans */
    4596        1122 :                 cmd_lockmode = AccessExclusiveLock;
    4597        1122 :                 break;
    4598             : 
    4599             :                 /*
    4600             :                  * Subcommands that may be visible to concurrent SELECTs
    4601             :                  */
    4602        1704 :             case AT_DropColumn: /* change visible to SELECT */
    4603             :             case AT_AddColumnToView:    /* CREATE VIEW */
    4604             :             case AT_DropOids:   /* used to equiv to DropColumn */
    4605             :             case AT_EnableAlwaysRule:   /* may change SELECT rules */
    4606             :             case AT_EnableReplicaRule:  /* may change SELECT rules */
    4607             :             case AT_EnableRule: /* may change SELECT rules */
    4608             :             case AT_DisableRule:    /* may change SELECT rules */
    4609        1704 :                 cmd_lockmode = AccessExclusiveLock;
    4610        1704 :                 break;
    4611             : 
    4612             :                 /*
    4613             :                  * Changing owner may remove implicit SELECT privileges
    4614             :                  */
    4615        1820 :             case AT_ChangeOwner:    /* change visible to SELECT */
    4616        1820 :                 cmd_lockmode = AccessExclusiveLock;
    4617        1820 :                 break;
    4618             : 
    4619             :                 /*
    4620             :                  * Changing foreign table options may affect optimization.
    4621             :                  */
    4622         254 :             case AT_GenericOptions:
    4623             :             case AT_AlterColumnGenericOptions:
    4624         254 :                 cmd_lockmode = AccessExclusiveLock;
    4625         254 :                 break;
    4626             : 
    4627             :                 /*
    4628             :                  * These subcommands affect write operations only.
    4629             :                  */
    4630         340 :             case AT_EnableTrig:
    4631             :             case AT_EnableAlwaysTrig:
    4632             :             case AT_EnableReplicaTrig:
    4633             :             case AT_EnableTrigAll:
    4634             :             case AT_EnableTrigUser:
    4635             :             case AT_DisableTrig:
    4636             :             case AT_DisableTrigAll:
    4637             :             case AT_DisableTrigUser:
    4638         340 :                 cmd_lockmode = ShareRowExclusiveLock;
    4639         340 :                 break;
    4640             : 
    4641             :                 /*
    4642             :                  * These subcommands affect write operations only. XXX
    4643             :                  * Theoretically, these could be ShareRowExclusiveLock.
    4644             :                  */
    4645        2474 :             case AT_ColumnDefault:
    4646             :             case AT_CookedColumnDefault:
    4647             :             case AT_AlterConstraint:
    4648             :             case AT_AddIndex:   /* from ADD CONSTRAINT */
    4649             :             case AT_AddIndexConstraint:
    4650             :             case AT_ReplicaIdentity:
    4651             :             case AT_SetNotNull:
    4652             :             case AT_EnableRowSecurity:
    4653             :             case AT_DisableRowSecurity:
    4654             :             case AT_ForceRowSecurity:
    4655             :             case AT_NoForceRowSecurity:
    4656             :             case AT_AddIdentity:
    4657             :             case AT_DropIdentity:
    4658             :             case AT_SetIdentity:
    4659             :             case AT_SetExpression:
    4660             :             case AT_DropExpression:
    4661             :             case AT_SetCompression:
    4662        2474 :                 cmd_lockmode = AccessExclusiveLock;
    4663        2474 :                 break;
    4664             : 
    4665       14016 :             case AT_AddConstraint:
    4666             :             case AT_ReAddConstraint:    /* becomes AT_AddConstraint */
    4667             :             case AT_ReAddDomainConstraint:  /* becomes AT_AddConstraint */
    4668       14016 :                 if (IsA(cmd->def, Constraint))
    4669             :                 {
    4670       14016 :                     Constraint *con = (Constraint *) cmd->def;
    4671             : 
    4672       14016 :                     switch (con->contype)
    4673             :                     {
    4674       10742 :                         case CONSTR_EXCLUSION:
    4675             :                         case CONSTR_PRIMARY:
    4676             :                         case CONSTR_UNIQUE:
    4677             : 
    4678             :                             /*
    4679             :                              * Cases essentially the same as CREATE INDEX. We
    4680             :                              * could reduce the lock strength to ShareLock if
    4681             :                              * we can work out how to allow concurrent catalog
    4682             :                              * updates. XXX Might be set down to
    4683             :                              * ShareRowExclusiveLock but requires further
    4684             :                              * analysis.
    4685             :                              */
    4686       10742 :                             cmd_lockmode = AccessExclusiveLock;
    4687       10742 :                             break;
    4688        2440 :                         case CONSTR_FOREIGN:
    4689             : 
    4690             :                             /*
    4691             :                              * We add triggers to both tables when we add a
    4692             :                              * Foreign Key, so the lock level must be at least
    4693             :                              * as strong as CREATE TRIGGER.
    4694             :                              */
    4695        2440 :                             cmd_lockmode = ShareRowExclusiveLock;
    4696        2440 :                             break;
    4697             : 
    4698         834 :                         default:
    4699         834 :                             cmd_lockmode = AccessExclusiveLock;
    4700             :                     }
    4701           0 :                 }
    4702       14016 :                 break;
    4703             : 
    4704             :                 /*
    4705             :                  * These subcommands affect inheritance behaviour. Queries
    4706             :                  * started before us will continue to see the old inheritance
    4707             :                  * behaviour, while queries started after we commit will see
    4708             :                  * new behaviour. No need to prevent reads or writes to the
    4709             :                  * subtable while we hook it up though. Changing the TupDesc
    4710             :                  * may be a problem, so keep highest lock.
    4711             :                  */
    4712         432 :             case AT_AddInherit:
    4713             :             case AT_DropInherit:
    4714         432 :                 cmd_lockmode = AccessExclusiveLock;
    4715         432 :                 break;
    4716             : 
    4717             :                 /*
    4718             :                  * These subcommands affect implicit row type conversion. They
    4719             :                  * have affects similar to CREATE/DROP CAST on queries. don't
    4720             :                  * provide for invalidating parse trees as a result of such
    4721             :                  * changes, so we keep these at AccessExclusiveLock.
    4722             :                  */
    4723          72 :             case AT_AddOf:
    4724             :             case AT_DropOf:
    4725          72 :                 cmd_lockmode = AccessExclusiveLock;
    4726          72 :                 break;
    4727             : 
    4728             :                 /*
    4729             :                  * Only used by CREATE OR REPLACE VIEW which must conflict
    4730             :                  * with an SELECTs currently using the view.
    4731             :                  */
    4732         194 :             case AT_ReplaceRelOptions:
    4733         194 :                 cmd_lockmode = AccessExclusiveLock;
    4734         194 :                 break;
    4735             : 
    4736             :                 /*
    4737             :                  * These subcommands affect general strategies for performance
    4738             :                  * and maintenance, though don't change the semantic results
    4739             :                  * from normal data reads and writes. Delaying an ALTER TABLE
    4740             :                  * behind currently active writes only delays the point where
    4741             :                  * the new strategy begins to take effect, so there is no
    4742             :                  * benefit in waiting. In this case the minimum restriction
    4743             :                  * applies: we don't currently allow concurrent catalog
    4744             :                  * updates.
    4745             :                  */
    4746         234 :             case AT_SetStatistics:  /* Uses MVCC in getTableAttrs() */
    4747             :             case AT_ClusterOn:  /* Uses MVCC in getIndexes() */
    4748             :             case AT_DropCluster:    /* Uses MVCC in getIndexes() */
    4749             :             case AT_SetOptions: /* Uses MVCC in getTableAttrs() */
    4750             :             case AT_ResetOptions:   /* Uses MVCC in getTableAttrs() */
    4751         234 :                 cmd_lockmode = ShareUpdateExclusiveLock;
    4752         234 :                 break;
    4753             : 
    4754         112 :             case AT_SetLogged:
    4755             :             case AT_SetUnLogged:
    4756         112 :                 cmd_lockmode = AccessExclusiveLock;
    4757         112 :                 break;
    4758             : 
    4759         394 :             case AT_ValidateConstraint: /* Uses MVCC in getConstraints() */
    4760         394 :                 cmd_lockmode = ShareUpdateExclusiveLock;
    4761         394 :                 break;
    4762             : 
    4763             :                 /*
    4764             :                  * Rel options are more complex than first appears. Options
    4765             :                  * are set here for tables, views and indexes; for historical
    4766             :                  * reasons these can all be used with ALTER TABLE, so we can't
    4767             :                  * decide between them using the basic grammar.
    4768             :                  */
    4769         752 :             case AT_SetRelOptions:  /* Uses MVCC in getIndexes() and
    4770             :                                      * getTables() */
    4771             :             case AT_ResetRelOptions:    /* Uses MVCC in getIndexes() and
    4772             :                                          * getTables() */
    4773         752 :                 cmd_lockmode = AlterTableGetRelOptionsLockLevel((List *) cmd->def);
    4774         752 :                 break;
    4775             : 
    4776        2638 :             case AT_AttachPartition:
    4777        2638 :                 cmd_lockmode = ShareUpdateExclusiveLock;
    4778        2638 :                 break;
    4779             : 
    4780         564 :             case AT_DetachPartition:
    4781         564 :                 if (((PartitionCmd *) cmd->def)->concurrent)
    4782         164 :                     cmd_lockmode = ShareUpdateExclusiveLock;
    4783             :                 else
    4784         400 :                     cmd_lockmode = AccessExclusiveLock;
    4785         564 :                 break;
    4786             : 
    4787          20 :             case AT_DetachPartitionFinalize:
    4788          20 :                 cmd_lockmode = ShareUpdateExclusiveLock;
    4789          20 :                 break;
    4790             : 
    4791           0 :             default:            /* oops */
    4792           0 :                 elog(ERROR, "unrecognized alter table type: %d",
    4793             :                      (int) cmd->subtype);
    4794             :                 break;
    4795             :         }
    4796             : 
    4797             :         /*
    4798             :          * Take the greatest lockmode from any subcommand
    4799             :          */
    4800       30632 :         if (cmd_lockmode > lockmode)
    4801       25546 :             lockmode = cmd_lockmode;
    4802             :     }
    4803             : 
    4804       29562 :     return lockmode;
    4805             : }
    4806             : 
    4807             : /*
    4808             :  * ATController provides top level control over the phases.
    4809             :  *
    4810             :  * parsetree is passed in to allow it to be passed to event triggers
    4811             :  * when requested.
    4812             :  */
    4813             : static void
    4814       29320 : ATController(AlterTableStmt *parsetree,
    4815             :              Relation rel, List *cmds, bool recurse, LOCKMODE lockmode,
    4816             :              AlterTableUtilityContext *context)
    4817             : {
    4818       29320 :     List       *wqueue = NIL;
    4819             :     ListCell   *lcmd;
    4820             : 
    4821             :     /* Phase 1: preliminary examination of commands, create work queue */
    4822       59366 :     foreach(lcmd, cmds)
    4823             :     {
    4824       30384 :         AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);
    4825             : 
    4826       30384 :         ATPrepCmd(&wqueue, rel, cmd, recurse, false, lockmode, context);
    4827             :     }
    4828             : 
    4829             :     /* Close the relation, but keep lock until commit */
    4830       28982 :     relation_close(rel, NoLock);
    4831             : 
    4832             :     /* Phase 2: update system catalogs */
    4833       28982 :     ATRewriteCatalogs(&wqueue, lockmode, context);
    4834             : 
    4835             :     /* Phase 3: scan/rewrite tables as needed, and run afterStmts */
    4836       26424 :     ATRewriteTables(parsetree, &wqueue, lockmode, context);
    4837       26094 : }
    4838             : 
    4839             : /*
    4840             :  * ATPrepCmd
    4841             :  *
    4842             :  * Traffic cop for ALTER TABLE Phase 1 operations, including simple
    4843             :  * recursion and permission checks.
    4844             :  *
    4845             :  * Caller must have acquired appropriate lock type on relation already.
    4846             :  * This lock should be held until commit.
    4847             :  */
    4848             : static void
    4849       31746 : ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
    4850             :           bool recurse, bool recursing, LOCKMODE lockmode,
    4851             :           AlterTableUtilityContext *context)
    4852             : {
    4853             :     AlteredTableInfo *tab;
    4854       31746 :     AlterTablePass pass = AT_PASS_UNSET;
    4855             : 
    4856             :     /* Find or create work queue entry for this table */
    4857       31746 :     tab = ATGetQueueEntry(wqueue, rel);
    4858             : 
    4859             :     /*
    4860             :      * Disallow any ALTER TABLE other than ALTER TABLE DETACH FINALIZE on
    4861             :      * partitions that are pending detach.
    4862             :      */
    4863       31746 :     if (rel->rd_rel->relispartition &&
    4864        2712 :         cmd->subtype != AT_DetachPartitionFinalize &&
    4865        1356 :         PartitionHasPendingDetach(RelationGetRelid(rel)))
    4866           2 :         ereport(ERROR,
    4867             :                 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    4868             :                 errmsg("cannot alter partition \"%s\" with an incomplete detach",
    4869             :                        RelationGetRelationName(rel)),
    4870             :                 errhint("Use ALTER TABLE ... DETACH PARTITION ... FINALIZE to complete the pending detach operation."));
    4871             : 
    4872             :     /*
    4873             :      * Copy the original subcommand for each table, so we can scribble on it.
    4874             :      * This avoids conflicts when different child tables need to make
    4875             :      * different parse transformations (for example, the same column may have
    4876             :      * different column numbers in different children).
    4877             :      */
    4878       31744 :     cmd = copyObject(cmd);
    4879             : 
    4880             :     /*
    4881             :      * Do permissions and relkind checking, recursion to child tables if
    4882             :      * needed, and any additional phase-1 processing needed.  (But beware of
    4883             :      * adding any processing that looks at table details that another
    4884             :      * subcommand could change.  In some cases we reject multiple subcommands
    4885             :      * that could try to change the same state in contrary ways.)
    4886             :      */
    4887       31744 :     switch (cmd->subtype)
    4888             :     {
    4889        1990 :         case AT_AddColumn:      /* ADD COLUMN */
    4890        1990 :             ATSimplePermissions(cmd->subtype, rel,
    4891             :                                 ATT_TABLE | ATT_PARTITIONED_TABLE |
    4892             :                                 ATT_COMPOSITE_TYPE | ATT_FOREIGN_TABLE);
    4893        1990 :             ATPrepAddColumn(wqueue, rel, recurse, recursing, false, cmd,
    4894             :                             lockmode, context);
    4895             :             /* Recursion occurs during execution phase */
    4896        1978 :             pass = AT_PASS_ADD_COL;
    4897        1978 :             break;
    4898          24 :         case AT_AddColumnToView:    /* add column via CREATE OR REPLACE VIEW */
    4899          24 :             ATSimplePermissions(cmd->subtype, rel, ATT_VIEW);
    4900          24 :             ATPrepAddColumn(wqueue, rel, recurse, recursing, true, cmd,
    4901             :                             lockmode, context);
    4902             :             /* Recursion occurs during execution phase */
    4903          24 :             pass = AT_PASS_ADD_COL;
    4904          24 :             break;
    4905         598 :         case AT_ColumnDefault:  /* ALTER COLUMN DEFAULT */
    4906             : 
    4907             :             /*
    4908             :              * We allow defaults on views so that INSERT into a view can have
    4909             :              * default-ish behavior.  This works because the rewriter
    4910             :              * substitutes default values into INSERTs before it expands
    4911             :              * rules.
    4912             :              */
    4913         598 :             ATSimplePermissions(cmd->subtype, rel,
    4914             :                                 ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_VIEW |
    4915             :                                 ATT_FOREIGN_TABLE);
    4916         598 :             ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
    4917             :             /* No command-specific prep needed */
    4918         598 :             pass = cmd->def ? AT_PASS_ADD_OTHERCONSTR : AT_PASS_DROP;
    4919         598 :             break;
    4920          56 :         case AT_CookedColumnDefault:    /* add a pre-cooked default */
    4921             :             /* This is currently used only in CREATE TABLE */
    4922             :             /* (so the permission check really isn't necessary) */
    4923          56 :             ATSimplePermissions(cmd->subtype, rel,
    4924             :                                 ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
    4925             :             /* This command never recurses */
    4926          56 :             pass = AT_PASS_ADD_OTHERCONSTR;
    4927          56 :             break;
    4928         166 :         case AT_AddIdentity:
    4929         166 :             ATSimplePermissions(cmd->subtype, rel,
    4930             :                                 ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_VIEW |
    4931             :                                 ATT_FOREIGN_TABLE);
    4932             :             /* Set up recursion for phase 2; no other prep needed */
    4933         166 :             if (recurse)
    4934         160 :                 cmd->recurse = true;
    4935         166 :             pass = AT_PASS_ADD_OTHERCONSTR;
    4936         166 :             break;
    4937          62 :         case AT_SetIdentity:
    4938          62 :             ATSimplePermissions(cmd->subtype, rel,
    4939             :                                 ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_VIEW |
    4940             :                                 ATT_FOREIGN_TABLE);
    4941             :             /* Set up recursion for phase 2; no other prep needed */
    4942          62 :             if (recurse)
    4943          56 :                 cmd->recurse = true;
    4944             :             /* This should run after AddIdentity, so do it in MISC pass */
    4945          62 :             pass = AT_PASS_MISC;
    4946          62 :             break;
    4947          56 :         case AT_DropIdentity:
    4948          56 :             ATSimplePermissions(cmd->subtype, rel,
    4949             :                                 ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_VIEW |
    4950             :                                 ATT_FOREIGN_TABLE);
    4951             :             /* Set up recursion for phase 2; no other prep needed */
    4952          56 :             if (recurse)
    4953          50 :                 cmd->recurse = true;
    4954          56 :             pass = AT_PASS_DROP;
    4955          56 :             break;
    4956         262 :         case AT_DropNotNull:    /* ALTER COLUMN DROP NOT NULL */
    4957         262 :             ATSimplePermissions(cmd->subtype, rel,
    4958             :                                 ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
    4959             :             /* Set up recursion for phase 2; no other prep needed */
    4960         256 :             if (recurse)
    4961         238 :                 cmd->recurse = true;
    4962         256 :             pass = AT_PASS_DROP;
    4963         256 :             break;
    4964         384 :         case AT_SetNotNull:     /* ALTER COLUMN SET NOT NULL */
    4965         384 :             ATSimplePermissions(cmd->subtype, rel,
    4966             :                                 ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
    4967             :             /* Set up recursion for phase 2; no other prep needed */
    4968         378 :             if (recurse)
    4969         354 :                 cmd->recurse = true;
    4970         378 :             pass = AT_PASS_COL_ATTRS;
    4971         378 :             break;
    4972          84 :         case AT_SetExpression:  /* ALTER COLUMN SET EXPRESSION */
    4973          84 :             ATSimplePermissions(cmd->subtype, rel,
    4974             :                                 ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
    4975          84 :             ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
    4976          84 :             pass = AT_PASS_SET_EXPRESSION;
    4977          84 :             break;
    4978          44 :         case AT_DropExpression: /* ALTER COLUMN DROP EXPRESSION */
    4979          44 :             ATSimplePermissions(cmd->subtype, rel,
    4980             :                                 ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
    4981          44 :             ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
    4982          44 :             ATPrepDropExpression(rel, cmd, recurse, recursing, lockmode);
    4983          32 :             pass = AT_PASS_DROP;
    4984          32 :             break;
    4985         164 :         case AT_SetStatistics:  /* ALTER COLUMN SET STATISTICS */
    4986         164 :             ATSimplePermissions(cmd->subtype, rel,
    4987             :                                 ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_MATVIEW |
    4988             :                                 ATT_INDEX | ATT_PARTITIONED_INDEX | ATT_FOREIGN_TABLE);
    4989         164 :             ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
    4990             :             /* No command-specific prep needed */
    4991         164 :             pass = AT_PASS_MISC;
    4992         164 :             break;
    4993          44 :         case AT_SetOptions:     /* ALTER COLUMN SET ( options ) */
    4994             :         case AT_ResetOptions:   /* ALTER COLUMN RESET ( options ) */
    4995          44 :             ATSimplePermissions(cmd->subtype, rel,
    4996             :                                 ATT_TABLE | ATT_PARTITIONED_TABLE |
    4997             :                                 ATT_MATVIEW | ATT_FOREIGN_TABLE);
    4998             :             /* This command never recurses */
    4999          32 :             pass = AT_PASS_MISC;
    5000          32 :             break;
    5001         234 :         case AT_SetStorage:     /* ALTER COLUMN SET STORAGE */
    5002         234 :             ATSimplePermissions(cmd->subtype, rel,
    5003             :                                 ATT_TABLE | ATT_PARTITIONED_TABLE |
    5004             :                                 ATT_MATVIEW | ATT_FOREIGN_TABLE);
    5005         234 :             ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
    5006             :             /* No command-specific prep needed */
    5007         234 :             pass = AT_PASS_MISC;
    5008         234 :             break;
    5009          68 :         case AT_SetCompression: /* ALTER COLUMN SET COMPRESSION */
    5010          68 :             ATSimplePermissions(cmd->subtype, rel,
    5011             :                                 ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_MATVIEW);
    5012             :             /* This command never recurses */
    5013             :             /* No command-specific prep needed */
    5014          68 :             pass = AT_PASS_MISC;
    5015          68 :             break;
    5016        1610 :         case AT_DropColumn:     /* DROP COLUMN */
    5017        1610 :             ATSimplePermissions(cmd->subtype, rel,
    5018             :                                 ATT_TABLE | ATT_PARTITIONED_TABLE |
    5019             :                                 ATT_COMPOSITE_TYPE | ATT_FOREIGN_TABLE);
    5020        1604 :             ATPrepDropColumn(wqueue, rel, recurse, recursing, cmd,
    5021             :                              lockmode, context);
    5022             :             /* Recursion occurs during execution phase */
    5023        1592 :             pass = AT_PASS_DROP;
    5024        1592 :             break;
    5025           0 :         case AT_AddIndex:       /* ADD INDEX */
    5026           0 :             ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_PARTITIONED_TABLE);
    5027             :             /* This command never recurses */
    5028             :             /* No command-specific prep needed */
    5029           0 :             pass = AT_PASS_ADD_INDEX;
    5030           0 :             break;
    5031       14938 :         case AT_AddConstraint:  /* ADD CONSTRAINT */
    5032       14938 :             ATSimplePermissions(cmd->subtype, rel,
    5033             :                                 ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
    5034       14938 :             ATPrepAddPrimaryKey(wqueue, rel, cmd, recurse, lockmode, context);
    5035       14932 :             if (recurse)
    5036             :             {
    5037             :                 /* recurses at exec time; lock descendants and set flag */
    5038       14584 :                 (void) find_all_inheritors(RelationGetRelid(rel), lockmode, NULL);
    5039       14584 :                 cmd->recurse = true;
    5040             :             }
    5041       14932 :             pass = AT_PASS_ADD_CONSTR;
    5042       14932 :             break;
    5043           0 :         case AT_AddIndexConstraint: /* ADD CONSTRAINT USING INDEX */
    5044           0 :             ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_PARTITIONED_TABLE);
    5045             :             /* This command never recurses */
    5046             :             /* No command-specific prep needed */
    5047           0 :             pass = AT_PASS_ADD_INDEXCONSTR;
    5048           0 :             break;
    5049         822 :         case AT_DropConstraint: /* DROP CONSTRAINT */
    5050         822 :             ATSimplePermissions(cmd->subtype, rel,
    5051             :                                 ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
    5052         822 :             ATCheckPartitionsNotInUse(rel, lockmode);
    5053             :             /* Other recursion occurs during execution phase */
    5054             :             /* No command-specific prep needed except saving recurse flag */
    5055         816 :             if (recurse)
    5056         780 :                 cmd->recurse = true;
    5057         816 :             pass = AT_PASS_DROP;
    5058         816 :             break;
    5059        1174 :         case AT_AlterColumnType:    /* ALTER COLUMN TYPE */
    5060        1174 :             ATSimplePermissions(cmd->subtype, rel,
    5061             :                                 ATT_TABLE | ATT_PARTITIONED_TABLE |
    5062             :                                 ATT_COMPOSITE_TYPE | ATT_FOREIGN_TABLE);
    5063             :             /* See comments for ATPrepAlterColumnType */
    5064        1174 :             cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, recurse, lockmode,
    5065             :                                       AT_PASS_UNSET, context);
    5066             :             Assert(cmd != NULL);
    5067             :             /* Performs own recursion */
    5068        1168 :             ATPrepAlterColumnType(wqueue, tab, rel, recurse, recursing, cmd,
    5069             :                                   lockmode, context);
    5070        1000 :             pass = AT_PASS_ALTER_TYPE;
    5071        1000 :             break;
    5072         172 :         case AT_AlterColumnGenericOptions:
    5073         172 :             ATSimplePermissions(cmd->subtype, rel, ATT_FOREIGN_TABLE);
    5074             :             /* This command never recurses */
    5075             :             /* No command-specific prep needed */
    5076         172 :             pass = AT_PASS_MISC;
    5077         172 :             break;
    5078        1796 :         case AT_ChangeOwner:    /* ALTER OWNER */
    5079             :             /* This command never recurses */
    5080             :             /* No command-specific prep needed */
    5081        1796 :             pass = AT_PASS_MISC;
    5082        1796 :             break;
    5083          64 :         case AT_ClusterOn:      /* CLUSTER ON */
    5084             :         case AT_DropCluster:    /* SET WITHOUT CLUSTER */
    5085          64 :             ATSimplePermissions(cmd->subtype, rel,
    5086             :                                 ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_MATVIEW);
    5087             :             /* These commands never recurse */
    5088             :             /* No command-specific prep needed */
    5089          64 :             pass = AT_PASS_MISC;
    5090          64 :             break;
    5091         112 :         case AT_SetLogged:      /* SET LOGGED */
    5092             :         case AT_SetUnLogged:    /* SET UNLOGGED */
    5093         112 :             ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_SEQUENCE);
    5094         100 :             if (tab->chgPersistence)
    5095           0 :                 ereport(ERROR,
    5096             :                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    5097             :                          errmsg("cannot change persistence setting twice")));
    5098         100 :             ATPrepChangePersistence(tab, rel, cmd->subtype == AT_SetLogged);
    5099          88 :             pass = AT_PASS_MISC;
    5100          88 :             break;
    5101           6 :         case AT_DropOids:       /* SET WITHOUT OIDS */
    5102           6 :             ATSimplePermissions(cmd->subtype, rel,
    5103             :                                 ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
    5104           6 :             pass = AT_PASS_DROP;
    5105           6 :             break;
    5106         128 :         case AT_SetAccessMethod:    /* SET ACCESS METHOD */
    5107         128 :             ATSimplePermissions(cmd->subtype, rel,
    5108             :                                 ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_MATVIEW);
    5109             : 
    5110             :             /* check if another access method change was already requested */
    5111         128 :             if (tab->chgAccessMethod)
    5112          18 :                 ereport(ERROR,
    5113             :                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    5114             :                          errmsg("cannot have multiple SET ACCESS METHOD subcommands")));
    5115             : 
    5116         110 :             ATPrepSetAccessMethod(tab, rel, cmd->name);
    5117         110 :             pass = AT_PASS_MISC;    /* does not matter; no work in Phase 2 */
    5118         110 :             break;
    5119         158 :         case AT_SetTableSpace:  /* SET TABLESPACE */
    5120         158 :             ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_PARTITIONED_TABLE |
    5121             :                                 ATT_MATVIEW | ATT_INDEX | ATT_PARTITIONED_INDEX);
    5122             :             /* This command never recurses */
    5123         158 :             ATPrepSetTableSpace(tab, rel, cmd->name, lockmode);
    5124         158 :             pass = AT_PASS_MISC;    /* doesn't actually matter */
    5125         158 :             break;
    5126         946 :         case AT_SetRelOptions:  /* SET (...) */
    5127             :         case AT_ResetRelOptions:    /* RESET (...) */
    5128             :         case AT_ReplaceRelOptions:  /* reset them all, then set just these */
    5129         946 :             ATSimplePermissions(cmd->subtype, rel,
    5130             :                                 ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_VIEW |
    5131             :                                 ATT_MATVIEW | ATT_INDEX);
    5132             :             /* This command never recurses */
    5133             :             /* No command-specific prep needed */
    5134         946 :             pass = AT_PASS_MISC;
    5135         946 :             break;
    5136         346 :         case AT_AddInherit:     /* INHERIT */
    5137         346 :             ATSimplePermissions(cmd->subtype, rel,
    5138             :                                 ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
    5139             :             /* This command never recurses */
    5140         346 :             ATPrepAddInherit(rel);
    5141         328 :             pass = AT_PASS_MISC;
    5142         328 :             break;
    5143          86 :         case AT_DropInherit:    /* NO INHERIT */
    5144          86 :             ATSimplePermissions(cmd->subtype, rel,
    5145             :                                 ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
    5146             :             /* This command never recurses */
    5147             :             /* No command-specific prep needed */
    5148          86 :             pass = AT_PASS_MISC;
    5149          86 :             break;
    5150         132 :         case AT_AlterConstraint:    /* ALTER CONSTRAINT */
    5151         132 :             ATSimplePermissions(cmd->subtype, rel,
    5152             :                                 ATT_TABLE | ATT_PARTITIONED_TABLE);
    5153             :             /* Recursion occurs during execution phase */
    5154         126 :             pass = AT_PASS_MISC;
    5155         126 :             break;
    5156         394 :         case AT_ValidateConstraint: /* VALIDATE CONSTRAINT */
    5157         394 :             ATSimplePermissions(cmd->subtype, rel,
    5158             :                                 ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
    5159             :             /* Recursion occurs during execution phase */
    5160             :             /* No command-specific prep needed except saving recurse flag */
    5161         394 :             if (recurse)
    5162         394 :                 cmd->recurse = true;
    5163         394 :             pass = AT_PASS_MISC;
    5164         394 :             break;
    5165         484 :         case AT_ReplicaIdentity:    /* REPLICA IDENTITY ... */
    5166         484 :             ATSimplePermissions(cmd->subtype, rel,
    5167             :                                 ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_MATVIEW);
    5168         484 :             pass = AT_PASS_MISC;
    5169             :             /* This command never recurses */
    5170             :             /* No command-specific prep needed */
    5171         484 :             break;
    5172         340 :         case AT_EnableTrig:     /* ENABLE TRIGGER variants */
    5173             :         case AT_EnableAlwaysTrig:
    5174             :         case AT_EnableReplicaTrig:
    5175             :         case AT_EnableTrigAll:
    5176             :         case AT_EnableTrigUser:
    5177             :         case AT_DisableTrig:    /* DISABLE TRIGGER variants */
    5178             :         case AT_DisableTrigAll:
    5179             :         case AT_DisableTrigUser:
    5180         340 :             ATSimplePermissions(cmd->subtype, rel,
    5181             :                                 ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
    5182             :             /* Set up recursion for phase 2; no other prep needed */
    5183         340 :             if (recurse)
    5184         312 :                 cmd->recurse = true;
    5185         340 :             pass = AT_PASS_MISC;
    5186         340 :             break;
    5187         532 :         case AT_EnableRule:     /* ENABLE/DISABLE RULE variants */
    5188             :         case AT_EnableAlwaysRule:
    5189             :         case AT_EnableReplicaRule:
    5190             :         case AT_DisableRule:
    5191             :         case AT_AddOf:          /* OF */
    5192             :         case AT_DropOf:         /* NOT OF */
    5193             :         case AT_EnableRowSecurity:
    5194             :         case AT_DisableRowSecurity:
    5195             :         case AT_ForceRowSecurity:
    5196             :         case AT_NoForceRowSecurity:
    5197         532 :             ATSimplePermissions(cmd->subtype, rel,
    5198             :                                 ATT_TABLE | ATT_PARTITIONED_TABLE);
    5199             :             /* These commands never recurse */
    5200             :             /* No command-specific prep needed */
    5201         532 :             pass = AT_PASS_MISC;
    5202         532 :             break;
    5203          58 :         case AT_GenericOptions:
    5204          58 :             ATSimplePermissions(cmd->subtype, rel, ATT_FOREIGN_TABLE);
    5205             :             /* No command-specific prep needed */
    5206          58 :             pass = AT_PASS_MISC;
    5207          58 :             break;
    5208        2626 :         case AT_AttachPartition:
    5209        2626 :             ATSimplePermissions(cmd->subtype, rel,
    5210             :                                 ATT_PARTITIONED_TABLE | ATT_PARTITIONED_INDEX);
    5211             :             /* No command-specific prep needed */
    5212        2620 :             pass = AT_PASS_MISC;
    5213        2620 :             break;
    5214         564 :         case AT_DetachPartition:
    5215         564 :             ATSimplePermissions(cmd->subtype, rel, ATT_PARTITIONED_TABLE);
    5216             :             /* No command-specific prep needed */
    5217         546 :             pass = AT_PASS_MISC;
    5218         546 :             break;
    5219          20 :         case AT_DetachPartitionFinalize:
    5220          20 :             ATSimplePermissions(cmd->subtype, rel, ATT_PARTITIONED_TABLE);
    5221             :             /* No command-specific prep needed */
    5222          14 :             pass = AT_PASS_MISC;
    5223          14 :             break;
    5224           0 :         default:                /* oops */
    5225           0 :             elog(ERROR, "unrecognized alter table type: %d",
    5226             :                  (int) cmd->subtype);
    5227             :             pass = AT_PASS_UNSET;   /* keep compiler quiet */
    5228             :             break;
    5229             :     }
    5230             :     Assert(pass > AT_PASS_UNSET);
    5231             : 
    5232             :     /* Add the subcommand to the appropriate list for phase 2 */
    5233       31396 :     tab->subcmds[pass] = lappend(tab->subcmds[pass], cmd);
    5234       31396 : }
    5235             : 
    5236             : /*
    5237             :  * ATRewriteCatalogs
    5238             :  *
    5239             :  * Traffic cop for ALTER TABLE Phase 2 operations.  Subcommands are
    5240             :  * dispatched in a "safe" execution order (designed to avoid unnecessary
    5241             :  * conflicts).
    5242             :  */
    5243             : static void
    5244       28982 : ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode,
    5245             :                   AlterTableUtilityContext *context)
    5246             : {
    5247             :     ListCell   *ltab;
    5248             : 
    5249             :     /*
    5250             :      * We process all the tables "in parallel", one pass at a time.  This is
    5251             :      * needed because we may have to propagate work from one table to another
    5252             :      * (specifically, ALTER TYPE on a foreign key's PK has to dispatch the
    5253             :      * re-adding of the foreign key constraint to the other table).  Work can
    5254             :      * only be propagated into later passes, however.
    5255             :      */
    5256      365346 :     for (AlterTablePass pass = 0; pass < AT_NUM_PASSES; pass++)
    5257             :     {
    5258             :         /* Go through each table that needs to be processed */
    5259      689402 :         foreach(ltab, *wqueue)
    5260             :         {
    5261      353038 :             AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
    5262      353038 :             List       *subcmds = tab->subcmds[pass];
    5263             :             ListCell   *lcmd;
    5264             : 
    5265      353038 :             if (subcmds == NIL)
    5266      302416 :                 continue;
    5267             : 
    5268             :             /*
    5269             :              * Open the relation and store it in tab.  This allows subroutines
    5270             :              * close and reopen, if necessary.  Appropriate lock was obtained
    5271             :              * by phase 1, needn't get it again.
    5272             :              */
    5273       50622 :             tab->rel = relation_open(tab->relid, NoLock);
    5274             : 
    5275      102626 :             foreach(lcmd, subcmds)
    5276       54562 :                 ATExecCmd(wqueue, tab,
    5277       54562 :                           lfirst_node(AlterTableCmd, lcmd),
    5278             :                           lockmode, pass, context);
    5279             : 
    5280             :             /*
    5281             :              * After the ALTER TYPE or SET EXPRESSION pass, do cleanup work
    5282             :              * (this is not done in ATExecAlterColumnType since it should be
    5283             :              * done only once if multiple columns of a table are altered).
    5284             :              */
    5285       48064 :             if (pass == AT_PASS_ALTER_TYPE || pass == AT_PASS_SET_EXPRESSION)
    5286         988 :                 ATPostAlterTypeCleanup(wqueue, tab, lockmode);
    5287             : 
    5288       48064 :             if (tab->rel)
    5289             :             {
    5290       48064 :                 relation_close(tab->rel, NoLock);
    5291       48064 :                 tab->rel = NULL;
    5292             :             }
    5293             :         }
    5294             :     }
    5295             : 
    5296             :     /* Check to see if a toast table must be added. */
    5297       56630 :     foreach(ltab, *wqueue)
    5298             :     {
    5299       30206 :         AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
    5300             : 
    5301             :         /*
    5302             :          * If the table is source table of ATTACH PARTITION command, we did
    5303             :          * not modify anything about it that will change its toasting
    5304             :          * requirement, so no need to check.
    5305             :          */
    5306       30206 :         if (((tab->relkind == RELKIND_RELATION ||
    5307        5762 :               tab->relkind == RELKIND_PARTITIONED_TABLE) &&
    5308       28334 :              tab->partition_constraint == NULL) ||
    5309        3796 :             tab->relkind == RELKIND_MATVIEW)
    5310       26460 :             AlterTableCreateToastTable(tab->relid, (Datum) 0, lockmode);
    5311             :     }
    5312       26424 : }
    5313             : 
    5314             : /*
    5315             :  * ATExecCmd: dispatch a subcommand to appropriate execution routine
    5316             :  */
    5317             : static void
    5318       54562 : ATExecCmd(List **wqueue, AlteredTableInfo *tab,
    5319             :           AlterTableCmd *cmd, LOCKMODE lockmode, AlterTablePass cur_pass,
    5320             :           AlterTableUtilityContext *context)
    5321             : {
    5322       54562 :     ObjectAddress address = InvalidObjectAddress;
    5323       54562 :     Relation    rel = tab->rel;
    5324             : 
    5325       54562 :     switch (cmd->subtype)
    5326             :     {
    5327        1996 :         case AT_AddColumn:      /* ADD COLUMN */
    5328             :         case AT_AddColumnToView:    /* add column via CREATE OR REPLACE VIEW */
    5329        1996 :             address = ATExecAddColumn(wqueue, tab, rel, &cmd,
    5330        1996 :                                       cmd->recurse, false,
    5331             :                                       lockmode, cur_pass, context);
    5332        1876 :             break;
    5333         562 :         case AT_ColumnDefault:  /* ALTER COLUMN DEFAULT */
    5334         562 :             address = ATExecColumnDefault(rel, cmd->name, cmd->def, lockmode);
    5335         502 :             break;
    5336          56 :         case AT_CookedColumnDefault:    /* add a pre-cooked default */
    5337          56 :             address = ATExecCookedColumnDefault(rel, cmd->num, cmd->def);
    5338          56 :             break;
    5339         166 :         case AT_AddIdentity:
    5340         166 :             cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
    5341             :                                       cur_pass, context);
    5342             :             Assert(cmd != NULL);
    5343         154 :             address = ATExecAddIdentity(rel, cmd->name, cmd->def, lockmode, cmd->recurse, false);
    5344         106 :             break;
    5345          62 :         case AT_SetIdentity:
    5346          62 :             cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
    5347             :                                       cur_pass, context);
    5348             :             Assert(cmd != NULL);
    5349          62 :             address = ATExecSetIdentity(rel, cmd->name, cmd->def, lockmode, cmd->recurse, false);
    5350          38 :             break;
    5351          56 :         case AT_DropIdentity:
    5352          56 :             address = ATExecDropIdentity(rel, cmd->name, cmd->missing_ok, lockmode, cmd->recurse, false);
    5353          38 :             break;
    5354         256 :         case AT_DropNotNull:    /* ALTER COLUMN DROP NOT NULL */
    5355         256 :             address = ATExecDropNotNull(rel, cmd->name, cmd->recurse, lockmode);
    5356         154 :             break;
    5357         378 :         case AT_SetNotNull:     /* ALTER COLUMN SET NOT NULL */
    5358         378 :             address = ATExecSetNotNull(wqueue, rel, NULL, cmd->name,
    5359         378 :                                        cmd->recurse, false, lockmode);
    5360         348 :             break;
    5361          84 :         case AT_SetExpression:
    5362          84 :             address = ATExecSetExpression(tab, rel, cmd->name, cmd->def, lockmode);
    5363          78 :             break;
    5364          32 :         case AT_DropExpression:
    5365          32 :             address = ATExecDropExpression(rel, cmd->name, cmd->missing_ok, lockmode);
    5366          26 :             break;
    5367         164 :         case AT_SetStatistics:  /* ALTER COLUMN SET STATISTICS */
    5368         164 :             address = ATExecSetStatistics(rel, cmd->name, cmd->num, cmd->def, lockmode);
    5369         116 :             break;
    5370          26 :         case AT_SetOptions:     /* ALTER COLUMN SET ( options ) */
    5371          26 :             address = ATExecSetOptions(rel, cmd->name, cmd->def, false, lockmode);
    5372          26 :             break;
    5373           6 :         case AT_ResetOptions:   /* ALTER COLUMN RESET ( options ) */
    5374           6 :             address = ATExecSetOptions(rel, cmd->name, cmd->def, true, lockmode);
    5375           6 :             break;
    5376         234 :         case AT_SetStorage:     /* ALTER COLUMN SET STORAGE */
    5377         234 :             address = ATExecSetStorage(rel, cmd->name, cmd->def, lockmode);
    5378         222 :             break;
    5379          68 :         case AT_SetCompression: /* ALTER COLUMN SET COMPRESSION */
    5380          68 :             address = ATExecSetCompression(rel, cmd->name, cmd->def,
    5381             :                                            lockmode);
    5382          62 :             break;
    5383        1592 :         case AT_DropColumn:     /* DROP COLUMN */
    5384        1592 :             address = ATExecDropColumn(wqueue, rel, cmd->name,
    5385        1592 :                                        cmd->behavior, cmd->recurse, false,
    5386        1592 :                                        cmd->missing_ok, lockmode,
    5387             :                                        NULL);
    5388        1418 :             break;
    5389        1144 :         case AT_AddIndex:       /* ADD INDEX */
    5390        1144 :             address = ATExecAddIndex(tab, rel, (IndexStmt *) cmd->def, false,
    5391             :                                      lockmode);
    5392         974 :             break;
    5393         444 :         case AT_ReAddIndex:     /* ADD INDEX */
    5394         444 :             address = ATExecAddIndex(tab, rel, (IndexStmt *) cmd->def, true,
    5395             :                                      lockmode);
    5396         444 :             break;
    5397          14 :         case AT_ReAddStatistics:    /* ADD STATISTICS */
    5398          14 :             address = ATExecAddStatistics(tab, rel, (CreateStatsStmt *) cmd->def,
    5399             :                                           true, lockmode);
    5400          14 :             break;
    5401       26592 :         case AT_AddConstraint:  /* ADD CONSTRAINT */
    5402             :             /* Transform the command only during initial examination */
    5403       26592 :             if (cur_pass == AT_PASS_ADD_CONSTR)
    5404       14902 :                 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd,
    5405       14932 :                                           cmd->recurse, lockmode,
    5406             :                                           cur_pass, context);
    5407             :             /* Depending on constraint type, might be no more work to do now */
    5408       26562 :             if (cmd != NULL)
    5409             :                 address =
    5410       11660 :                     ATExecAddConstraint(wqueue, tab, rel,
    5411       11660 :                                         (Constraint *) cmd->def,
    5412       11660 :                                         cmd->recurse, false, lockmode);
    5413       25936 :             break;
    5414         326 :         case AT_ReAddConstraint:    /* Re-add pre-existing check constraint */
    5415             :             address =
    5416         326 :                 ATExecAddConstraint(wqueue, tab, rel, (Constraint *) cmd->def,
    5417             :                                     true, true, lockmode);
    5418         314 :             break;
    5419          14 :         case AT_ReAddDomainConstraint:  /* Re-add pre-existing domain check
    5420             :                                          * constraint */
    5421             :             address =
    5422          14 :                 AlterDomainAddConstraint(((AlterDomainStmt *) cmd->def)->typeName,
    5423          14 :                                          ((AlterDomainStmt *) cmd->def)->def,
    5424             :                                          NULL);
    5425           8 :             break;
    5426          78 :         case AT_ReAddComment:   /* Re-add existing comment */
    5427          78 :             address = CommentObject((CommentStmt *) cmd->def);
    5428          78 :             break;
    5429        9540 :         case AT_AddIndexConstraint: /* ADD CONSTRAINT USING INDEX */
    5430        9540 :             address = ATExecAddIndexConstraint(tab, rel, (IndexStmt *) cmd->def,
    5431             :                                                lockmode);
    5432        9528 :             break;
    5433         126 :         case AT_AlterConstraint:    /* ALTER CONSTRAINT */
    5434         126 :             address = ATExecAlterConstraint(rel, cmd, false, false, lockmode);
    5435         114 :             break;
    5436         394 :         case AT_ValidateConstraint: /* VALIDATE CONSTRAINT */
    5437         394 :             address = ATExecValidateConstraint(wqueue, rel, cmd->name, cmd->recurse,
    5438             :                                                false, lockmode);
    5439         388 :             break;
    5440         816 :         case AT_DropConstraint: /* DROP CONSTRAINT */
    5441         816 :             ATExecDropConstraint(rel, cmd->name, cmd->behavior,
    5442         816 :                                  cmd->recurse,
    5443         816 :                                  cmd->missing_ok, lockmode);
    5444         606 :             break;
    5445         970 :         case AT_AlterColumnType:    /* ALTER COLUMN TYPE */
    5446             :             /* parse transformation was done earlier */
    5447         970 :             address = ATExecAlterColumnType(tab, rel, cmd, lockmode);
    5448         940 :             break;
    5449         172 :         case AT_AlterColumnGenericOptions:  /* ALTER COLUMN OPTIONS */
    5450             :             address =
    5451         172 :                 ATExecAlterColumnGenericOptions(rel, cmd->name,
    5452         172 :                                                 (List *) cmd->def, lockmode);
    5453         166 :             break;
    5454        1796 :         case AT_ChangeOwner:    /* ALTER OWNER */
    5455        1790 :             ATExecChangeOwner(RelationGetRelid(rel),
    5456        1796 :                               get_rolespec_oid(cmd->newowner, false),
    5457             :                               false, lockmode);
    5458        1778 :             break;
    5459          64 :         case AT_ClusterOn:      /* CLUSTER ON */
    5460          64 :             address = ATExecClusterOn(rel, cmd->name, lockmode);
    5461          58 :             break;
    5462          18 :         case AT_DropCluster:    /* SET WITHOUT CLUSTER */
    5463          18 :             ATExecDropCluster(rel, lockmode);
    5464          12 :             break;
    5465          88 :         case AT_SetLogged:      /* SET LOGGED */
    5466             :         case AT_SetUnLogged:    /* SET UNLOGGED */
    5467          88 :             break;
    5468           6 :         case AT_DropOids:       /* SET WITHOUT OIDS */
    5469             :             /* nothing to do here, oid columns don't exist anymore */
    5470           6 :             break;
    5471          92 :         case AT_SetAccessMethod:    /* SET ACCESS METHOD */
    5472             : 
    5473             :             /*
    5474             :              * Only do this for partitioned tables, for which this is just a
    5475             :              * catalog change.  Tables with storage are handled by Phase 3.
    5476             :              */
    5477          92 :             if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&
    5478          50 :                 tab->chgAccessMethod)
    5479          44 :                 ATExecSetAccessMethodNoStorage(rel, tab->newAccessMethod);
    5480          92 :             break;
    5481         158 :         case AT_SetTableSpace:  /* SET TABLESPACE */
    5482             : 
    5483             :             /*
    5484             :              * Only do this for partitioned tables and indexes, for which this
    5485             :              * is just a catalog change.  Other relation types which have
    5486             :              * storage are handled by Phase 3.
    5487             :              */
    5488         158 :             if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
    5489         146 :                 rel->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
    5490          36 :                 ATExecSetTableSpaceNoStorage(rel, tab->newTableSpace);
    5491             : 
    5492         152 :             break;
    5493         946 :         case AT_SetRelOptions:  /* SET (...) */
    5494             :         case AT_ResetRelOptions:    /* RESET (...) */
    5495             :         case AT_ReplaceRelOptions:  /* replace entire option list */
    5496         946 :             ATExecSetRelOptions(rel, (List *) cmd->def, cmd->subtype, lockmode);
    5497         894 :             break;
    5498         122 :         case AT_EnableTrig:     /* ENABLE TRIGGER name */
    5499         122 :             ATExecEnableDisableTrigger(rel, cmd->name,
    5500             :                                        TRIGGER_FIRES_ON_ORIGIN, false,
    5501         122 :                                        cmd->recurse,
    5502             :                                        lockmode);
    5503         122 :             break;
    5504          40 :         case AT_EnableAlwaysTrig:   /* ENABLE ALWAYS TRIGGER name */
    5505          40 :             ATExecEnableDisableTrigger(rel, cmd->name,
    5506             :                                        TRIGGER_FIRES_ALWAYS, false,
    5507          40 :                                        cmd->recurse,
    5508             :                                        lockmode);
    5509          40 :             break;
    5510          16 :         case AT_EnableReplicaTrig:  /* ENABLE REPLICA TRIGGER name */
    5511          16 :             ATExecEnableDisableTrigger(rel, cmd->name,
    5512             :                                        TRIGGER_FIRES_ON_REPLICA, false,
    5513          16 :                                        cmd->recurse,
    5514             :                                        lockmode);
    5515          16 :             break;
    5516         138 :         case AT_DisableTrig:    /* DISABLE TRIGGER name */
    5517         138 :             ATExecEnableDisableTrigger(rel, cmd->name,
    5518             :                                        TRIGGER_DISABLED, false,
    5519         138 :                                        cmd->recurse,
    5520             :                                        lockmode);
    5521         138 :             break;
    5522           0 :         case AT_EnableTrigAll:  /* ENABLE TRIGGER ALL */
    5523           0 :             ATExecEnableDisableTrigger(rel, NULL,
    5524             :                                        TRIGGER_FIRES_ON_ORIGIN, false,
    5525           0 :                                        cmd->recurse,
    5526             :                                        lockmode);
    5527           0 :             break;
    5528          12 :         case AT_DisableTrigAll: /* DISABLE TRIGGER ALL */
    5529          12 :             ATExecEnableDisableTrigger(rel, NULL,
    5530             :                                        TRIGGER_DISABLED, false,
    5531          12 :                                        cmd->recurse,
    5532             :                                        lockmode);
    5533          12 :             break;
    5534           0 :         case AT_EnableTrigUser: /* ENABLE TRIGGER USER */
    5535           0 :             ATExecEnableDisableTrigger(rel, NULL,
    5536             :                                        TRIGGER_FIRES_ON_ORIGIN, true,
    5537           0 :                                        cmd->recurse,
    5538             :                                        lockmode);
    5539           0 :             break;
    5540          12 :         case AT_DisableTrigUser:    /* DISABLE TRIGGER USER */
    5541          12 :             ATExecEnableDisableTrigger(rel, NULL,
    5542             :                                        TRIGGER_DISABLED, true,
    5543          12 :                                        cmd->recurse,
    5544             :                                        lockmode);
    5545          12 :             break;
    5546             : 
    5547           8 :         case AT_EnableRule:     /* ENABLE RULE name */
    5548           8 :             ATExecEnableDisableRule(rel, cmd->name,
    5549             :                                     RULE_FIRES_ON_ORIGIN, lockmode);
    5550           8 :             break;
    5551           0 :         case AT_EnableAlwaysRule:   /* ENABLE ALWAYS RULE name */
    5552           0 :             ATExecEnableDisableRule(rel, cmd->name,
    5553             :                                     RULE_FIRES_ALWAYS, lockmode);
    5554           0 :             break;
    5555           6 :         case AT_EnableReplicaRule:  /* ENABLE REPLICA RULE name */
    5556           6 :             ATExecEnableDisableRule(rel, cmd->name,
    5557             :                                     RULE_FIRES_ON_REPLICA, lockmode);
    5558           6 :             break;
    5559          32 :         case AT_DisableRule:    /* DISABLE RULE name */
    5560          32 :             ATExecEnableDisableRule(rel, cmd->name,
    5561             :                                     RULE_DISABLED, lockmode);
    5562          32 :             break;
    5563             : 
    5564         328 :         case AT_AddInherit:
    5565         328 :             address = ATExecAddInherit(rel, (RangeVar *) cmd->def, lockmode);
    5566         232 :             break;
    5567          86 :         case AT_DropInherit:
    5568          86 :             address = ATExecDropInherit(rel, (RangeVar *) cmd->def, lockmode);
    5569          80 :             break;
    5570          66 :         case AT_AddOf:
    5571          66 :             address = ATExecAddOf(rel, (TypeName *) cmd->def, lockmode);
    5572          30 :             break;
    5573           6 :         case AT_DropOf:
    5574           6 :             ATExecDropOf(rel, lockmode);
    5575           6 :             break;
    5576         502 :         case AT_ReplicaIdentity:
    5577         502 :             ATExecReplicaIdentity(rel, (ReplicaIdentityStmt *) cmd->def, lockmode);
    5578         454 :             break;
    5579         284 :         case AT_EnableRowSecurity:
    5580         284 :             ATExecSetRowSecurity(rel, true);
    5581         284 :             break;
    5582          10 :         case AT_DisableRowSecurity:
    5583          10 :             ATExecSetRowSecurity(rel, false);
    5584          10 :             break;
    5585          88 :         case AT_ForceRowSecurity:
    5586          88 :             ATExecForceNoForceRowSecurity(rel, true);
    5587          88 :             break;
    5588          32 :         case AT_NoForceRowSecurity:
    5589          32 :             ATExecForceNoForceRowSecurity(rel, false);
    5590          32 :             break;
    5591          58 :         case AT_GenericOptions:
    5592          58 :             ATExecGenericOptions(rel, (List *) cmd->def);
    5593          56 :             break;
    5594        2620 :         case AT_AttachPartition:
    5595        2620 :             cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
    5596             :                                       cur_pass, context);
    5597             :             Assert(cmd != NULL);
    5598        2596 :             if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
    5599        2204 :                 address = ATExecAttachPartition(wqueue, rel, (PartitionCmd *) cmd->def,
    5600             :                                                 context);
    5601             :             else
    5602         392 :                 address = ATExecAttachPartitionIdx(wqueue, rel,
    5603         392 :                                                    ((PartitionCmd *) cmd->def)->name);
    5604        2254 :             break;
    5605         546 :         case AT_DetachPartition:
    5606         546 :             cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
    5607             :                                       cur_pass, context);
    5608             :             Assert(cmd != NULL);
    5609             :             /* ATPrepCmd ensures it must be a table */
    5610             :             Assert(rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
    5611         546 :             address = ATExecDetachPartition(wqueue, tab, rel,
    5612         546 :                                             ((PartitionCmd *) cmd->def)->name,
    5613         546 :                                             ((PartitionCmd *) cmd->def)->concurrent);
    5614         416 :             break;
    5615          14 :         case AT_DetachPartitionFinalize:
    5616          14 :             address = ATExecDetachPartitionFinalize(rel, ((PartitionCmd *) cmd->def)->name);
    5617          14 :             break;
    5618           0 :         default:                /* oops */
    5619           0 :             elog(ERROR, "unrecognized alter table type: %d",
    5620             :                  (int) cmd->subtype);
    5621             :             break;
    5622             :     }
    5623             : 
    5624             :     /*
    5625             :      * Report the subcommand to interested event triggers.
    5626             :      */
    5627       52004 :     if (cmd)
    5628       37102 :         EventTriggerCollectAlterTableSubcmd((Node *) cmd, address);
    5629             : 
    5630             :     /*
    5631             :      * Bump the command counter to ensure the next subcommand in the sequence
    5632             :      * can see the changes so far
    5633             :      */
    5634       52004 :     CommandCounterIncrement();
    5635       52004 : }
    5636             : 
    5637             : /*
    5638             :  * ATParseTransformCmd: perform parse transformation for one subcommand
    5639             :  *
    5640             :  * Returns the transformed subcommand tree, if there is one, else NULL.
    5641             :  *
    5642             :  * The parser may hand back additional AlterTableCmd(s) and/or other
    5643             :  * utility statements, either before or after the original subcommand.
    5644             :  * Other AlterTableCmds are scheduled into the appropriate slot of the
    5645             :  * AlteredTableInfo (they had better be for later passes than the current one).
    5646             :  * Utility statements that are supposed to happen before the AlterTableCmd
    5647             :  * are executed immediately.  Those that are supposed to happen afterwards
    5648             :  * are added to the tab->afterStmts list to be done at the very end.
    5649             :  */
    5650             : static AlterTableCmd *
    5651       21376 : ATParseTransformCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
    5652             :                     AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode,
    5653             :                     AlterTablePass cur_pass, AlterTableUtilityContext *context)
    5654             : {
    5655       21376 :     AlterTableCmd *newcmd = NULL;
    5656       21376 :     AlterTableStmt *atstmt = makeNode(AlterTableStmt);
    5657             :     List       *beforeStmts;
    5658             :     List       *afterStmts;
    5659             :     ListCell   *lc;
    5660             : 
    5661             :     /* Gin up an AlterTableStmt with just this subcommand and this table */
    5662       21376 :     atstmt->relation =
    5663       21376 :         makeRangeVar(get_namespace_name(RelationGetNamespace(rel)),
    5664       21376 :                      pstrdup(RelationGetRelationName(rel)),
    5665             :                      -1);
    5666       21376 :     atstmt->relation->inh = recurse;
    5667       21376 :     atstmt->cmds = list_make1(cmd);
    5668       21376 :     atstmt->objtype = OBJECT_TABLE; /* needn't be picky here */
    5669       21376 :     atstmt->missing_ok = false;
    5670             : 
    5671             :     /* Transform the AlterTableStmt */
    5672       21376 :     atstmt = transformAlterTableStmt(RelationGetRelid(rel),
    5673             :                                      atstmt,
    5674             :                                      context->queryString,
    5675             :                                      &beforeStmts,
    5676             :                                      &afterStmts);
    5677             : 
    5678             :     /* Execute any statements that should happen before these subcommand(s) */
    5679       21784 :     foreach(lc, beforeStmts)
    5680             :     {
    5681         486 :         Node       *stmt = (Node *) lfirst(lc);
    5682             : 
    5683         486 :         ProcessUtilityForAlterTable(stmt, context);
    5684         474 :         CommandCounterIncrement();
    5685             :     }
    5686             : 
    5687             :     /* Examine the transformed subcommands and schedule them appropriately */
    5688       50074 :     foreach(lc, atstmt->cmds)
    5689             :     {
    5690       28776 :         AlterTableCmd *cmd2 = lfirst_node(AlterTableCmd, lc);
    5691             :         AlterTablePass pass;
    5692             : 
    5693             :         /*
    5694             :          * This switch need only cover the subcommand types that can be added
    5695             :          * by parse_utilcmd.c; otherwise, we'll use the default strategy of
    5696             :          * executing the subcommand immediately, as a substitute for the
    5697             :          * original subcommand.  (Note, however, that this does cause
    5698             :          * AT_AddConstraint subcommands to be rescheduled into later passes,
    5699             :          * which is important for index and foreign key constraints.)
    5700             :          *
    5701             :          * We assume we needn't do any phase-1 checks for added subcommands.
    5702             :          */
    5703       28776 :         switch (cmd2->subtype)
    5704             :         {
    5705        1168 :             case AT_AddIndex:
    5706        1168 :                 pass = AT_PASS_ADD_INDEX;
    5707        1168 :                 break;
    5708        9540 :             case AT_AddIndexConstraint:
    5709        9540 :                 pass = AT_PASS_ADD_INDEXCONSTR;
    5710        9540 :                 break;
    5711       11672 :             case AT_AddConstraint:
    5712             :                 /* Recursion occurs during execution phase */
    5713       11672 :                 if (recurse)
    5714       11622 :                     cmd2->recurse = true;
    5715       11672 :                 switch (castNode(Constraint, cmd2->def)->contype)
    5716             :                 {
    5717        8422 :                     case CONSTR_NOTNULL:
    5718        8422 :                         pass = AT_PASS_COL_ATTRS;
    5719        8422 :                         break;
    5720           0 :                     case CONSTR_PRIMARY:
    5721             :                     case CONSTR_UNIQUE:
    5722             :                     case CONSTR_EXCLUSION:
    5723           0 :                         pass = AT_PASS_ADD_INDEXCONSTR;
    5724           0 :                         break;
    5725        3250 :                     default:
    5726        3250 :                         pass = AT_PASS_ADD_OTHERCONSTR;
    5727        3250 :                         break;
    5728             :                 }
    5729       11672 :                 break;
    5730           0 :             case AT_AlterColumnGenericOptions:
    5731             :                 /* This command never recurses */
    5732             :                 /* No command-specific prep needed */
    5733           0 :                 pass = AT_PASS_MISC;
    5734           0 :                 break;
    5735        6396 :             default:
    5736        6396 :                 pass = cur_pass;
    5737        6396 :                 break;
    5738             :         }
    5739             : 
    5740       28776 :         if (pass < cur_pass)
    5741             :         {
    5742             :             /* Cannot schedule into a pass we already finished */
    5743           0 :             elog(ERROR, "ALTER TABLE scheduling failure: too late for pass %d",
    5744             :                  pass);
    5745             :         }
    5746       28776 :         else if (pass > cur_pass)
    5747             :         {
    5748             :             /* OK, queue it up for later */
    5749       22380 :             tab->subcmds[pass] = lappend(tab->subcmds[pass], cmd2);
    5750             :         }
    5751             :         else
    5752             :         {
    5753             :             /*
    5754             :              * We should see at most one subcommand for the current pass,
    5755             :              * which is the transformed version of the original subcommand.
    5756             :              */
    5757        6396 :             if (newcmd == NULL && cmd->subtype == cmd2->subtype)
    5758             :             {
    5759             :                 /* Found the transformed version of our subcommand */
    5760        6396 :                 newcmd = cmd2;
    5761             :             }
    5762             :             else
    5763           0 :                 elog(ERROR, "ALTER TABLE scheduling failure: bogus item for pass %d",
    5764             :                      pass);
    5765             :         }
    5766             :     }
    5767             : 
    5768             :     /* Queue up any after-statements to happen at the end */
    5769       21298 :     tab->afterStmts = list_concat(tab->afterStmts, afterStmts);
    5770             : 
    5771       21298 :     return newcmd;
    5772             : }
    5773             : 
    5774             : /*
    5775             :  * ATRewriteTables: ALTER TABLE phase 3
    5776             :  */
    5777             : static void
    5778       26424 : ATRewriteTables(AlterTableStmt *parsetree, List **wqueue, LOCKMODE lockmode,
    5779             :                 AlterTableUtilityContext *context)
    5780             : {
    5781             :     ListCell   *ltab;
    5782             : 
    5783             :     /* Go through each table that needs to be checked or rewritten */
    5784       56368 :     foreach(ltab, *wqueue)
    5785             :     {
    5786       30194 :         AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
    5787             : 
    5788             :         /* Relations without storage may be ignored here */
    5789       30194 :         if (!RELKIND_HAS_STORAGE(tab->relkind))
    5790        5474 :             continue;
    5791             : 
    5792             :         /*
    5793             :          * If we change column data types, the operation has to be propagated
    5794             :          * to tables that use this table's rowtype as a column type.
    5795             :          * tab->newvals will also be non-NULL in the case where we're adding a
    5796             :          * column with a default.  We choose to forbid that case as well,
    5797             :          * since composite types might eventually support defaults.
    5798             :          *
    5799             :          * (Eventually we'll probably need to check for composite type
    5800             :          * dependencies even when we're just scanning the table without a
    5801             :          * rewrite, but at the moment a composite type does not enforce any
    5802             :          * constraints, so it's not necessary/appropriate to enforce them just
    5803             :          * during ALTER.)
    5804             :          */
    5805       24720 :         if (tab->newvals != NIL || tab->rewrite > 0)
    5806             :         {
    5807             :             Relation    rel;
    5808             : 
    5809        1494 :             rel = table_open(tab->relid, NoLock);
    5810        1494 :             find_composite_type_dependencies(rel->rd_rel->reltype, rel, NULL);
    5811        1482 :             table_close(rel, NoLock);
    5812             :         }
    5813             : 
    5814             :         /*
    5815             :          * We only need to rewrite the table if at least one column needs to
    5816             :          * be recomputed, or we are changing its persistence or access method.
    5817             :          *
    5818             :          * There are two reasons for requiring a rewrite when changing
    5819             :          * persistence: on one hand, we need to ensure that the buffers
    5820             :          * belonging to each of the two relations are marked with or without
    5821             :          * BM_PERMANENT properly.  On the other hand, since rewriting creates
    5822             :          * and assigns a new relfilenumber, we automatically create or drop an
    5823             :          * init fork for the relation as appropriate.
    5824             :          */
    5825       24708 :         if (tab->rewrite > 0 && tab->relkind != RELKIND_SEQUENCE)
    5826         856 :         {
    5827             :             /* Build a temporary relation and copy data */
    5828             :             Relation    OldHeap;
    5829             :             Oid         OIDNewHeap;
    5830             :             Oid         NewAccessMethod;
    5831             :             Oid         NewTableSpace;
    5832             :             char        persistence;
    5833             : 
    5834         894 :             OldHeap = table_open(tab->relid, NoLock);
    5835             : 
    5836             :             /*
    5837             :              * We don't support rewriting of system catalogs; there are too
    5838             :              * many corner cases and too little benefit.  In particular this
    5839             :              * is certainly not going to work for mapped catalogs.
    5840             :              */
    5841         894 :             if (IsSystemRelation(OldHeap))
    5842           0 :                 ereport(ERROR,
    5843             :                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    5844             :                          errmsg("cannot rewrite system relation \"%s\"",
    5845             :                                 RelationGetRelationName(OldHeap))));
    5846             : 
    5847         894 :             if (RelationIsUsedAsCatalogTable(OldHeap))
    5848           2 :                 ereport(ERROR,
    5849             :                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    5850             :                          errmsg("cannot rewrite table \"%s\" used as a catalog table",
    5851             :                                 RelationGetRelationName(OldHeap))));
    5852             : 
    5853             :             /*
    5854             :              * Don't allow rewrite on temp tables of other backends ... their
    5855             :              * local buffer manager is not going to cope.  (This is redundant
    5856             :              * with the check in CheckAlterTableIsSafe, but for safety we'll
    5857             :              * check here too.)
    5858             :              */
    5859         892 :             if (RELATION_IS_OTHER_TEMP(OldHeap))
    5860           0 :                 ereport(ERROR,
    5861             :                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    5862             :                          errmsg("cannot rewrite temporary tables of other sessions")));
    5863             : 
    5864             :             /*
    5865             :              * Select destination tablespace (same as original unless user
    5866             :              * requested a change)
    5867             :              */
    5868         892 :             if (tab->newTableSpace)
    5869           0 :                 NewTableSpace = tab->newTableSpace;
    5870             :             else
    5871         892 :                 NewTableSpace = OldHeap->rd_rel->reltablespace;
    5872             : 
    5873             :             /*
    5874             :              * Select destination access method (same as original unless user
    5875             :              * requested a change)
    5876             :              */
    5877         892 :             if (tab->chgAccessMethod)
    5878          36 :                 NewAccessMethod = tab->newAccessMethod;
    5879             :             else
    5880         856 :                 NewAccessMethod = OldHeap->rd_rel->relam;
    5881             : 
    5882             :             /*
    5883             :              * Select persistence of transient table (same as original unless
    5884             :              * user requested a change)
    5885             :              */
    5886         892 :             persistence = tab->chgPersistence ?
    5887         840 :                 tab->newrelpersistence : OldHeap->rd_rel->relpersistence;
    5888             : 
    5889         892 :             table_close(OldHeap, NoLock);
    5890             : 
    5891             :             /*
    5892             :              * Fire off an Event Trigger now, before actually rewriting the
    5893             :              * table.
    5894             :              *
    5895             :              * We don't support Event Trigger for nested commands anywhere,
    5896             :              * here included, and parsetree is given NULL when coming from
    5897             :              * AlterTableInternal.
    5898             :              *
    5899             :              * And fire it only once.
    5900             :              */
    5901         892 :             if (parsetree)
    5902         892 :                 EventTriggerTableRewrite((Node *) parsetree,
    5903             :                                          tab->relid,
    5904             :                                          tab->rewrite);
    5905             : 
    5906             :             /*
    5907             :              * Create transient table that will receive the modified data.
    5908             :              *
    5909             :              * Ensure it is marked correctly as logged or unlogged.  We have
    5910             :              * to do this here so that buffers for the new relfilenumber will
    5911             :              * have the right persistence set, and at the same time ensure
    5912             :              * that the original filenumbers's buffers will get read in with
    5913             :              * the correct setting (i.e. the original one).  Otherwise a
    5914             :              * rollback after the rewrite would possibly result with buffers
    5915             :              * for the original filenumbers having the wrong persistence
    5916             :              * setting.
    5917             :              *
    5918             :              * NB: This relies on swap_relation_files() also swapping the
    5919             :              * persistence. That wouldn't work for pg_class, but that can't be
    5920             :              * unlogged anyway.
    5921             :              */
    5922         886 :             OIDNewHeap = make_new_heap(tab->relid, NewTableSpace, NewAccessMethod,
    5923             :                                        persistence, lockmode);
    5924             : 
    5925             :             /*
    5926             :              * Copy the heap data into the new table with the desired
    5927             :              * modifications, and test the current data within the table
    5928             :              * against new constraints generated by ALTER TABLE commands.
    5929             :              */
    5930         886 :             ATRewriteTable(tab, OIDNewHeap);
    5931             : 
    5932             :             /*
    5933             :              * Swap the physical files of the old and new heaps, then rebuild
    5934             :              * indexes and discard the old heap.  We can use RecentXmin for
    5935             :              * the table's new relfrozenxid because we rewrote all the tuples
    5936             :              * in ATRewriteTable, so no older Xid remains in the table.  Also,
    5937             :              * we never try to swap toast tables by content, since we have no
    5938             :              * interest in letting this code work on system catalogs.
    5939             :              */
    5940         862 :             finish_heap_swap(tab->relid, OIDNewHeap,
    5941             :                              false, false, true,
    5942         862 :                              !OidIsValid(tab->newTableSpace),
    5943             :                              RecentXmin,
    5944             :                              ReadNextMultiXactId(),
    5945             :                              persistence);
    5946             : 
    5947         856 :             InvokeObjectPostAlterHook(RelationRelationId, tab->relid, 0);
    5948             :         }
    5949       23814 :         else if (tab->rewrite > 0 && tab->relkind == RELKIND_SEQUENCE)
    5950             :         {
    5951          24 :             if (tab->chgPersistence)
    5952          24 :                 SequenceChangePersistence(tab->relid, tab->newrelpersistence);
    5953             :         }
    5954             :         else
    5955             :         {
    5956             :             /*
    5957             :              * If required, test the current data within the table against new
    5958             :              * constraints generated by ALTER TABLE commands, but don't
    5959             :              * rebuild data.
    5960             :              */
    5961       23790 :             if (tab->constraints != NIL || tab->verify_new_notnull ||
    5962       21232 :                 tab->partition_constraint != NULL)
    5963        4334 :                 ATRewriteTable(tab, InvalidOid);
    5964             : 
    5965             :             /*
    5966             :              * If we had SET TABLESPACE but no reason to reconstruct tuples,
    5967             :              * just do a block-by-block copy.
    5968             :              */
    5969       23590 :             if (tab->newTableSpace)
    5970         122 :                 ATExecSetTableSpace(tab->relid, tab->newTableSpace, lockmode);
    5971             :         }
    5972             : 
    5973             :         /*
    5974             :          * Also change persistence of owned sequences, so that it matches the
    5975             :          * table persistence.
    5976             :          */
    5977       24470 :         if (tab->chgPersistence)
    5978             :         {
    5979          76 :             List       *seqlist = getOwnedSequences(tab->relid);
    5980             :             ListCell   *lc;
    5981             : 
    5982         124 :             foreach(lc, seqlist)
    5983             :             {
    5984          48 :                 Oid         seq_relid = lfirst_oid(lc);
    5985             : 
    5986          48 :                 SequenceChangePersistence(seq_relid, tab->newrelpersistence);
    5987             :             }
    5988             :         }
    5989             :     }
    5990             : 
    5991             :     /*
    5992             :      * Foreign key constraints are checked in a final pass, since (a) it's
    5993             :      * generally best to examine each one separately, and (b) it's at least
    5994             :      * theoretically possible that we have changed both relations of the
    5995             :      * foreign key, and we'd better have finished both rewrites before we try
    5996             :      * to read the tables.
    5997             :      */
    5998       55908 :     foreach(ltab, *wqueue)
    5999             :     {
    6000       29814 :         AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
    6001       29814 :         Relation    rel = NULL;
    6002             :         ListCell   *lcon;
    6003             : 
    6004             :         /* Relations without storage may be ignored here too */
    6005       29814 :         if (!RELKIND_HAS_STORAGE(tab->relkind))
    6006        5394 :             continue;
    6007             : 
    6008       26130 :         foreach(lcon, tab->constraints)
    6009             :         {
    6010        1790 :             NewConstraint *con = lfirst(lcon);
    6011             : 
    6012        1790 :             if (con->contype == CONSTR_FOREIGN)
    6013             :             {
    6014        1108 :                 Constraint *fkconstraint = (Constraint *) con->qual;
    6015             :                 Relation    refrel;
    6016             : 
    6017        1108 :                 if (rel == NULL)
    6018             :                 {
    6019             :                     /* Long since locked, no need for another */
    6020        1096 :                     rel = table_open(tab->relid, NoLock);
    6021             :                 }
    6022             : 
    6023        1108 :                 refrel = table_open(con->refrelid, RowShareLock);
    6024             : 
    6025        1108 :                 validateForeignKeyConstraint(fkconstraint->conname, rel, refrel,
    6026             :                                              con->refindid,
    6027             :                                              con->conid,
    6028        1108 :                                              con->conwithperiod);
    6029             : 
    6030             :                 /*
    6031             :                  * No need to mark the constraint row as validated, we did
    6032             :                  * that when we inserted the row earlier.
    6033             :                  */
    6034             : 
    6035        1028 :                 table_close(refrel, NoLock);
    6036             :             }
    6037             :         }
    6038             : 
    6039       24340 :         if (rel)
    6040        1016 :             table_close(rel, NoLock);
    6041             :     }
    6042             : 
    6043             :     /* Finally, run any afterStmts that were queued up */
    6044       55790 :     foreach(ltab, *wqueue)
    6045             :     {
    6046       29696 :         AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
    6047             :         ListCell   *lc;
    6048             : 
    6049       29782 :         foreach(lc, tab->afterStmts)
    6050             :         {
    6051          86 :             Node       *stmt = (Node *) lfirst(lc);
    6052             : 
    6053          86 :             ProcessUtilityForAlterTable(stmt, context);
    6054          86 :             CommandCounterIncrement();
    6055             :         }
    6056             :     }
    6057       26094 : }
    6058             : 
    6059             : /*
    6060             :  * ATRewriteTable: scan or rewrite one table
    6061             :  *
    6062             :  * A rewrite is requested by passing a valid OIDNewHeap; in that case, caller
    6063             :  * must already hold AccessExclusiveLock on it.
    6064             :  */
    6065             : static void
    6066        5220 : ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap)
    6067             : {
    6068             :     Relation    oldrel;
    6069             :     Relation    newrel;
    6070             :     TupleDesc   oldTupDesc;
    6071             :     TupleDesc   newTupDesc;
    6072        5220 :     bool        needscan = false;
    6073             :     List       *notnull_attrs;
    6074             :     int         i;
    6075             :     ListCell   *l;
    6076             :     EState     *estate;
    6077             :     CommandId   mycid;
    6078             :     BulkInsertState bistate;
    6079             :     int         ti_options;
    6080        5220 :     ExprState  *partqualstate = NULL;
    6081             : 
    6082             :     /*
    6083             :      * Open the relation(s).  We have surely already locked the existing
    6084             :      * table.
    6085             :      */
    6086        5220 :     oldrel = table_open(tab->relid, NoLock);
    6087        5220 :     oldTupDesc = tab->oldDesc;
    6088        5220 :     newTupDesc = RelationGetDescr(oldrel);  /* includes all mods */
    6089             : 
    6090        5220 :     if (OidIsValid(OIDNewHeap))
    6091             :     {
    6092             :         Assert(CheckRelationOidLockedByMe(OIDNewHeap, AccessExclusiveLock,
    6093             :                                           false));
    6094         886 :         newrel = table_open(OIDNewHeap, NoLock);
    6095             :     }
    6096             :     else
    6097        4334 :         newrel = NULL;
    6098             : 
    6099             :     /*
    6100             :      * Prepare a BulkInsertState and options for table_tuple_insert.  The FSM
    6101             :      * is empty, so don't bother using it.
    6102             :      */
    6103        5220 :     if (newrel)
    6104             :     {
    6105         886 :         mycid = GetCurrentCommandId(true);
    6106         886 :         bistate = GetBulkInsertState();
    6107         886 :         ti_options = TABLE_INSERT_SKIP_FSM;
    6108             :     }
    6109             :     else
    6110             :     {
    6111             :         /* keep compiler quiet about using these uninitialized */
    6112        4334 :         mycid = 0;
    6113        4334 :         bistate = NULL;
    6114        4334 :         ti_options = 0;
    6115             :     }
    6116             : 
    6117             :     /*
    6118             :      * Generate the constraint and default execution states
    6119             :      */
    6120             : 
    6121        5220 :     estate = CreateExecutorState();
    6122             : 
    6123             :     /* Build the needed expression execution states */
    6124        7106 :     foreach(l, tab->constraints)
    6125             :     {
    6126        1886 :         NewConstraint *con = lfirst(l);
    6127             : 
    6128        1886 :         switch (con->contype)
    6129             :         {
    6130         772 :             case CONSTR_CHECK:
    6131         772 :                 needscan = true;
    6132         772 :                 con->qualstate = ExecPrepareExpr((Expr *) con->qual, estate);
    6133         772 :                 break;
    6134        1114 :             case CONSTR_FOREIGN:
    6135             :                 /* Nothing to do here */
    6136        1114 :                 break;
    6137           0 :             default:
    6138           0 :                 elog(ERROR, "unrecognized constraint type: %d",
    6139             :                      (int) con->contype);
    6140             :         }
    6141             :     }
    6142             : 
    6143             :     /* Build expression execution states for partition check quals */
    6144        5220 :     if (tab->partition_constraint)
    6145             :     {
    6146        1918 :         needscan = true;
    6147        1918 :         partqualstate = ExecPrepareExpr(tab->partition_constraint, estate);
    6148             :     }
    6149             : 
    6150        6102 :     foreach(l, tab->newvals)
    6151             :     {
    6152         882 :         NewColumnValue *ex = lfirst(l);
    6153             : 
    6154             :         /* expr already planned */
    6155         882 :         ex->exprstate = ExecInitExpr((Expr *) ex->expr, NULL);
    6156             :     }
    6157             : 
    6158        5220 :     notnull_attrs = NIL;
    6159        5220 :     if (newrel || tab->verify_new_notnull)
    6160             :     {
    6161             :         /*
    6162             :          * If we are rebuilding the tuples OR if we added any new but not
    6163             :          * verified not-null constraints, check all not-null constraints. This
    6164             :          * is a bit of overkill but it minimizes risk of bugs.
    6165             :          */
    6166        6264 :         for (i = 0; i < newTupDesc->natts; i++)
    6167             :         {
    6168        4560 :             Form_pg_attribute attr = TupleDescAttr(newTupDesc, i);
    6169             : 
    6170        4560 :             if (attr->attnotnull && !attr->attisdropped)
    6171        1808 :                 notnull_attrs = lappend_int(notnull_attrs, i);
    6172             :         }
    6173        1704 :         if (notnull_attrs)
    6174        1288 :             needscan = true;
    6175             :     }
    6176             : 
    6177        5220 :     if (newrel || needscan)
    6178             :     {
    6179             :         ExprContext *econtext;
    6180             :         TupleTableSlot *oldslot;
    6181             :         TupleTableSlot *newslot;
    6182             :         TableScanDesc scan;
    6183             :         MemoryContext oldCxt;
    6184        4302 :         List       *dropped_attrs = NIL;
    6185             :         ListCell   *lc;
    6186             :         Snapshot    snapshot;
    6187             : 
    6188        4302 :         if (newrel)
    6189         886 :             ereport(DEBUG1,
    6190             :                     (errmsg_internal("rewriting table \"%s\"",
    6191             :                                      RelationGetRelationName(oldrel))));
    6192             :         else
    6193        3416 :             ereport(DEBUG1,
    6194             :                     (errmsg_internal("verifying table \"%s\"",
    6195             :                                      RelationGetRelationName(oldrel))));
    6196             : 
    6197        4302 :         if (newrel)
    6198             :         {
    6199             :             /*
    6200             :              * All predicate locks on the tuples or pages are about to be made
    6201             :              * invalid, because we move tuples around.  Promote them to
    6202             :              * relation locks.
    6203             :              */
    6204         886 :             TransferPredicateLocksToHeapRelation(oldrel);
    6205             :         }
    6206             : 
    6207        4302 :         econtext = GetPerTupleExprContext(estate);
    6208             : 
    6209             :         /*
    6210             :          * Create necessary tuple slots. When rewriting, two slots are needed,
    6211             :          * otherwise one suffices. In the case where one slot suffices, we
    6212             :          * need to use the new tuple descriptor, otherwise some constraints
    6213             :          * can't be evaluated.  Note that even when the tuple layout is the
    6214             :          * same and no rewrite is required, the tupDescs might not be
    6215             :          * (consider ADD COLUMN without a default).
    6216             :          */
    6217        4302 :         if (tab->rewrite)
    6218             :         {
    6219             :             Assert(newrel != NULL);
    6220         886 :             oldslot = MakeSingleTupleTableSlot(oldTupDesc,
    6221             :                                                table_slot_callbacks(oldrel));
    6222         886 :             newslot = MakeSingleTupleTableSlot(newTupDesc,
    6223             :                                                table_slot_callbacks(newrel));
    6224             : 
    6225             :             /*
    6226             :              * Set all columns in the new slot to NULL initially, to ensure
    6227             :              * columns added as part of the rewrite are initialized to NULL.
    6228             :              * That is necessary as tab->newvals will not contain an
    6229             :              * expression for columns with a NULL default, e.g. when adding a
    6230             :              * column without a default together with a column with a default
    6231             :              * requiring an actual rewrite.
    6232             :              */
    6233         886 :             ExecStoreAllNullTuple(newslot);
    6234             :         }
    6235             :         else
    6236             :         {
    6237        3416 :             oldslot = MakeSingleTupleTableSlot(newTupDesc,
    6238             :                                                table_slot_callbacks(oldrel));
    6239        3416 :             newslot = NULL;
    6240             :         }
    6241             : 
    6242             :         /*
    6243             :          * Any attributes that are dropped according to the new tuple
    6244             :          * descriptor can be set to NULL. We precompute the list of dropped
    6245             :          * attributes to avoid needing to do so in the per-tuple loop.
    6246             :          */
    6247       15114 :         for (i = 0; i < newTupDesc->natts; i++)
    6248             :         {
    6249       10812 :             if (TupleDescAttr(newTupDesc, i)->attisdropped)
    6250         778 :                 dropped_attrs = lappend_int(dropped_attrs, i);
    6251             :         }
    6252             : 
    6253             :         /*
    6254             :          * Scan through the rows, generating a new row if needed and then
    6255             :          * checking all the constraints.
    6256             :          */
    6257        4302 :         snapshot = RegisterSnapshot(GetLatestSnapshot());
    6258        4302 :         scan = table_beginscan(oldrel, snapshot, 0, NULL);
    6259             : 
    6260             :         /*
    6261             :          * Switch to per-tuple memory context and reset it for each tuple
    6262             :          * produced, so we don't leak memory.
    6263             :          */
    6264        4302 :         oldCxt = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
    6265             : 
    6266      768838 :         while (table_scan_getnextslot(scan, ForwardScanDirection, oldslot))
    6267             :         {
    6268             :             TupleTableSlot *insertslot;
    6269             : 
    6270      764760 :             if (tab->rewrite > 0)
    6271             :             {
    6272             :                 /* Extract data from old tuple */
    6273       99570 :                 slot_getallattrs(oldslot);
    6274       99570 :                 ExecClearTuple(newslot);
    6275             : 
    6276             :                 /* copy attributes */
    6277       99570 :                 memcpy(newslot->tts_values, oldslot->tts_values,
    6278       99570 :                        sizeof(Datum) * oldslot->tts_nvalid);
    6279       99570 :                 memcpy(newslot->tts_isnull, oldslot->tts_isnull,
    6280       99570 :                        sizeof(bool) * oldslot->tts_nvalid);
    6281             : 
    6282             :                 /* Set dropped attributes to null in new tuple */
    6283       99656 :                 foreach(lc, dropped_attrs)
    6284          86 :                     newslot->tts_isnull[lfirst_int(lc)] = true;
    6285             : 
    6286             :                 /*
    6287             :                  * Constraints and GENERATED expressions might reference the
    6288             :                  * tableoid column, so fill tts_tableOid with the desired
    6289             :                  * value.  (We must do this each time, because it gets
    6290             :                  * overwritten with newrel's OID during storing.)
    6291             :                  */
    6292       99570 :                 newslot->tts_tableOid = RelationGetRelid(oldrel);
    6293             : 
    6294             :                 /*
    6295             :                  * Process supplied expressions to replace selected columns.
    6296             :                  *
    6297             :                  * First, evaluate expressions whose inputs come from the old
    6298             :                  * tuple.
    6299             :                  */
    6300       99570 :                 econtext->ecxt_scantuple = oldslot;
    6301             : 
    6302      204960 :                 foreach(l, tab->newvals)
    6303             :                 {
    6304      105402 :                     NewColumnValue *ex = lfirst(l);
    6305             : 
    6306      105402 :                     if (ex->is_generated)
    6307         150 :                         continue;
    6308             : 
    6309      105252 :                     newslot->tts_values[ex->attnum - 1]
    6310      105240 :                         = ExecEvalExpr(ex->exprstate,
    6311             :                                        econtext,
    6312      105252 :                                        &newslot->tts_isnull[ex->attnum - 1]);
    6313             :                 }
    6314             : 
    6315       99558 :                 ExecStoreVirtualTuple(newslot);
    6316             : 
    6317             :                 /*
    6318             :                  * Now, evaluate any expressions whose inputs come from the
    6319             :                  * new tuple.  We assume these columns won't reference each
    6320             :                  * other, so that there's no ordering dependency.
    6321             :                  */
    6322       99558 :                 econtext->ecxt_scantuple = newslot;
    6323             : 
    6324      204948 :                 foreach(l, tab->newvals)
    6325             :                 {
    6326      105390 :                     NewColumnValue *ex = lfirst(l);
    6327             : 
    6328      105390 :                     if (!ex->is_generated)
    6329      105240 :                         continue;
    6330             : 
    6331         150 :                     newslot->tts_values[ex->attnum - 1]
    6332         150 :                         = ExecEvalExpr(ex->exprstate,
    6333             :                                        econtext,
    6334         150 :                                        &newslot->tts_isnull[ex->attnum - 1]);
    6335             :                 }
    6336             : 
    6337       99558 :                 insertslot = newslot;
    6338             :             }
    6339             :             else
    6340             :             {
    6341             :                 /*
    6342             :                  * If there's no rewrite, old and new table are guaranteed to
    6343             :                  * have the same AM, so we can just use the old slot to verify
    6344             :                  * new constraints etc.
    6345             :                  */
    6346      665190 :                 insertslot = oldslot;
    6347             :             }
    6348             : 
    6349             :             /* Now check any constraints on the possibly-changed tuple */
    6350      764748 :             econtext->ecxt_scantuple = insertslot;
    6351             : 
    6352     3341030 :             foreach(l, notnull_attrs)
    6353             :             {
    6354     2576348 :                 int         attn = lfirst_int(l);
    6355             : 
    6356     2576348 :                 if (slot_attisnull(insertslot, attn + 1))
    6357             :                 {
    6358          66 :                     Form_pg_attribute attr = TupleDescAttr(newTupDesc, attn);
    6359             : 
    6360          66 :                     ereport(ERROR,
    6361             :                             (errcode(ERRCODE_NOT_NULL_VIOLATION),
    6362             :                              errmsg("column \"%s\" of relation \"%s\" contains null values",
    6363             :                                     NameStr(attr->attname),
    6364             :                                     RelationGetRelationName(oldrel)),
    6365             :                              errtablecol(oldrel, attn + 1)));
    6366             :                 }
    6367             :             }
    6368             : 
    6369      772782 :             foreach(l, tab->constraints)
    6370             :             {
    6371        8172 :                 NewConstraint *con = lfirst(l);
    6372             : 
    6373        8172 :                 switch (con->contype)
    6374             :                 {
    6375        8072 :                     case CONSTR_CHECK:
    6376        8072 :                         if (!ExecCheck(con->qualstate, econtext))
    6377          72 :                             ereport(ERROR,
    6378             :                                     (errcode(ERRCODE_CHECK_VIOLATION),
    6379             :                                      errmsg("check constraint \"%s\" of relation \"%s\" is violated by some row",
    6380             :                                             con->name,
    6381             :                                             RelationGetRelationName(oldrel)),
    6382             :                                      errtableconstraint(oldrel, con->name)));
    6383        8000 :                         break;
    6384         100 :                     case CONSTR_NOTNULL:
    6385             :                     case CONSTR_FOREIGN:
    6386             :                         /* Nothing to do here */
    6387         100 :                         break;
    6388           0 :                     default:
    6389           0 :                         elog(ERROR, "unrecognized constraint type: %d",
    6390             :                              (int) con->contype);
    6391             :                 }
    6392             :             }
    6393             : 
    6394      764610 :             if (partqualstate && !ExecCheck(partqualstate, econtext))
    6395             :             {
    6396          74 :                 if (tab->validate_default)
    6397          26 :                     ereport(ERROR,
    6398             :                             (errcode(ERRCODE_CHECK_VIOLATION),
    6399             :                              errmsg("updated partition constraint for default partition \"%s\" would be violated by some row",
    6400             :                                     RelationGetRelationName(oldrel)),
    6401             :                              errtable(oldrel)));
    6402             :                 else
    6403          48 :                     ereport(ERROR,
    6404             :                             (errcode(ERRCODE_CHECK_VIOLATION),
    6405             :                              errmsg("partition constraint of relation \"%s\" is violated by some row",
    6406             :                                     RelationGetRelationName(oldrel)),
    6407             :                              errtable(oldrel)));
    6408             :             }
    6409             : 
    6410             :             /* Write the tuple out to the new relation */
    6411      764536 :             if (newrel)
    6412       99546 :                 table_tuple_insert(newrel, insertslot, mycid,
    6413             :                                    ti_options, bistate);
    6414             : 
    6415      764536 :             ResetExprContext(econtext);
    6416             : 
    6417      764536 :             CHECK_FOR_INTERRUPTS();
    6418             :         }
    6419             : 
    6420        4078 :         MemoryContextSwitchTo(oldCxt);
    6421        4078 :         table_endscan(scan);
    6422        4078 :         UnregisterSnapshot(snapshot);
    6423             : 
    6424        4078 :         ExecDropSingleTupleTableSlot(oldslot);
    6425        4078 :         if (newslot)
    6426         862 :             ExecDropSingleTupleTableSlot(newslot);
    6427             :     }
    6428             : 
    6429        4996 :     FreeExecutorState(estate);
    6430             : 
    6431        4996 :     table_close(oldrel, NoLock);
    6432        4996 :     if (newrel)
    6433             :     {
    6434         862 :         FreeBulkInsertState(bistate);
    6435             : 
    6436         862 :         table_finish_bulk_insert(newrel, ti_options);
    6437             : 
    6438         862 :         table_close(newrel, NoLock);
    6439             :     }
    6440        4996 : }
    6441             : 
    6442             : /*
    6443             :  * ATGetQueueEntry: find or create an entry in the ALTER TABLE work queue
    6444             :  */
    6445             : static AlteredTableInfo *
    6446       38260 : ATGetQueueEntry(List **wqueue, Relation rel)
    6447             : {
    6448       38260 :     Oid         relid = RelationGetRelid(rel);
    6449             :     AlteredTableInfo *tab;
    6450             :     ListCell   *ltab;
    6451             : 
    6452       47230 :     foreach(ltab, *wqueue)
    6453             :     {
    6454       13944 :         tab = (AlteredTableInfo *) lfirst(ltab);
    6455       13944 :         if (tab->relid == relid)
    6456        4974 :             return tab;
    6457             :     }
    6458             : 
    6459             :     /*
    6460             :      * Not there, so add it.  Note that we make a copy of the relation's
    6461             :      * existing descriptor before anything interesting can happen to it.
    6462             :      */
    6463       33286 :     tab = (AlteredTableInfo *) palloc0(sizeof(AlteredTableInfo));
    6464       33286 :     tab->relid = relid;
    6465       33286 :     tab->rel = NULL;         /* set later */
    6466       33286 :     tab->relkind = rel->rd_rel->relkind;
    6467       33286 :     tab->oldDesc = CreateTupleDescCopyConstr(RelationGetDescr(rel));
    6468       33286 :     tab->newAccessMethod = InvalidOid;
    6469       33286 :     tab->chgAccessMethod = false;
    6470       33286 :     tab->newTableSpace = InvalidOid;
    6471       33286 :     tab->newrelpersistence = RELPERSISTENCE_PERMANENT;
    6472       33286 :     tab->chgPersistence = false;
    6473             : 
    6474       33286 :     *wqueue = lappend(*wqueue, tab);
    6475             : 
    6476       33286 :     return tab;
    6477             : }
    6478             : 
    6479             : static const char *
    6480          78 : alter_table_type_to_string(AlterTableType cmdtype)
    6481             : {
    6482          78 :     switch (cmdtype)
    6483             :     {
    6484           0 :         case AT_AddColumn:
    6485             :         case AT_AddColumnToView:
    6486           0 :             return "ADD COLUMN";
    6487           0 :         case AT_ColumnDefault:
    6488             :         case AT_CookedColumnDefault:
    6489           0 :             return "ALTER COLUMN ... SET DEFAULT";
    6490           6 :         case AT_DropNotNull:
    6491           6 :             return "ALTER COLUMN ... DROP NOT NULL";
    6492           6 :         case AT_SetNotNull:
    6493           6 :             return "ALTER COLUMN ... SET NOT NULL";
    6494           0 :         case AT_SetExpression:
    6495           0 :             return "ALTER COLUMN ... SET EXPRESSION";
    6496           0 :         case AT_DropExpression:
    6497           0 :             return "ALTER COLUMN ... DROP EXPRESSION";
    6498           0 :         case AT_SetStatistics:
    6499           0 :             return "ALTER COLUMN ... SET STATISTICS";
    6500          12 :         case AT_SetOptions:
    6501          12 :             return "ALTER COLUMN ... SET";
    6502           0 :         case AT_ResetOptions:
    6503           0 :             return "ALTER COLUMN ... RESET";
    6504           0 :         case AT_SetStorage:
    6505           0 :             return "ALTER COLUMN ... SET STORAGE";
    6506           0 :         case AT_SetCompression:
    6507           0 :             return "ALTER COLUMN ... SET COMPRESSION";
    6508           6 :         case AT_DropColumn:
    6509           6 :             return "DROP COLUMN";
    6510           0 :         case AT_AddIndex:
    6511             :         case AT_ReAddIndex:
    6512           0 :             return NULL;        /* not real grammar */
    6513           0 :         case AT_AddConstraint:
    6514             :         case AT_ReAddConstraint:
    6515             :         case AT_ReAddDomainConstraint:
    6516             :         case AT_AddIndexConstraint:
    6517           0 :             return "ADD CONSTRAINT";
    6518           6 :         case AT_AlterConstraint:
    6519           6 :             return "ALTER CONSTRAINT";
    6520           0 :         case AT_ValidateConstraint:
    6521           0 :             return "VALIDATE CONSTRAINT";
    6522           0 :         case AT_DropConstraint:
    6523           0 :             return "DROP CONSTRAINT";
    6524           0 :         case AT_ReAddComment:
    6525           0 :             return NULL;        /* not real grammar */
    6526           0 :         case AT_AlterColumnType:
    6527           0 :             return "ALTER COLUMN ... SET DATA TYPE";
    6528           0 :         case AT_AlterColumnGenericOptions:
    6529           0 :             return "ALTER COLUMN ... OPTIONS";
    6530           0 :         case AT_ChangeOwner:
    6531           0 :             return "OWNER TO";
    6532           0 :         case AT_ClusterOn:
    6533           0 :             return "CLUSTER ON";
    6534           0 :         case AT_DropCluster:
    6535           0 :             return "SET WITHOUT CLUSTER";
    6536           0 :         case AT_SetAccessMethod:
    6537           0 :             return "SET ACCESS METHOD";
    6538           6 :         case AT_SetLogged:
    6539           6 :             return "SET LOGGED";
    6540           6 :         case AT_SetUnLogged:
    6541           6 :             return "SET UNLOGGED";
    6542           0 :         case AT_DropOids:
    6543           0 :             return "SET WITHOUT OIDS";
    6544           0 :         case AT_SetTableSpace:
    6545           0 :             return "SET TABLESPACE";
    6546           0 :         case AT_SetRelOptions:
    6547           0 :             return "SET";
    6548           0 :         case AT_ResetRelOptions:
    6549           0 :             return "RESET";
    6550           0 :         case AT_ReplaceRelOptions:
    6551           0 :             return NULL;        /* not real grammar */
    6552           0 :         case AT_EnableTrig:
    6553           0 :             return "ENABLE TRIGGER";
    6554           0 :         case AT_EnableAlwaysTrig:
    6555           0 :             return "ENABLE ALWAYS TRIGGER";
    6556           0 :         case AT_EnableReplicaTrig:
    6557           0 :             return "ENABLE REPLICA TRIGGER";
    6558           0 :         case AT_DisableTrig:
    6559           0 :             return "DISABLE TRIGGER";
    6560           0 :         case AT_EnableTrigAll:
    6561           0 :             return "ENABLE TRIGGER ALL";
    6562           0 :         case AT_DisableTrigAll:
    6563           0 :             return "DISABLE TRIGGER ALL";
    6564           0 :         case AT_EnableTrigUser:
    6565           0 :             return "ENABLE TRIGGER USER";
    6566           0 :         case AT_DisableTrigUser:
    6567           0 :             return "DISABLE TRIGGER USER";
    6568           0 :         case AT_EnableRule:
    6569           0 :             return "ENABLE RULE";
    6570           0 :         case AT_EnableAlwaysRule:
    6571           0 :             return "ENABLE ALWAYS RULE";
    6572           0 :         case AT_EnableReplicaRule:
    6573           0 :             return "ENABLE REPLICA RULE";
    6574           0 :         case AT_DisableRule:
    6575           0 :             return "DISABLE RULE";
    6576           0 :         case AT_AddInherit:
    6577           0 :             return "INHERIT";
    6578           0 :         case AT_DropInherit:
    6579           0 :             return "NO INHERIT";
    6580           0 :         case AT_AddOf:
    6581           0 :             return "OF";
    6582           0 :         case AT_DropOf:
    6583           0 :             return "NOT OF";
    6584           0 :         case AT_ReplicaIdentity:
    6585           0 :             return "REPLICA IDENTITY";
    6586           0 :         case AT_EnableRowSecurity:
    6587           0 :             return "ENABLE ROW SECURITY";
    6588           0 :         case AT_DisableRowSecurity:
    6589           0 :             return "DISABLE ROW SECURITY";
    6590           0 :         case AT_ForceRowSecurity:
    6591           0 :             return "FORCE ROW SECURITY";
    6592           0 :         case AT_NoForceRowSecurity:
    6593           0 :             return "NO FORCE ROW SECURITY";
    6594           0 :         case AT_GenericOptions:
    6595           0 :             return "OPTIONS";
    6596           6 :         case AT_AttachPartition:
    6597           6 :             return "ATTACH PARTITION";
    6598          18 :         case AT_DetachPartition:
    6599          18 :             return "DETACH PARTITION";
    6600           6 :         case AT_DetachPartitionFinalize:
    6601           6 :             return "DETACH PARTITION ... FINALIZE";
    6602           0 :         case AT_AddIdentity:
    6603           0 :             return "ALTER COLUMN ... ADD IDENTITY";
    6604           0 :         case AT_SetIdentity:
    6605           0 :             return "ALTER COLUMN ... SET";
    6606           0 :         case AT_DropIdentity:
    6607           0 :             return "ALTER COLUMN ... DROP IDENTITY";
    6608           0 :         case AT_ReAddStatistics:
    6609           0 :             return NULL;        /* not real grammar */
    6610             :     }
    6611             : 
    6612           0 :     return NULL;
    6613             : }
    6614             : 
    6615             : /*
    6616             :  * ATSimplePermissions
    6617             :  *
    6618             :  * - Ensure that it is a relation (or possibly a view)
    6619             :  * - Ensure this user is the owner
    6620             :  * - Ensure that it is not a system table
    6621             :  */
    6622             : static void
    6623       34768 : ATSimplePermissions(AlterTableType cmdtype, Relation rel, int allowed_targets)
    6624             : {
    6625             :     int         actual_target;
    6626             : 
    6627       34768 :     switch (rel->rd_rel->relkind)
    6628             :     {
    6629       27234 :         case RELKIND_RELATION:
    6630       27234 :             actual_target = ATT_TABLE;
    6631       27234 :             break;
    6632        5286 :         case RELKIND_PARTITIONED_TABLE:
    6633        5286 :             actual_target = ATT_PARTITIONED_TABLE;
    6634        5286 :             break;
    6635         396 :         case RELKIND_VIEW:
    6636         396 :             actual_target = ATT_VIEW;
    6637         396 :             break;
    6638          46 :         case RELKIND_MATVIEW:
    6639          46 :             actual_target = ATT_MATVIEW;
    6640          46 :             break;
    6641         226 :         case RELKIND_INDEX:
    6642         226 :             actual_target = ATT_INDEX;
    6643         226 :             break;
    6644         434 :         case RELKIND_PARTITIONED_INDEX:
    6645         434 :             actual_target = ATT_PARTITIONED_INDEX;
    6646         434 :             break;
    6647         214 :         case RELKIND_COMPOSITE_TYPE:
    6648         214 :             actual_target = ATT_COMPOSITE_TYPE;
    6649         214 :             break;
    6650         908 :         case RELKIND_FOREIGN_TABLE:
    6651         908 :             actual_target = ATT_FOREIGN_TABLE;
    6652         908 :             break;
    6653          24 :         case RELKIND_SEQUENCE:
    6654          24 :             actual_target = ATT_SEQUENCE;
    6655          24 :             break;
    6656           0 :         default:
    6657           0 :             actual_target = 0;
    6658           0 :             break;
    6659             :     }
    6660             : 
    6661             :     /* Wrong target type? */
    6662       34768 :     if ((actual_target & allowed_targets) == 0)
    6663             :     {
    6664          78 :         const char *action_str = alter_table_type_to_string(cmdtype);
    6665             : 
    6666          78 :         if (action_str)
    6667          78 :             ereport(ERROR,
    6668             :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    6669             :             /* translator: %s is a group of some SQL keywords */
    6670             :                      errmsg("ALTER action %s cannot be performed on relation \"%s\"",
    6671             :                             action_str, RelationGetRelationName(rel)),
    6672             :                      errdetail_relkind_not_supported(rel->rd_rel->relkind)));
    6673             :         else
    6674             :             /* internal error? */
    6675           0 :             elog(ERROR, "invalid ALTER action attempted on relation \"%s\"",
    6676             :                  RelationGetRelationName(rel));
    6677             :     }
    6678             : 
    6679             :     /* Permissions checks */
    6680       34690 :     if (!object_ownercheck(RelationRelationId, RelationGetRelid(rel), GetUserId()))
    6681          12 :         aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(rel->rd_rel->relkind),
    6682          12 :                        RelationGetRelationName(rel));
    6683             : 
    6684       34678 :     if (!allowSystemTableMods && IsSystemRelation(rel))
    6685           0 :         ereport(ERROR,
    6686             :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
    6687             :                  errmsg("permission denied: \"%s\" is a system catalog",
    6688             :                         RelationGetRelationName(rel))));
    6689       34678 : }
    6690             : 
    6691             : /*
    6692             :  * ATSimpleRecursion
    6693             :  *
    6694             :  * Simple table recursion sufficient for most ALTER TABLE operations.
    6695             :  * All direct and indirect children are processed in an unspecified order.
    6696             :  * Note that if a child inherits from the original table via multiple
    6697             :  * inheritance paths, it will be visited just once.
    6698             :  */
    6699             : static void
    6700        1124 : ATSimpleRecursion(List **wqueue, Relation rel,
    6701             :                   AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode,
    6702             :                   AlterTableUtilityContext *context)
    6703             : {
    6704             :     /*
    6705             :      * Propagate to children, if desired and if there are (or might be) any
    6706             :      * children.
    6707             :      */
    6708        1124 :     if (recurse && rel->rd_rel->relhassubclass)
    6709             :     {
    6710          66 :         Oid         relid = RelationGetRelid(rel);
    6711             :         ListCell   *child;
    6712             :         List       *children;
    6713             : 
    6714          66 :         children = find_all_inheritors(relid, lockmode, NULL);
    6715             : 
    6716             :         /*
    6717             :          * find_all_inheritors does the recursive search of the inheritance
    6718             :          * hierarchy, so all we have to do is process all of the relids in the
    6719             :          * list that it returns.
    6720             :          */
    6721         294 :         foreach(child, children)
    6722             :         {
    6723         228 :             Oid         childrelid = lfirst_oid(child);
    6724             :             Relation    childrel;
    6725             : 
    6726         228 :             if (childrelid == relid)
    6727          66 :                 continue;
    6728             :             /* find_all_inheritors already got lock */
    6729         162 :             childrel = relation_open(childrelid, NoLock);
    6730         162 :             CheckAlterTableIsSafe(childrel);
    6731         162 :             ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode, context);
    6732         162 :             relation_close(childrel, NoLock);
    6733             :         }
    6734             :     }
    6735        1124 : }
    6736             : 
    6737             : /*
    6738             :  * Obtain list of partitions of the given table, locking them all at the given
    6739             :  * lockmode and ensuring that they all pass CheckAlterTableIsSafe.
    6740             :  *
    6741             :  * This function is a no-op if the given relation is not a partitioned table;
    6742             :  * in particular, nothing is done if it's a legacy inheritance parent.
    6743             :  */
    6744             : static void
    6745         822 : ATCheckPartitionsNotInUse(Relation rel, LOCKMODE lockmode)
    6746             : {
    6747         822 :     if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
    6748             :     {
    6749             :         List       *inh;
    6750             :         ListCell   *cell;
    6751             : 
    6752         188 :         inh = find_all_inheritors(RelationGetRelid(rel), lockmode, NULL);
    6753             :         /* first element is the parent rel; must ignore it */
    6754         610 :         for_each_from(cell, inh, 1)
    6755             :         {
    6756             :             Relation    childrel;
    6757             : 
    6758             :             /* find_all_inheritors already got lock */
    6759         428 :             childrel = table_open(lfirst_oid(cell), NoLock);
    6760         428 :             CheckAlterTableIsSafe(childrel);
    6761         422 :             table_close(childrel, NoLock);
    6762             :         }
    6763         182 :         list_free(inh);
    6764             :     }
    6765         816 : }
    6766             : 
    6767             : /*
    6768             :  * ATTypedTableRecursion
    6769             :  *
    6770             :  * Propagate ALTER TYPE operations to the typed tables of that type.
    6771             :  * Also check the RESTRICT/CASCADE behavior.  Given CASCADE, also permit
    6772             :  * recursion to inheritance children of the typed tables.
    6773             :  */
    6774             : static void
    6775         190 : ATTypedTableRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd,
    6776             :                       LOCKMODE lockmode, AlterTableUtilityContext *context)
    6777             : {
    6778             :     ListCell   *child;
    6779             :     List       *children;
    6780             : 
    6781             :     Assert(rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE);
    6782             : 
    6783         190 :     children = find_typed_table_dependencies(rel->rd_rel->reltype,
    6784         190 :                                              RelationGetRelationName(rel),
    6785             :                                              cmd->behavior);
    6786             : 
    6787         202 :     foreach(child, children)
    6788             :     {
    6789          30 :         Oid         childrelid = lfirst_oid(child);
    6790             :         Relation    childrel;
    6791             : 
    6792          30 :         childrel = relation_open(childrelid, lockmode);
    6793          30 :         CheckAlterTableIsSafe(childrel);
    6794          30 :         ATPrepCmd(wqueue, childrel, cmd, true, true, lockmode, context);
    6795          30 :         relation_close(childrel, NoLock);
    6796             :     }
    6797         172 : }
    6798             : 
    6799             : 
    6800             : /*
    6801             :  * find_composite_type_dependencies
    6802             :  *
    6803             :  * Check to see if the type "typeOid" is being used as a column in some table
    6804             :  * (possibly nested several levels deep in composite types, arrays, etc!).
    6805             :  * Eventually, we'd like to propagate the check or rewrite operation
    6806             :  * into such tables, but for now, just error out if we find any.
    6807             :  *
    6808             :  * Caller should provide either the associated relation of a rowtype,
    6809             :  * or a type name (not both) for use in the error message, if any.
    6810             :  *
    6811             :  * Note that "typeOid" is not necessarily a composite type; it could also be
    6812             :  * another container type such as an array or range, or a domain over one of
    6813             :  * these things.  The name of this function is therefore somewhat historical,
    6814             :  * but it's not worth changing.
    6815             :  *
    6816             :  * We assume that functions and views depending on the type are not reasons
    6817             :  * to reject the ALTER.  (How safe is this really?)
    6818             :  */
    6819             : void
    6820        3960 : find_composite_type_dependencies(Oid typeOid, Relation origRelation,
    6821             :                                  const char *origTypeName)
    6822             : {
    6823             :     Relation    depRel;
    6824             :     ScanKeyData key[2];
    6825             :     SysScanDesc depScan;
    6826             :     HeapTuple   depTup;
    6827             : 
    6828             :     /* since this function recurses, it could be driven to stack overflow */
    6829        3960 :     check_stack_depth();
    6830             : 
    6831             :     /*
    6832             :      * We scan pg_depend to find those things that depend on the given type.
    6833             :      * (We assume we can ignore refobjsubid for a type.)
    6834             :      */
    6835        3960 :     depRel = table_open(DependRelationId, AccessShareLock);
    6836             : 
    6837        3960 :     ScanKeyInit(&key[0],
    6838             :                 Anum_pg_depend_refclassid,
    6839             :                 BTEqualStrategyNumber, F_OIDEQ,
    6840             :                 ObjectIdGetDatum(TypeRelationId));
    6841        3960 :     ScanKeyInit(&key[1],
    6842             :                 Anum_pg_depend_refobjid,
    6843             :                 BTEqualStrategyNumber, F_OIDEQ,
    6844             :                 ObjectIdGetDatum(typeOid));
    6845             : 
    6846        3960 :     depScan = systable_beginscan(depRel, DependReferenceIndexId, true,
    6847             :                                  NULL, 2, key);
    6848             : 
    6849        6108 :     while (HeapTupleIsValid(depTup = systable_getnext(depScan)))
    6850             :     {
    6851        2244 :         Form_pg_depend pg_depend = (Form_pg_depend) GETSTRUCT(depTup);
    6852             :         Relation    rel;
    6853             :         TupleDesc   tupleDesc;
    6854             :         Form_pg_attribute att;
    6855             : 
    6856             :         /* Check for directly dependent types */
    6857        2244 :         if (pg_depend->classid == TypeRelationId)
    6858             :         {
    6859             :             /*
    6860             :              * This must be an array, domain, or range containing the given
    6861             :              * type, so recursively check for uses of this type.  Note that
    6862             :              * any error message will mention the original type not the
    6863             :              * container; this is intentional.
    6864             :              */
    6865        1898 :             find_composite_type_dependencies(pg_depend->objid,
    6866             :                                              origRelation, origTypeName);
    6867        1874 :             continue;
    6868             :         }
    6869             : 
    6870             :         /* Else, ignore dependees that aren't relations */
    6871         346 :         if (pg_depend->classid != RelationRelationId)
    6872         122 :             continue;
    6873             : 
    6874         224 :         rel = relation_open(pg_depend->objid, AccessShareLock);
    6875         224 :         tupleDesc = RelationGetDescr(rel);
    6876             : 
    6877             :         /*
    6878             :          * If objsubid identifies a specific column, refer to that in error
    6879             :          * messages.  Otherwise, search to see if there's a user column of the
    6880             :          * type.  (We assume system columns are never of interesting types.)
    6881             :          * The search is needed because an index containing an expression
    6882             :          * column of the target type will just be recorded as a whole-relation
    6883             :          * dependency.  If we do not find a column of the type, the dependency
    6884             :          * must indicate that the type is transiently referenced in an index
    6885             :          * expression but not stored on disk, which we assume is OK, just as
    6886             :          * we do for references in views.  (It could also be that the target
    6887             :          * type is embedded in some container type that is stored in an index
    6888             :          * column, but the previous recursion should catch such cases.)
    6889             :          */
    6890         224 :         if (pg_depend->objsubid > 0 && pg_depend->objsubid <= tupleDesc->natts)
    6891          66 :             att = TupleDescAttr(tupleDesc, pg_depend->objsubid - 1);
    6892             :         else
    6893             :         {
    6894         158 :             att = NULL;
    6895         406 :             for (int attno = 1; attno <= tupleDesc->natts; attno++)
    6896             :             {
    6897         254 :                 att = TupleDescAttr(tupleDesc, attno - 1);
    6898         254 :                 if (att->atttypid == typeOid && !att->attisdropped)
    6899           6 :                     break;
    6900         248 :                 att = NULL;
    6901             :             }
    6902         158 :             if (att == NULL)
    6903             :             {
    6904             :                 /* No such column, so assume OK */
    6905         152 :                 relation_close(rel, AccessShareLock);
    6906         152 :                 continue;
    6907             :             }
    6908             :         }
    6909             : 
    6910             :         /*
    6911             :          * We definitely should reject if the relation has storage.  If it's
    6912             :          * partitioned, then perhaps we don't have to reject: if there are
    6913             :          * partitions then we'll fail when we find one, else there is no
    6914             :          * stored data to worry about.  However, it's possible that the type
    6915             :          * change would affect conclusions about whether the type is sortable
    6916             :          * or hashable and thus (if it's a partitioning column) break the
    6917             :          * partitioning rule.  For now, reject for partitioned rels too.
    6918             :          */
    6919          72 :         if (RELKIND_HAS_STORAGE(rel->rd_rel->relkind) ||
    6920           0 :             RELKIND_HAS_PARTITIONS(rel->rd_rel->relkind))
    6921             :         {
    6922          72 :             if (origTypeName)
    6923          30 :                 ereport(ERROR,
    6924             :                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    6925             :                          errmsg("cannot alter type \"%s\" because column \"%s.%s\" uses it",
    6926             :                                 origTypeName,
    6927             :                                 RelationGetRelationName(rel),
    6928             :                                 NameStr(att->attname))));
    6929          42 :             else if (origRelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
    6930          18 :                 ereport(ERROR,
    6931             :                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    6932             :                          errmsg("cannot alter type \"%s\" because column \"%s.%s\" uses it",
    6933             :                                 RelationGetRelationName(origRelation),
    6934             :                                 RelationGetRelationName(rel),
    6935             :                                 NameStr(att->attname))));
    6936          24 :             else if (origRelation->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
    6937           6 :                 ereport(ERROR,
    6938             :                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    6939             :                          errmsg("cannot alter foreign table \"%s\" because column \"%s.%s\" uses its row type",
    6940             :                                 RelationGetRelationName(origRelation),
    6941             :                                 RelationGetRelationName(rel),
    6942             :                                 NameStr(att->attname))));
    6943             :             else
    6944          18 :                 ereport(ERROR,
    6945             :                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    6946             :                          errmsg("cannot alter table \"%s\" because column \"%s.%s\" uses its row type",
    6947             :                                 RelationGetRelationName(origRelation),
    6948             :                                 RelationGetRelationName(rel),
    6949             :                                 NameStr(att->attname))));
    6950             :         }
    6951           0 :         else if (OidIsValid(rel->rd_rel->reltype))
    6952             :         {
    6953             :             /*
    6954             :              * A view or composite type itself isn't a problem, but we must
    6955             :              * recursively check for indirect dependencies via its rowtype.
    6956             :              */
    6957           0 :             find_composite_type_dependencies(rel->rd_rel->reltype,
    6958             :                                              origRelation, origTypeName);
    6959             :         }
    6960             : 
    6961           0 :         relation_close(rel, AccessShareLock);
    6962             :     }
    6963             : 
    6964        3864 :     systable_endscan(depScan);
    6965             : 
    6966        3864 :     relation_close(depRel, AccessShareLock);
    6967        3864 : }
    6968             : 
    6969             : 
    6970             : /*
    6971             :  * find_typed_table_dependencies
    6972             :  *
    6973             :  * Check to see if a composite type is being used as the type of a
    6974             :  * typed table.  Abort if any are found and behavior is RESTRICT.
    6975             :  * Else return the list of tables.
    6976             :  */
    6977             : static List *
    6978         214 : find_typed_table_dependencies(Oid typeOid, const char *typeName, DropBehavior behavior)
    6979             : {
    6980             :     Relation    classRel;
    6981             :     ScanKeyData key[1];
    6982             :     TableScanDesc scan;
    6983             :     HeapTuple   tuple;
    6984         214 :     List       *result = NIL;
    6985             : 
    6986         214 :     classRel = table_open(RelationRelationId, AccessShareLock);
    6987             : 
    6988         214 :     ScanKeyInit(&key[0],
    6989             :                 Anum_pg_class_reloftype,
    6990             :                 BTEqualStrategyNumber, F_OIDEQ,
    6991             :                 ObjectIdGetDatum(typeOid));
    6992             : 
    6993         214 :     scan = table_beginscan_catalog(classRel, 1, key);
    6994             : 
    6995         250 :     while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
    6996             :     {
    6997          60 :         Form_pg_class classform = (Form_pg_class) GETSTRUCT(tuple);
    6998             : 
    6999          60 :         if (behavior == DROP_RESTRICT)
    7000          24 :             ereport(ERROR,
    7001             :                     (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
    7002             :                      errmsg("cannot alter type \"%s\" because it is the type of a typed table",
    7003             :                             typeName),
    7004             :                      errhint("Use ALTER ... CASCADE to alter the typed tables too.")));
    7005             :         else
    7006          36 :             result = lappend_oid(result, classform->oid);
    7007             :     }
    7008             : 
    7009         190 :     table_endscan(scan);
    7010         190 :     table_close(classRel, AccessShareLock);
    7011             : 
    7012         190 :     return result;
    7013             : }
    7014             : 
    7015             : 
    7016             : /*
    7017             :  * check_of_type
    7018             :  *
    7019             :  * Check whether a type is suitable for CREATE TABLE OF/ALTER TABLE OF.  If it
    7020             :  * isn't suitable, throw an error.  Currently, we require that the type
    7021             :  * originated with CREATE TYPE AS.  We could support any row type, but doing so
    7022             :  * would require handling a number of extra corner cases in the DDL commands.
    7023             :  * (Also, allowing domain-over-composite would open up a can of worms about
    7024             :  * whether and how the domain's constraints should apply to derived tables.)
    7025             :  */
    7026             : void
    7027         176 : check_of_type(HeapTuple typetuple)
    7028             : {
    7029         176 :     Form_pg_type typ = (Form_pg_type) GETSTRUCT(typetuple);
    7030         176 :     bool        typeOk = false;
    7031             : 
    7032         176 :     if (typ->typtype == TYPTYPE_COMPOSITE)
    7033             :     {
    7034             :         Relation    typeRelation;
    7035             : 
    7036             :         Assert(OidIsValid(typ->typrelid));
    7037         170 :         typeRelation = relation_open(typ->typrelid, AccessShareLock);
    7038         170 :         typeOk = (typeRelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE);
    7039             : 
    7040             :         /*
    7041             :          * Close the parent rel, but keep our AccessShareLock on it until xact
    7042             :          * commit.  That will prevent someone else from deleting or ALTERing
    7043             :          * the type before the typed table creation/conversion commits.
    7044             :          */
    7045         170 :         relation_close(typeRelation, NoLock);
    7046             : 
    7047         170 :         if (!typeOk)
    7048           6 :             ereport(ERROR,
    7049             :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    7050             :                      errmsg("type %s is the row type of another table",
    7051             :                             format_type_be(typ->oid)),
    7052             :                      errdetail("A typed table must use a stand-alone composite type created with CREATE TYPE.")));
    7053             :     }
    7054             :     else
    7055           6 :         ereport(ERROR,
    7056             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    7057             :                  errmsg("type %s is not a composite type",
    7058             :                         format_type_be(typ->oid))));
    7059         164 : }
    7060             : 
    7061             : 
    7062             : /*
    7063             :  * ALTER TABLE ADD COLUMN
    7064             :  *
    7065             :  * Adds an additional attribute to a relation making the assumption that
    7066             :  * CHECK, NOT NULL, and FOREIGN KEY constraints will be removed from the
    7067             :  * AT_AddColumn AlterTableCmd by parse_utilcmd.c and added as independent
    7068             :  * AlterTableCmd's.
    7069             :  *
    7070             :  * ADD COLUMN cannot use the normal ALTER TABLE recursion mechanism, because we
    7071             :  * have to decide at runtime whether to recurse or not depending on whether we
    7072             :  * actually add a column or merely merge with an existing column.  (We can't
    7073             :  * check this in a static pre-pass because it won't handle multiple inheritance
    7074             :  * situations correctly.)
    7075             :  */
    7076             : static void
    7077        2014 : ATPrepAddColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
    7078             :                 bool is_view, AlterTableCmd *cmd, LOCKMODE lockmode,
    7079             :                 AlterTableUtilityContext *context)
    7080             : {
    7081        2014 :     if (rel->rd_rel->reloftype && !recursing)
    7082           6 :         ereport(ERROR,
    7083             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    7084             :                  errmsg("cannot add column to typed table")));
    7085             : 
    7086        2008 :     if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
    7087          58 :         ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
    7088             : 
    7089        2002 :     if (recurse && !is_view)
    7090        1902 :         cmd->recurse = true;
    7091        2002 : }
    7092             : 
    7093             : /*
    7094             :  * Add a column to a table.  The return value is the address of the
    7095             :  * new column in the parent relation.
    7096             :  *
    7097             :  * cmd is pass-by-ref so that we can replace it with the parse-transformed
    7098             :  * copy (but that happens only after we check for IF NOT EXISTS).
    7099             :  */
    7100             : static ObjectAddress
    7101        2644 : ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
    7102             :                 AlterTableCmd **cmd, bool recurse, bool recursing,
    7103             :                 LOCKMODE lockmode, AlterTablePass cur_pass,
    7104             :                 AlterTableUtilityContext *context)
    7105             : {
    7106        2644 :     Oid         myrelid = RelationGetRelid(rel);
    7107        2644 :     ColumnDef  *colDef = castNode(ColumnDef, (*cmd)->def);
    7108        2644 :     bool        if_not_exists = (*cmd)->missing_ok;
    7109             :     Relation    pgclass,
    7110             :                 attrdesc;
    7111             :     HeapTuple   reltup;
    7112             :     Form_pg_class relform;
    7113             :     Form_pg_attribute attribute;
    7114             :     int         newattnum;
    7115             :     char        relkind;
    7116             :     Expr       *defval;
    7117             :     List       *children;
    7118             :     ListCell   *child;
    7119             :     AlterTableCmd *childcmd;
    7120             :     ObjectAddress address;
    7121             :     TupleDesc   tupdesc;
    7122             : 
    7123             :     /* since this function recurses, it could be driven to stack overflow */
    7124        2644 :     check_stack_depth();
    7125             : 
    7126             :     /* At top level, permission check was done in ATPrepCmd, else do it */
    7127        2644 :     if (recursing)
    7128         648 :         ATSimplePermissions((*cmd)->subtype, rel,
    7129             :                             ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
    7130             : 
    7131        2644 :     if (rel->rd_rel->relispartition && !recursing)
    7132          12 :         ereport(ERROR,
    7133             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    7134             :                  errmsg("cannot add column to a partition")));
    7135             : 
    7136        2632 :     attrdesc = table_open(AttributeRelationId, RowExclusiveLock);
    7137             : 
    7138             :     /*
    7139             :      * Are we adding the column to a recursion child?  If so, check whether to
    7140             :      * merge with an existing definition for the column.  If we do merge, we
    7141             :      * must not recurse.  Children will already have the column, and recursing
    7142             :      * into them would mess up attinhcount.
    7143             :      */
    7144        2632 :     if (colDef->inhcount > 0)
    7145             :     {
    7146             :         HeapTuple   tuple;
    7147             : 
    7148             :         /* Does child already have a column by this name? */
    7149         648 :         tuple = SearchSysCacheCopyAttName(myrelid, colDef->colname);
    7150         648 :         if (HeapTupleIsValid(tuple))
    7151             :         {
    7152          48 :             Form_pg_attribute childatt = (Form_pg_attribute) GETSTRUCT(tuple);
    7153             :             Oid         ctypeId;
    7154             :             int32       ctypmod;
    7155             :             Oid         ccollid;
    7156             : 
    7157             :             /* Child column must match on type, typmod, and collation */
    7158          48 :             typenameTypeIdAndMod(NULL, colDef->typeName, &ctypeId, &ctypmod);
    7159          48 :             if (ctypeId != childatt->atttypid ||
    7160          48 :                 ctypmod != childatt->atttypmod)
    7161           0 :                 ereport(ERROR,
    7162             :                         (errcode(ERRCODE_DATATYPE_MISMATCH),
    7163             :                          errmsg("child table \"%s\" has different type for column \"%s\"",
    7164             :                                 RelationGetRelationName(rel), colDef->colname)));
    7165          48 :             ccollid = GetColumnDefCollation(NULL, colDef, ctypeId);
    7166          48 :             if (ccollid != childatt->attcollation)
    7167           0 :                 ereport(ERROR,
    7168             :                         (errcode(ERRCODE_COLLATION_MISMATCH),
    7169             :                          errmsg("child table \"%s\" has different collation for column \"%s\"",
    7170             :                                 RelationGetRelationName(rel), colDef->colname),
    7171             :                          errdetail("\"%s\" versus \"%s\"",
    7172             :                                    get_collation_name(ccollid),
    7173             :                                    get_collation_name(childatt->attcollation))));
    7174             : 
    7175             :             /* Bump the existing child att's inhcount */
    7176          48 :             if (pg_add_s16_overflow(childatt->attinhcount, 1,
    7177             :                                     &childatt->attinhcount))
    7178           0 :                 ereport(ERROR,
    7179             :                         errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
    7180             :                         errmsg("too many inheritance parents"));
    7181          48 :             CatalogTupleUpdate(attrdesc, &tuple->t_self, tuple);
    7182             : 
    7183          48 :             heap_freetuple(tuple);
    7184             : 
    7185             :             /* Inform the user about the merge */
    7186          48 :             ereport(NOTICE,
    7187             :                     (errmsg("merging definition of column \"%s\" for child \"%s\"",
    7188             :                             colDef->colname, RelationGetRelationName(rel))));
    7189             : 
    7190          48 :             table_close(attrdesc, RowExclusiveLock);
    7191             : 
    7192             :             /* Make the child column change visible */
    7193          48 :             CommandCounterIncrement();
    7194             : 
    7195          48 :             return InvalidObjectAddress;
    7196             :         }
    7197             :     }
    7198             : 
    7199             :     /* skip if the name already exists and if_not_exists is true */
    7200        2584 :     if (!check_for_column_name_collision(rel, colDef->colname, if_not_exists))
    7201             :     {
    7202          54 :         table_close(attrdesc, RowExclusiveLock);
    7203          54 :         return InvalidObjectAddress;
    7204             :     }
    7205             : 
    7206             :     /*
    7207             :      * Okay, we need to add the column, so go ahead and do parse
    7208             :      * transformation.  This can result in queueing up, or even immediately
    7209             :      * executing, subsidiary operations (such as creation of unique indexes);
    7210             :      * so we mustn't do it until we have made the if_not_exists check.
    7211             :      *
    7212             :      * When recursing, the command was already transformed and we needn't do
    7213             :      * so again.  Also, if context isn't given we can't transform.  (That
    7214             :      * currently happens only for AT_AddColumnToView; we expect that view.c
    7215             :      * passed us a ColumnDef that doesn't need work.)
    7216             :      */
    7217        2500 :     if (context != NULL && !recursing)
    7218             :     {
    7219        1876 :         *cmd = ATParseTransformCmd(wqueue, tab, rel, *cmd, recurse, lockmode,
    7220             :                                    cur_pass, context);
    7221             :         Assert(*cmd != NULL);
    7222        1870 :         colDef = castNode(ColumnDef, (*cmd)->def);
    7223             :     }
    7224             : 
    7225             :     /*
    7226             :      * Regular inheritance children are independent enough not to inherit the
    7227             :      * identity column from parent hence cannot recursively add identity
    7228             :      * column if the table has inheritance children.
    7229             :      *
    7230             :      * Partitions, on the other hand, are integral part of a partitioned table
    7231             :      * and inherit identity column.  Hence propagate identity column down the
    7232             :      * partition hierarchy.
    7233             :      */
    7234        2494 :     if (colDef->identity &&
    7235          54 :         recurse &&
    7236         102 :         rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE &&
    7237          48 :         find_inheritance_children(myrelid, NoLock) != NIL)
    7238           6 :         ereport(ERROR,
    7239             :                 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    7240             :                  errmsg("cannot recursively add identity column to table that has child tables")));
    7241             : 
    7242        2488 :     pgclass = table_open(RelationRelationId, RowExclusiveLock);
    7243             : 
    7244        2488 :     reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(myrelid));
    7245        2488 :     if (!HeapTupleIsValid(reltup))
    7246           0 :         elog(ERROR, "cache lookup failed for relation %u", myrelid);
    7247        2488 :     relform = (Form_pg_class) GETSTRUCT(reltup);
    7248        2488 :     relkind = relform->relkind;
    7249             : 
    7250             :     /* Determine the new attribute's number */
    7251        2488 :     newattnum = relform->relnatts + 1;
    7252        2488 :     if (newattnum > MaxHeapAttributeNumber)
    7253           0 :         ereport(ERROR,
    7254             :                 (errcode(ERRCODE_TOO_MANY_COLUMNS),
    7255             :                  errmsg("tables can have at most %d columns",
    7256             :                         MaxHeapAttributeNumber)));
    7257             : 
    7258             :     /*
    7259             :      * Construct new attribute's pg_attribute entry.
    7260             :      */
    7261        2488 :     tupdesc = BuildDescForRelation(list_make1(colDef));
    7262             : 
    7263        2476 :     attribute = TupleDescAttr(tupdesc, 0);
    7264             : 
    7265             :     /* Fix up attribute number */
    7266        2476 :     attribute->attnum = newattnum;
    7267             : 
    7268             :     /* make sure datatype is legal for a column */
    7269        2476 :     CheckAttributeType(NameStr(attribute->attname), attribute->atttypid, attribute->attcollation,
    7270        2476 :                        list_make1_oid(rel->rd_rel->reltype),
    7271             :                        0);
    7272             : 
    7273        2446 :     InsertPgAttributeTuples(attrdesc, tupdesc, myrelid, NULL, NULL);
    7274             : 
    7275        2446 :     table_close(attrdesc, RowExclusiveLock);
    7276             : 
    7277             :     /*
    7278             :      * Update pg_class tuple as appropriate
    7279             :      */
    7280        2446 :     relform->relnatts = newattnum;
    7281             : 
    7282        2446 :     CatalogTupleUpdate(pgclass, &reltup->t_self, reltup);
    7283             : 
    7284        2446 :     heap_freetuple(reltup);
    7285             : 
    7286             :     /* Post creation hook for new attribute */
    7287        2446 :     InvokeObjectPostCreateHook(RelationRelationId, myrelid, newattnum);
    7288             : 
    7289        2446 :     table_close(pgclass, RowExclusiveLock);
    7290             : 
    7291             :     /* Make the attribute's catalog entry visible */
    7292        2446 :     CommandCounterIncrement();
    7293             : 
    7294             :     /*
    7295             :      * Store the DEFAULT, if any, in the catalogs
    7296             :      */
    7297        2446 :     if (colDef->raw_default)
    7298             :     {
    7299             :         RawColumnDefault *rawEnt;
    7300             : 
    7301         708 :         rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
    7302         708 :         rawEnt->attnum = attribute->attnum;
    7303         708 :         rawEnt->raw_default = copyObject(colDef->raw_default);
    7304             : 
    7305             :         /*
    7306             :          * Attempt to skip a complete table rewrite by storing the specified
    7307             :          * DEFAULT value outside of the heap.  This may be disabled inside
    7308             :          * AddRelationNewConstraints if the optimization cannot be applied.
    7309             :          */
    7310         708 :         rawEnt->missingMode = (!colDef->generated);
    7311             : 
    7312         708 :         rawEnt->generated = colDef->generated;
    7313             : 
    7314             :         /*
    7315             :          * This function is intended for CREATE TABLE, so it processes a
    7316             :          * _list_ of defaults, but we just do one.
    7317             :          */
    7318         708 :         AddRelationNewConstraints(rel, list_make1(rawEnt), NIL,
    7319             :                                   false, true, false, NULL);
    7320             : 
    7321             :         /* Make the additional catalog changes visible */
    7322         696 :         CommandCounterIncrement();
    7323             : 
    7324             :         /*
    7325             :          * Did the request for a missing value work? If not we'll have to do a
    7326             :          * rewrite
    7327             :          */
    7328         696 :         if (!rawEnt->missingMode)
    7329         108 :             tab->rewrite |= AT_REWRITE_DEFAULT_VAL;
    7330             :     }
    7331             : 
    7332             :     /*
    7333             :      * Tell Phase 3 to fill in the default expression, if there is one.
    7334             :      *
    7335             :      * If there is no default, Phase 3 doesn't have to do anything, because
    7336             :      * that effectively means that the default is NULL.  The heap tuple access
    7337             :      * routines always check for attnum > # of attributes in tuple, and return
    7338             :      * NULL if so, so without any modification of the tuple data we will get
    7339             :      * the effect of NULL values in the new column.
    7340             :      *
    7341             :      * An exception occurs when the new column is of a domain type: the domain
    7342             :      * might have a not-null constraint, or a check constraint that indirectly
    7343             :      * rejects nulls.  If there are any domain constraints then we construct
    7344             :      * an explicit NULL default value that will be passed through
    7345             :      * CoerceToDomain processing.  (This is a tad inefficient, since it causes
    7346             :      * rewriting the table which we really don't have to do, but the present
    7347             :      * design of domain processing doesn't offer any simple way of checking
    7348             :      * the constraints more directly.)
    7349             :      *
    7350             :      * Note: we use build_column_default, and not just the cooked default
    7351             :      * returned by AddRelationNewConstraints, so that the right thing happens
    7352             :      * when a datatype's default applies.
    7353             :      *
    7354             :      * Note: it might seem that this should happen at the end of Phase 2, so
    7355             :      * that the effects of subsequent subcommands can be taken into account.
    7356             :      * It's intentional that we do it now, though.  The new column should be
    7357             :      * filled according to what is said in the ADD COLUMN subcommand, so that
    7358             :      * the effects are the same as if this subcommand had been run by itself
    7359             :      * and the later subcommands had been issued in new ALTER TABLE commands.
    7360             :      *
    7361             :      * We can skip this entirely for relations without storage, since Phase 3
    7362             :      * is certainly not going to touch them.  System attributes don't have
    7363             :      * interesting defaults, either.
    7364             :      */
    7365        2434 :     if (RELKIND_HAS_STORAGE(relkind))
    7366             :     {
    7367             :         /*
    7368             :          * For an identity column, we can't use build_column_default(),
    7369             :          * because the sequence ownership isn't set yet.  So do it manually.
    7370             :          */
    7371        2082 :         if (colDef->identity)
    7372             :         {
    7373          42 :             NextValueExpr *nve = makeNode(NextValueExpr);
    7374             : 
    7375          42 :             nve->seqid = RangeVarGetRelid(colDef->identitySequence, NoLock, false);
    7376          42 :             nve->typeId = attribute->atttypid;
    7377             : 
    7378          42 :             defval = (Expr *) nve;
    7379             : 
    7380             :             /* must do a rewrite for identity columns */
    7381          42 :             tab->rewrite |= AT_REWRITE_DEFAULT_VAL;
    7382             :         }
    7383             :         else
    7384        2040 :             defval = (Expr *) build_column_default(rel, attribute->attnum);
    7385             : 
    7386        2082 :         if (!defval && DomainHasConstraints(attribute->atttypid))
    7387             :         {
    7388             :             Oid         baseTypeId;
    7389             :             int32       baseTypeMod;
    7390             :             Oid         baseTypeColl;
    7391             : 
    7392           6 :             baseTypeMod = attribute->atttypmod;
    7393           6 :             baseTypeId = getBaseTypeAndTypmod(attribute->atttypid, &baseTypeMod);
    7394           6 :             baseTypeColl = get_typcollation(baseTypeId);
    7395           6 :             defval = (Expr *) makeNullConst(baseTypeId, baseTypeMod, baseTypeColl);
    7396           6 :             defval = (Expr *) coerce_to_target_type(NULL,
    7397             :                                                     (Node *) defval,
    7398             :                                                     baseTypeId,
    7399             :                                                     attribute->atttypid,
    7400             :                                                     attribute->atttypmod,
    7401             :                                                     COERCION_ASSIGNMENT,
    7402             :                                                     COERCE_IMPLICIT_CAST,
    7403             :                                                     -1);
    7404           6 :             if (defval == NULL) /* should not happen */
    7405           0 :                 elog(ERROR, "failed to coerce base type to domain");
    7406             :         }
    7407             : 
    7408        2082 :         if (defval)
    7409             :         {
    7410             :             NewColumnValue *newval;
    7411             : 
    7412         610 :             newval = (NewColumnValue *) palloc0(sizeof(NewColumnValue));
    7413         610 :             newval->attnum = attribute->attnum;
    7414         610 :             newval->expr = expression_planner(defval);
    7415         610 :             newval->is_generated = (colDef->generated != '\0');
    7416             : 
    7417         610 :             tab->newvals = lappend(tab->newvals, newval);
    7418             :         }
    7419             : 
    7420        2082 :         if (DomainHasConstraints(attribute->atttypid))
    7421          12 :             tab->rewrite |= AT_REWRITE_DEFAULT_VAL;
    7422             : 
    7423        2082 :         if (!TupleDescAttr(rel->rd_att, attribute->attnum - 1)->atthasmissing)
    7424             :         {
    7425             :             /*
    7426             :              * If the new column is NOT NULL, and there is no missing value,
    7427             :              * tell Phase 3 it needs to check for NULLs.
    7428             :              */
    7429        1628 :             tab->verify_new_notnull |= colDef->is_not_null;
    7430             :         }
    7431             :     }
    7432             : 
    7433             :     /*
    7434             :      * Add needed dependency entries for the new column.
    7435             :      */
    7436        2434 :     add_column_datatype_dependency(myrelid, newattnum, attribute->atttypid);
    7437        2434 :     add_column_collation_dependency(myrelid, newattnum, attribute->attcollation);
    7438             : 
    7439             :     /*
    7440             :      * Propagate to children as appropriate.  Unlike most other ALTER
    7441             :      * routines, we have to do this one level of recursion at a time; we can't
    7442             :      * use find_all_inheritors to do it in one pass.
    7443             :      */
    7444             :     children =
    7445        2434 :         find_inheritance_children(RelationGetRelid(rel), lockmode);
    7446             : 
    7447             :     /*
    7448             :      * If we are told not to recurse, there had better not be any child
    7449             :      * tables; else the addition would put them out of step.
    7450             :      */
    7451        2434 :     if (children && !recurse)
    7452          12 :         ereport(ERROR,
    7453             :                 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    7454             :                  errmsg("column must be added to child tables too")));
    7455             : 
    7456             :     /* Children should see column as singly inherited */
    7457        2422 :     if (!recursing)
    7458             :     {
    7459        1822 :         childcmd = copyObject(*cmd);
    7460        1822 :         colDef = castNode(ColumnDef, childcmd->def);
    7461        1822 :         colDef->inhcount = 1;
    7462        1822 :         colDef->is_local = false;
    7463             :     }
    7464             :     else
    7465         600 :         childcmd = *cmd;        /* no need to copy again */
    7466             : 
    7467        3070 :     foreach(child, children)
    7468             :     {
    7469         648 :         Oid         childrelid = lfirst_oid(child);
    7470             :         Relation    childrel;
    7471             :         AlteredTableInfo *childtab;
    7472             : 
    7473             :         /* find_inheritance_children already got lock */
    7474         648 :         childrel = table_open(childrelid, NoLock);
    7475         648 :         CheckAlterTableIsSafe(childrel);
    7476             : 
    7477             :         /* Find or create work queue entry for this table */
    7478         648 :         childtab = ATGetQueueEntry(wqueue, childrel);
    7479             : 
    7480             :         /* Recurse to child; return value is ignored */
    7481         648 :         ATExecAddColumn(wqueue, childtab, childrel,
    7482             :                         &childcmd, recurse, true,
    7483             :                         lockmode, cur_pass, context);
    7484             : 
    7485         648 :         table_close(childrel, NoLock);
    7486             :     }
    7487             : 
    7488        2422 :     ObjectAddressSubSet(address, RelationRelationId, myrelid, newattnum);
    7489        2422 :     return address;
    7490             : }
    7491             : 
    7492             : /*
    7493             :  * If a new or renamed column will collide with the name of an existing
    7494             :  * column and if_not_exists is false then error out, else do nothing.
    7495             :  */
    7496             : static bool
    7497        3034 : check_for_column_name_collision(Relation rel, const char *colname,
    7498             :                                 bool if_not_exists)
    7499             : {
    7500             :     HeapTuple   attTuple;
    7501             :     int         attnum;
    7502             : 
    7503             :     /*
    7504             :      * this test is deliberately not attisdropped-aware, since if one tries to
    7505             :      * add a column matching a dropped column name, it's gonna fail anyway.
    7506             :      */
    7507        3034 :     attTuple = SearchSysCache2(ATTNAME,
    7508             :                                ObjectIdGetDatum(RelationGetRelid(rel)),
    7509             :                                PointerGetDatum(colname));
    7510        3034 :     if (!HeapTupleIsValid(attTuple))
    7511        2938 :         return true;
    7512             : 
    7513          96 :     attnum = ((Form_pg_attribute) GETSTRUCT(attTuple))->attnum;
    7514          96 :     ReleaseSysCache(attTuple);
    7515             : 
    7516             :     /*
    7517             :      * We throw a different error message for conflicts with system column
    7518             :      * names, since they are normally not shown and the user might otherwise
    7519             :      * be confused about the reason for the conflict.
    7520             :      */
    7521          96 :     if (attnum <= 0)
    7522          12 :         ereport(ERROR,
    7523             :                 (errcode(ERRCODE_DUPLICATE_COLUMN),
    7524             :                  errmsg("column name \"%s\" conflicts with a system column name",
    7525             :                         colname)));
    7526             :     else
    7527             :     {
    7528          84 :         if (if_not_exists)
    7529             :         {
    7530          54 :             ereport(NOTICE,
    7531             :                     (errcode(ERRCODE_DUPLICATE_COLUMN),
    7532             :                      errmsg("column \"%s\" of relation \"%s\" already exists, skipping",
    7533             :                             colname, RelationGetRelationName(rel))));
    7534          54 :             return false;
    7535             :         }
    7536             : 
    7537          30 :         ereport(ERROR,
    7538             :                 (errcode(ERRCODE_DUPLICATE_COLUMN),
    7539             :                  errmsg("column \"%s\" of relation \"%s\" already exists",
    7540             :                         colname, RelationGetRelationName(rel))));
    7541             :     }
    7542             : 
    7543             :     return true;
    7544             : }
    7545             : 
    7546             : /*
    7547             :  * Install a column's dependency on its datatype.
    7548             :  */
    7549             : static void
    7550        3374 : add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid)
    7551             : {
    7552             :     ObjectAddress myself,
    7553             :                 referenced;
    7554             : 
    7555        3374 :     myself.classId = RelationRelationId;
    7556        3374 :     myself.objectId = relid;
    7557        3374 :     myself.objectSubId = attnum;
    7558        3374 :     referenced.classId = TypeRelationId;
    7559        3374 :     referenced.objectId = typid;
    7560        3374 :     referenced.objectSubId = 0;
    7561        3374 :     recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
    7562        3374 : }
    7563             : 
    7564             : /*
    7565             :  * Install a column's dependency on its collation.
    7566             :  */
    7567             : static void
    7568        3374 : add_column_collation_dependency(Oid relid, int32 attnum, Oid collid)
    7569             : {
    7570             :     ObjectAddress myself,
    7571             :                 referenced;
    7572             : 
    7573             :     /* We know the default collation is pinned, so don't bother recording it */
    7574        3374 :     if (OidIsValid(collid) && collid != DEFAULT_COLLATION_OID)
    7575             :     {
    7576          18 :         myself.classId = RelationRelationId;
    7577          18 :         myself.objectId = relid;
    7578          18 :         myself.objectSubId = attnum;
    7579          18 :         referenced.classId = CollationRelationId;
    7580          18 :         referenced.objectId = collid;
    7581          18 :         referenced.objectSubId = 0;
    7582          18 :         recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
    7583             :     }
    7584        3374 : }
    7585             : 
    7586             : /*
    7587             :  * ALTER TABLE ALTER COLUMN DROP NOT NULL
    7588             :  *
    7589             :  * Return the address of the modified column.  If the column was already
    7590             :  * nullable, InvalidObjectAddress is returned.
    7591             :  */
    7592             : static ObjectAddress
    7593         256 : ATExecDropNotNull(Relation rel, const char *colName, bool recurse,
    7594             :                   LOCKMODE lockmode)
    7595             : {
    7596             :     HeapTuple   tuple;
    7597             :     HeapTuple   conTup;
    7598             :     Form_pg_attribute attTup;
    7599             :     AttrNumber  attnum;
    7600             :     Relation    attr_rel;
    7601             :     ObjectAddress address;
    7602             : 
    7603             :     /*
    7604             :      * lookup the attribute
    7605             :      */
    7606         256 :     attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
    7607             : 
    7608         256 :     tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
    7609         256 :     if (!HeapTupleIsValid(tuple))
    7610          18 :         ereport(ERROR,
    7611             :                 (errcode(ERRCODE_UNDEFINED_COLUMN),
    7612             :                  errmsg("column \"%s\" of relation \"%s\" does not exist",
    7613             :                         colName, RelationGetRelationName(rel))));
    7614         238 :     attTup = (Form_pg_attribute) GETSTRUCT(tuple);
    7615         238 :     attnum = attTup->attnum;
    7616         238 :     ObjectAddressSubSet(address, RelationRelationId,
    7617             :                         RelationGetRelid(rel), attnum);
    7618             : 
    7619             :     /* If the column is already nullable there's nothing to do. */
    7620         238 :     if (!attTup->attnotnull)
    7621             :     {
    7622           0 :         table_close(attr_rel, RowExclusiveLock);
    7623           0 :         return InvalidObjectAddress;
    7624             :     }
    7625             : 
    7626             :     /* Prevent them from altering a system attribute */
    7627         238 :     if (attnum <= 0)
    7628           0 :         ereport(ERROR,
    7629             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    7630             :                  errmsg("cannot alter system column \"%s\"",
    7631             :                         colName)));
    7632             : 
    7633         238 :     if (attTup->attidentity)
    7634          18 :         ereport(ERROR,
    7635             :                 (errcode(ERRCODE_SYNTAX_ERROR),
    7636             :                  errmsg("column \"%s\" of relation \"%s\" is an identity column",
    7637             :                         colName, RelationGetRelationName(rel))));
    7638             : 
    7639             :     /*
    7640             :      * If rel is partition, shouldn't drop NOT NULL if parent has the same.
    7641             :      */
    7642         220 :     if (rel->rd_rel->relispartition)
    7643             :     {
    7644          12 :         Oid         parentId = get_partition_parent(RelationGetRelid(rel), false);
    7645          12 :         Relation    parent = table_open(parentId, AccessShareLock);
    7646          12 :         TupleDesc   tupDesc = RelationGetDescr(parent);
    7647             :         AttrNumber  parent_attnum;
    7648             : 
    7649          12 :         parent_attnum = get_attnum(parentId, colName);
    7650          12 :         if (TupleDescAttr(tupDesc, parent_attnum - 1)->attnotnull)
    7651          12 :             ereport(ERROR,
    7652             :                     (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    7653             :                      errmsg("column \"%s\" is marked NOT NULL in parent table",
    7654             :                             colName)));
    7655           0 :         table_close(parent, AccessShareLock);
    7656             :     }
    7657             : 
    7658             :     /*
    7659             :      * Find the constraint that makes this column NOT NULL, and drop it.
    7660             :      * dropconstraint_internal() resets attnotnull.
    7661             :      */
    7662         208 :     conTup = findNotNullConstraintAttnum(RelationGetRelid(rel), attnum);
    7663         208 :     if (conTup == NULL)
    7664           0 :         elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation \"%s\"",
    7665             :              colName, RelationGetRelationName(rel));
    7666             : 
    7667             :     /* The normal case: we have a pg_constraint row, remove it */
    7668         208 :     dropconstraint_internal(rel, conTup, DROP_RESTRICT, recurse, false,
    7669             :                             false, lockmode);
    7670         154 :     heap_freetuple(conTup);
    7671             : 
    7672         154 :     InvokeObjectPostAlterHook(RelationRelationId,
    7673             :                               RelationGetRelid(rel), attnum);
    7674             : 
    7675         154 :     table_close(attr_rel, RowExclusiveLock);
    7676             : 
    7677         154 :     return address;
    7678             : }
    7679             : 
    7680             : /*
    7681             :  * Helper to set pg_attribute.attnotnull if it isn't set, and to tell phase 3
    7682             :  * to verify it.
    7683             :  *
    7684             :  * When called to alter an existing table, 'wqueue' must be given so that we
    7685             :  * can queue a check that existing tuples pass the constraint.  When called
    7686             :  * from table creation, 'wqueue' should be passed as NULL.
    7687             :  */
    7688             : static void
    7689       22884 : set_attnotnull(List **wqueue, Relation rel, AttrNumber attnum,
    7690             :                LOCKMODE lockmode)
    7691             : {
    7692             :     Form_pg_attribute attr;
    7693             : 
    7694       22884 :     CheckAlterTableIsSafe(rel);
    7695             : 
    7696             :     /*
    7697             :      * Exit quickly by testing attnotnull from the tupledesc's copy of the
    7698             :      * attribute.
    7699             :      */
    7700       22884 :     attr = TupleDescAttr(RelationGetDescr(rel), attnum - 1);
    7701       22884 :     if (attr->attisdropped)
    7702           0 :         return;
    7703             : 
    7704       22884 :     if (!attr->attnotnull)
    7705             :     {
    7706             :         Relation    attr_rel;
    7707             :         HeapTuple   tuple;
    7708             : 
    7709        1196 :         attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
    7710             : 
    7711        1196 :         tuple = SearchSysCacheCopyAttNum(RelationGetRelid(rel), attnum);
    7712        1196 :         if (!HeapTupleIsValid(tuple))
    7713           0 :             elog(ERROR, "cache lookup failed for attribute %d of relation %u",
    7714             :                  attnum, RelationGetRelid(rel));
    7715             : 
    7716        1196 :         attr = (Form_pg_attribute) GETSTRUCT(tuple);
    7717             :         Assert(!attr->attnotnull);
    7718        1196 :         attr->attnotnull = true;
    7719        1196 :         CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
    7720             : 
    7721             :         /*
    7722             :          * If the nullness isn't already proven by validated constraints, have
    7723             :          * ALTER TABLE phase 3 test for it.
    7724             :          */
    7725        1196 :         if (wqueue && !NotNullImpliedByRelConstraints(rel, attr))
    7726             :         {
    7727             :             AlteredTableInfo *tab;
    7728             : 
    7729        1098 :             tab = ATGetQueueEntry(wqueue, rel);
    7730        1098 :             tab->verify_new_notnull = true;
    7731             :         }
    7732             : 
    7733        1196 :         CommandCounterIncrement();
    7734             : 
    7735        1196 :         table_close(attr_rel, RowExclusiveLock);
    7736        1196 :         heap_freetuple(tuple);
    7737             :     }
    7738             : }
    7739             : 
    7740             : /*
    7741             :  * ALTER TABLE ALTER COLUMN SET NOT NULL
    7742             :  *
    7743             :  * Add a not-null constraint to a single table and its children.  Returns
    7744             :  * the address of the constraint added to the parent relation, if one gets
    7745             :  * added, or InvalidObjectAddress otherwise.
    7746             :  *
    7747             :  * We must recurse to child tables during execution, rather than using
    7748             :  * ALTER TABLE's normal prep-time recursion.
    7749             :  */
    7750             : static ObjectAddress
    7751         580 : ATExecSetNotNull(List **wqueue, Relation rel, char *conName, char *colName,
    7752             :                  bool recurse, bool recursing, LOCKMODE lockmode)
    7753             : {
    7754             :     HeapTuple   tuple;
    7755             :     AttrNumber  attnum;
    7756             :     ObjectAddress address;
    7757             :     Constraint *constraint;
    7758             :     CookedConstraint *ccon;
    7759             :     List       *cooked;
    7760         580 :     bool        is_no_inherit = false;
    7761             : 
    7762             :     /* Guard against stack overflow due to overly deep inheritance tree. */
    7763         580 :     check_stack_depth();
    7764             : 
    7765             :     /* At top level, permission check was done in ATPrepCmd, else do it */
    7766         580 :     if (recursing)
    7767             :     {
    7768         202 :         ATSimplePermissions(AT_AddConstraint, rel,
    7769             :                             ATT_PARTITIONED_TABLE | ATT_TABLE | ATT_FOREIGN_TABLE);
    7770             :         Assert(conName != NULL);
    7771             :     }
    7772             : 
    7773         580 :     attnum = get_attnum(RelationGetRelid(rel), colName);
    7774         580 :     if (attnum == InvalidAttrNumber)
    7775          18 :         ereport(ERROR,
    7776             :                 (errcode(ERRCODE_UNDEFINED_COLUMN),
    7777             :                  errmsg("column \"%s\" of relation \"%s\" does not exist",
    7778             :                         colName, RelationGetRelationName(rel))));
    7779             : 
    7780             :     /* Prevent them from altering a system attribute */
    7781         562 :     if (attnum <= 0)
    7782           0 :         ereport(ERROR,
    7783             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    7784             :                  errmsg("cannot alter system column \"%s\"",
    7785             :                         colName)));
    7786             : 
    7787             :     /* See if there's already a constraint */
    7788         562 :     tuple = findNotNullConstraintAttnum(RelationGetRelid(rel), attnum);
    7789         562 :     if (HeapTupleIsValid(tuple))
    7790             :     {
    7791          92 :         Form_pg_constraint conForm = (Form_pg_constraint) GETSTRUCT(tuple);
    7792          92 :         bool        changed = false;
    7793             : 
    7794             :         /*
    7795             :          * Don't let a NO INHERIT constraint be changed into inherit.
    7796             :          */
    7797          92 :         if (conForm->connoinherit && recurse)
    7798           6 :             ereport(ERROR,
    7799             :                     errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    7800             :                     errmsg("cannot change NO INHERIT status of NOT NULL constraint \"%s\" on relation \"%s\"",
    7801             :                            NameStr(conForm->conname),
    7802             :                            RelationGetRelationName(rel)));
    7803             : 
    7804             :         /*
    7805             :          * If we find an appropriate constraint, we're almost done, but just
    7806             :          * need to change some properties on it: if we're recursing, increment
    7807             :          * coninhcount; if not, set conislocal if not already set.
    7808             :          */
    7809          86 :         if (recursing)
    7810             :         {
    7811          66 :             if (pg_add_s16_overflow(conForm->coninhcount, 1,
    7812             :                                     &conForm->coninhcount))
    7813           0 :                 ereport(ERROR,
    7814             :                         errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
    7815             :                         errmsg("too many inheritance parents"));
    7816          66 :             changed = true;
    7817             :         }
    7818          20 :         else if (!conForm->conislocal)
    7819             :         {
    7820           0 :             conForm->conislocal = true;
    7821           0 :             changed = true;
    7822             :         }
    7823             : 
    7824          86 :         if (changed)
    7825             :         {
    7826             :             Relation    constr_rel;
    7827             : 
    7828          66 :             constr_rel = table_open(ConstraintRelationId, RowExclusiveLock);
    7829             : 
    7830          66 :             CatalogTupleUpdate(constr_rel, &tuple->t_self, tuple);
    7831          66 :             ObjectAddressSet(address, ConstraintRelationId, conForm->oid);
    7832          66 :             table_close(constr_rel, RowExclusiveLock);
    7833             :         }
    7834             : 
    7835          86 :         if (changed)
    7836          66 :             return address;
    7837             :         else
    7838          20 :             return InvalidObjectAddress;
    7839             :     }
    7840             : 
    7841             :     /*
    7842             :      * If we're asked not to recurse, and children exist, raise an error for
    7843             :      * partitioned tables.  For inheritance, we act as if NO INHERIT had been
    7844             :      * specified.
    7845             :      */
    7846         494 :     if (!recurse &&
    7847          24 :         find_inheritance_children(RelationGetRelid(rel),
    7848             :                                   NoLock) != NIL)
    7849             :     {
    7850          18 :         if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
    7851           6 :             ereport(ERROR,
    7852             :                     errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    7853             :                     errmsg("constraint must be added to child tables too"),
    7854             :                     errhint("Do not specify the ONLY keyword."));
    7855             :         else
    7856          12 :             is_no_inherit = true;
    7857             :     }
    7858             : 
    7859             :     /*
    7860             :      * No constraint exists; we must add one.  First determine a name to use,
    7861             :      * if we haven't already.
    7862             :      */
    7863         464 :     if (!recursing)
    7864             :     {
    7865             :         Assert(conName == NULL);
    7866         328 :         conName = ChooseConstraintName(RelationGetRelationName(rel),
    7867             :                                        colName, "not_null",
    7868         328 :                                        RelationGetNamespace(rel),
    7869             :                                        NIL);
    7870             :     }
    7871             : 
    7872         464 :     constraint = makeNotNullConstraint(makeString(colName));
    7873         464 :     constraint->is_no_inherit = is_no_inherit;
    7874         464 :     constraint->conname = conName;
    7875             : 
    7876             :     /* and do it */
    7877         464 :     cooked = AddRelationNewConstraints(rel, NIL, list_make1(constraint),
    7878         464 :                                        false, !recursing, false, NULL);
    7879         464 :     ccon = linitial(cooked);
    7880         464 :     ObjectAddressSet(address, ConstraintRelationId, ccon->conoid);
    7881             : 
    7882         464 :     InvokeObjectPostAlterHook(RelationRelationId,
    7883             :                               RelationGetRelid(rel), attnum);
    7884             : 
    7885             :     /* Mark pg_attribute.attnotnull for the column */
    7886         464 :     set_attnotnull(wqueue, rel, attnum, lockmode);
    7887             : 
    7888             :     /*
    7889             :      * Recurse to propagate the constraint to children that don't have one.
    7890             :      */
    7891         464 :     if (recurse)
    7892             :     {
    7893             :         List       *children;
    7894             : 
    7895         446 :         children = find_inheritance_children(RelationGetRelid(rel),
    7896             :                                              lockmode);
    7897             : 
    7898        1094 :         foreach_oid(childoid, children)
    7899             :         {
    7900         202 :             Relation    childrel = table_open(childoid, NoLock);
    7901             : 
    7902         202 :             CommandCounterIncrement();
    7903             : 
    7904         202 :             ATExecSetNotNull(wqueue, childrel, conName, colName,
    7905             :                              recurse, true, lockmode);
    7906         202 :             table_close(childrel, NoLock);
    7907             :         }
    7908             :     }
    7909             : 
    7910         464 :     return address;
    7911             : }
    7912             : 
    7913             : /*
    7914             :  * NotNullImpliedByRelConstraints
    7915             :  *      Does rel's existing constraints imply NOT NULL for the given attribute?
    7916             :  */
    7917             : static bool
    7918        1148 : NotNullImpliedByRelConstraints(Relation rel, Form_pg_attribute attr)
    7919             : {
    7920        1148 :     NullTest   *nnulltest = makeNode(NullTest);
    7921             : 
    7922        2296 :     nnulltest->arg = (Expr *) makeVar(1,
    7923        1148 :                                       attr->attnum,
    7924             :                                       attr->atttypid,
    7925             :                                       attr->atttypmod,
    7926             :                                       attr->attcollation,
    7927             :                                       0);
    7928        1148 :     nnulltest->nulltesttype = IS_NOT_NULL;
    7929             : 
    7930             :     /*
    7931             :      * argisrow = false is correct even for a composite column, because
    7932             :      * attnotnull does not represent a SQL-spec IS NOT NULL test in such a
    7933             :      * case, just IS DISTINCT FROM NULL.
    7934             :      */
    7935        1148 :     nnulltest->argisrow = false;
    7936        1148 :     nnulltest->location = -1;
    7937             : 
    7938        1148 :     if (ConstraintImpliedByRelConstraint(rel, list_make1(nnulltest), NIL))
    7939             :     {
    7940          50 :         ereport(DEBUG1,
    7941             :                 (errmsg_internal("existing constraints on column \"%s.%s\" are sufficient to prove that it does not contain nulls",
    7942             :                                  RelationGetRelationName(rel), NameStr(attr->attname))));
    7943          50 :         return true;
    7944             :     }
    7945             : 
    7946        1098 :     return false;
    7947             : }
    7948             : 
    7949             : /*
    7950             :  * ALTER TABLE ALTER COLUMN SET/DROP DEFAULT
    7951             :  *
    7952             :  * Return the address of the affected column.
    7953             :  */
    7954             : static ObjectAddress
    7955         562 : ATExecColumnDefault(Relation rel, const char *colName,
    7956             :                     Node *newDefault, LOCKMODE lockmode)
    7957             : {
    7958         562 :     TupleDesc   tupdesc = RelationGetDescr(rel);
    7959             :     AttrNumber  attnum;
    7960             :     ObjectAddress address;
    7961             : 
    7962             :     /*
    7963             :      * get the number of the attribute
    7964             :      */
    7965         562 :     attnum = get_attnum(RelationGetRelid(rel), colName);
    7966         562 :     if (attnum == InvalidAttrNumber)
    7967          30 :         ereport(ERROR,
    7968             :                 (errcode(ERRCODE_UNDEFINED_COLUMN),
    7969             :                  errmsg("column \"%s\" of relation \"%s\" does not exist",
    7970             :                         colName, RelationGetRelationName(rel))));
    7971             : 
    7972             :     /* Prevent them from altering a system attribute */
    7973         532 :     if (attnum <= 0)
    7974           0 :         ereport(ERROR,
    7975             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    7976             :                  errmsg("cannot alter system column \"%s\"",
    7977             :                         colName)));
    7978             : 
    7979         532 :     if (TupleDescAttr(tupdesc, attnum - 1)->attidentity)
    7980          18 :         ereport(ERROR,
    7981             :                 (errcode(ERRCODE_SYNTAX_ERROR),
    7982             :                  errmsg("column \"%s\" of relation \"%s\" is an identity column",
    7983             :                         colName, RelationGetRelationName(rel)),
    7984             :         /* translator: %s is an SQL ALTER command */
    7985             :                  newDefault ? 0 : errhint("Use %s instead.",
    7986             :                                           "ALTER TABLE ... ALTER COLUMN ... DROP IDENTITY")));
    7987             : 
    7988         514 :     if (TupleDescAttr(tupdesc, attnum - 1)->attgenerated)
    7989           6 :         ereport(ERROR,
    7990             :                 (errcode(ERRCODE_SYNTAX_ERROR),
    7991             :                  errmsg("column \"%s\" of relation \"%s\" is a generated column",
    7992             :                         colName, RelationGetRelationName(rel)),
    7993             :                  newDefault ?
    7994             :         /* translator: %s is an SQL ALTER command */
    7995             :                  errhint("Use %s instead.", "ALTER TABLE ... ALTER COLUMN ... SET EXPRESSION") :
    7996             :                  (TupleDescAttr(tupdesc, attnum - 1)->attgenerated == ATTRIBUTE_GENERATED_STORED ?
    7997             :                   errhint("Use %s instead.", "ALTER TABLE ... ALTER COLUMN ... DROP EXPRESSION") : 0)));
    7998             : 
    7999             :     /*
    8000             :      * Remove any old default for the column.  We use RESTRICT here for
    8001             :      * safety, but at present we do not expect anything to depend on the
    8002             :      * default.
    8003             :      *
    8004             :      * We treat removing the existing default as an internal operation when it
    8005             :      * is preparatory to adding a new default, but as a user-initiated
    8006             :      * operation when the user asked for a drop.
    8007             :      */
    8008         508 :     RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT, false,
    8009             :                       newDefault != NULL);
    8010             : 
    8011         508 :     if (newDefault)
    8012             :     {
    8013             :         /* SET DEFAULT */
    8014             :         RawColumnDefault *rawEnt;
    8015             : 
    8016         334 :         rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
    8017         334 :         rawEnt->attnum = attnum;
    8018         334 :         rawEnt->raw_default = newDefault;
    8019         334 :         rawEnt->missingMode = false;
    8020         334 :         rawEnt->generated = '\0';
    8021             : 
    8022             :         /*
    8023             :          * This function is intended for CREATE TABLE, so it processes a
    8024             :          * _list_ of defaults, but we just do one.
    8025             :          */
    8026         334 :         AddRelationNewConstraints(rel, list_make1(rawEnt), NIL,
    8027             :                                   false, true, false, NULL);
    8028             :     }
    8029             : 
    8030         502 :     ObjectAddressSubSet(address, RelationRelationId,
    8031             :                         RelationGetRelid(rel), attnum);
    8032         502 :     return address;
    8033             : }
    8034             : 
    8035             : /*
    8036             :  * Add a pre-cooked default expression.
    8037             :  *
    8038             :  * Return the address of the affected column.
    8039             :  */
    8040             : static ObjectAddress
    8041          56 : ATExecCookedColumnDefault(Relation rel, AttrNumber attnum,
    8042             :                           Node *newDefault)
    8043             : {
    8044             :     ObjectAddress address;
    8045             : 
    8046             :     /* We assume no checking is required */
    8047             : 
    8048             :     /*
    8049             :      * Remove any old default for the column.  We use RESTRICT here for
    8050             :      * safety, but at present we do not expect anything to depend on the
    8051             :      * default.  (In ordinary cases, there could not be a default in place
    8052             :      * anyway, but it's possible when combining LIKE with inheritance.)
    8053             :      */
    8054          56 :     RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT, false,
    8055             :                       true);
    8056             : 
    8057          56 :     (void) StoreAttrDefault(rel, attnum, newDefault, true, false);
    8058             : 
    8059          56 :     ObjectAddressSubSet(address, RelationRelationId,
    8060             :                         RelationGetRelid(rel), attnum);
    8061          56 :     return address;
    8062             : }
    8063             : 
    8064             : /*
    8065             :  * ALTER TABLE ALTER COLUMN ADD IDENTITY
    8066             :  *
    8067             :  * Return the address of the affected column.
    8068             :  */
    8069             : static ObjectAddress
    8070         160 : ATExecAddIdentity(Relation rel, const char *colName,
    8071             :                   Node *def, LOCKMODE lockmode, bool recurse, bool recursing)
    8072             : {
    8073             :     Relation    attrelation;
    8074             :     HeapTuple   tuple;
    8075             :     Form_pg_attribute attTup;
    8076             :     AttrNumber  attnum;
    8077             :     ObjectAddress address;
    8078         160 :     ColumnDef  *cdef = castNode(ColumnDef, def);
    8079             :     bool        ispartitioned;
    8080             : 
    8081         160 :     ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
    8082         160 :     if (ispartitioned && !recurse)
    8083           6 :         ereport(ERROR,
    8084             :                 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    8085             :                  errmsg("cannot add identity to a column of only the partitioned table"),
    8086             :                  errhint("Do not specify the ONLY keyword.")));
    8087             : 
    8088         154 :     if (rel->rd_rel->relispartition && !recursing)
    8089          12 :         ereport(ERROR,
    8090             :                 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    8091             :                 errmsg("cannot add identity to a column of a partition"));
    8092             : 
    8093         142 :     attrelation = table_open(AttributeRelationId, RowExclusiveLock);
    8094             : 
    8095         142 :     tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
    8096         142 :     if (!HeapTupleIsValid(tuple))
    8097           0 :         ereport(ERROR,
    8098             :                 (errcode(ERRCODE_UNDEFINED_COLUMN),
    8099             :                  errmsg("column \"%s\" of relation \"%s\" does not exist",
    8100             :                         colName, RelationGetRelationName(rel))));
    8101         142 :     attTup = (Form_pg_attribute) GETSTRUCT(tuple);
    8102         142 :     attnum = attTup->attnum;
    8103             : 
    8104             :     /* Can't alter a system attribute */
    8105         142 :     if (attnum <= 0)
    8106           0 :         ereport(ERROR,
    8107             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    8108             :                  errmsg("cannot alter system column \"%s\"",
    8109             :                         colName)));
    8110             : 
    8111             :     /*
    8112             :      * Creating a column as identity implies NOT NULL, so adding the identity
    8113             :      * to an existing column that is not NOT NULL would create a state that
    8114             :      * cannot be reproduced without contortions.
    8115             :      */
    8116         142 :     if (!attTup->attnotnull)
    8117           6 :         ereport(ERROR,
    8118             :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    8119             :                  errmsg("column \"%s\" of relation \"%s\" must be declared NOT NULL before identity can be added",
    8120             :                         colName, RelationGetRelationName(rel))));
    8121             : 
    8122         136 :     if (attTup->attidentity)
    8123          18 :         ereport(ERROR,
    8124             :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    8125             :                  errmsg("column \"%s\" of relation \"%s\" is already an identity column",
    8126             :                         colName, RelationGetRelationName(rel))));
    8127             : 
    8128         118 :     if (attTup->atthasdef)
    8129           6 :         ereport(ERROR,
    8130             :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    8131             :                  errmsg("column \"%s\" of relation \"%s\" already has a default value",
    8132             :                         colName, RelationGetRelationName(rel))));
    8133             : 
    8134         112 :     attTup->attidentity = cdef->identity;
    8135         112 :     CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
    8136             : 
    8137         112 :     InvokeObjectPostAlterHook(RelationRelationId,
    8138             :                               RelationGetRelid(rel),
    8139             :                               attTup->attnum);
    8140         112 :     ObjectAddressSubSet(address, RelationRelationId,
    8141             :                         RelationGetRelid(rel), attnum);
    8142         112 :     heap_freetuple(tuple);
    8143             : 
    8144         112 :     table_close(attrelation, RowExclusiveLock);
    8145             : 
    8146             :     /*
    8147             :      * Recurse to propagate the identity column to partitions.  Identity is
    8148             :      * not inherited in regular inheritance children.
    8149             :      */
    8150         112 :     if (recurse && ispartitioned)
    8151             :     {
    8152             :         List       *children;
    8153             :         ListCell   *lc;
    8154             : 
    8155          10 :         children = find_inheritance_children(RelationGetRelid(rel), lockmode);
    8156             : 
    8157          16 :         foreach(lc, children)
    8158             :         {
    8159             :             Relation    childrel;
    8160             : 
    8161           6 :             childrel = table_open(lfirst_oid(lc), NoLock);
    8162           6 :             ATExecAddIdentity(childrel, colName, def, lockmode, recurse, true);
    8163           6 :             table_close(childrel, NoLock);
    8164             :         }
    8165             :     }
    8166             : 
    8167         112 :     return address;
    8168             : }
    8169             : 
    8170             : /*
    8171             :  * ALTER TABLE ALTER COLUMN SET { GENERATED or sequence options }
    8172             :  *
    8173             :  * Return the address of the affected column.
    8174             :  */
    8175             : static ObjectAddress
    8176          74 : ATExecSetIdentity(Relation rel, const char *colName, Node *def,
    8177             :                   LOCKMODE lockmode, bool recurse, bool recursing)
    8178             : {
    8179             :     ListCell   *option;
    8180          74 :     DefElem    *generatedEl = NULL;
    8181             :     HeapTuple   tuple;
    8182             :     Form_pg_attribute attTup;
    8183             :     AttrNumber  attnum;
    8184             :     Relation    attrelation;
    8185             :     ObjectAddress address;
    8186             :     bool        ispartitioned;
    8187             : 
    8188          74 :     ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
    8189          74 :     if (ispartitioned && !recurse)
    8190           6 :         ereport(ERROR,
    8191             :                 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    8192             :                  errmsg("cannot change identity column of only the partitioned table"),
    8193             :                  errhint("Do not specify the ONLY keyword.")));
    8194             : 
    8195          68 :     if (rel->rd_rel->relispartition && !recursing)
    8196          12 :         ereport(ERROR,
    8197             :                 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    8198             :                 errmsg("cannot change identity column of a partition"));
    8199             : 
    8200         100 :     foreach(option, castNode(List, def))
    8201             :     {
    8202          44 :         DefElem    *defel = lfirst_node(DefElem, option);
    8203             : 
    8204          44 :         if (strcmp(defel->defname, "generated") == 0)
    8205             :         {
    8206          44 :             if (generatedEl)
    8207           0 :                 ereport(ERROR,
    8208             :                         (errcode(ERRCODE_SYNTAX_ERROR),
    8209             :                          errmsg("conflicting or redundant options")));
    8210          44 :             generatedEl = defel;
    8211             :         }
    8212             :         else
    8213           0 :             elog(ERROR, "option \"%s\" not recognized",
    8214             :                  defel->defname);
    8215             :     }
    8216             : 
    8217             :     /*
    8218             :      * Even if there is nothing to change here, we run all the checks.  There
    8219             :      * will be a subsequent ALTER SEQUENCE that relies on everything being
    8220             :      * there.
    8221             :      */
    8222             : 
    8223          56 :     attrelation = table_open(AttributeRelationId, RowExclusiveLock);
    8224          56 :     tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
    8225          56 :     if (!HeapTupleIsValid(tuple))
    8226           0 :         ereport(ERROR,
    8227             :                 (errcode(ERRCODE_UNDEFINED_COLUMN),
    8228             :                  errmsg("column \"%s\" of relation \"%s\" does not exist",
    8229             :                         colName, RelationGetRelationName(rel))));
    8230             : 
    8231          56 :     attTup = (Form_pg_attribute) GETSTRUCT(tuple);
    8232          56 :     attnum = attTup->attnum;
    8233             : 
    8234          56 :     if (attnum <= 0)
    8235           0 :         ereport(ERROR,
    8236             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    8237             :                  errmsg("cannot alter system column \"%s\"",
    8238             :                         colName)));
    8239             : 
    8240          56 :     if (!attTup->attidentity)
    8241           6 :         ereport(ERROR,
    8242             :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    8243             :                  errmsg("column \"%s\" of relation \"%s\" is not an identity column",
    8244             :                         colName, RelationGetRelationName(rel))));
    8245             : 
    8246          50 :     if (generatedEl)
    8247             :     {
    8248          44 :         attTup->attidentity = defGetInt32(generatedEl);
    8249          44 :         CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
    8250             : 
    8251          44 :         InvokeObjectPostAlterHook(RelationRelationId,
    8252             :                                   RelationGetRelid(rel),
    8253             :                                   attTup->attnum);
    8254          44 :         ObjectAddressSubSet(address, RelationRelationId,
    8255             :                             RelationGetRelid(rel), attnum);
    8256             :     }
    8257             :     else
    8258           6 :         address = InvalidObjectAddress;
    8259             : 
    8260          50 :     heap_freetuple(tuple);
    8261          50 :     table_close(attrelation, RowExclusiveLock);
    8262             : 
    8263             :     /*
    8264             :      * Recurse to propagate the identity change to partitions. Identity is not
    8265             :      * inherited in regular inheritance children.
    8266             :      */
    8267          50 :     if (generatedEl && recurse && ispartitioned)
    8268             :     {
    8269             :         List       *children;
    8270             :         ListCell   *lc;
    8271             : 
    8272           6 :         children = find_inheritance_children(RelationGetRelid(rel), lockmode);
    8273             : 
    8274          18 :         foreach(lc, children)
    8275             :         {
    8276             :             Relation    childrel;
    8277             : 
    8278          12 :             childrel = table_open(lfirst_oid(lc), NoLock);
    8279          12 :             ATExecSetIdentity(childrel, colName, def, lockmode, recurse, true);
    8280          12 :             table_close(childrel, NoLock);
    8281             :         }
    8282             :     }
    8283             : 
    8284          50 :     return address;
    8285             : }
    8286             : 
    8287             : /*
    8288             :  * ALTER TABLE ALTER COLUMN DROP IDENTITY
    8289             :  *
    8290             :  * Return the address of the affected column.
    8291             :  */
    8292             : static ObjectAddress
    8293          68 : ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode,
    8294             :                    bool recurse, bool recursing)
    8295             : {
    8296             :     HeapTuple   tuple;
    8297             :     Form_pg_attribute attTup;
    8298             :     AttrNumber  attnum;
    8299             :     Relation    attrelation;
    8300             :     ObjectAddress address;
    8301             :     Oid         seqid;
    8302             :     ObjectAddress seqaddress;
    8303             :     bool        ispartitioned;
    8304             : 
    8305          68 :     ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
    8306          68 :     if (ispartitioned && !recurse)
    8307           6 :         ereport(ERROR,
    8308             :                 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    8309             :                  errmsg("cannot drop identity from a column of only the partitioned table"),
    8310             :                  errhint("Do not specify the ONLY keyword.")));
    8311             : 
    8312          62 :     if (rel->rd_rel->relispartition && !recursing)
    8313           6 :         ereport(ERROR,
    8314             :                 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    8315             :                 errmsg("cannot drop identity from a column of a partition"));
    8316             : 
    8317          56 :     attrelation = table_open(AttributeRelationId, RowExclusiveLock);
    8318          56 :     tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
    8319          56 :     if (!HeapTupleIsValid(tuple))
    8320           0 :         ereport(ERROR,
    8321             :                 (errcode(ERRCODE_UNDEFINED_COLUMN),
    8322             :                  errmsg("column \"%s\" of relation \"%s\" does not exist",
    8323             :                         colName, RelationGetRelationName(rel))));
    8324             : 
    8325          56 :     attTup = (Form_pg_attribute) GETSTRUCT(tuple);
    8326          56 :     attnum = attTup->attnum;
    8327             : 
    8328          56 :     if (attnum <= 0)
    8329           0 :         ereport(ERROR,
    8330             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    8331             :                  errmsg("cannot alter system column \"%s\"",
    8332             :                         colName)));
    8333             : 
    8334          56 :     if (!attTup->attidentity)
    8335             :     {
    8336          12 :         if (!missing_ok)
    8337           6 :             ereport(ERROR,
    8338             :                     (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    8339             :                      errmsg("column \"%s\" of relation \"%s\" is not an identity column",
    8340             :                             colName, RelationGetRelationName(rel))));
    8341             :         else
    8342             :         {
    8343           6 :             ereport(NOTICE,
    8344             :                     (errmsg("column \"%s\" of relation \"%s\" is not an identity column, skipping",
    8345             :                             colName, RelationGetRelationName(rel))));
    8346           6 :             heap_freetuple(tuple);
    8347           6 :             table_close(attrelation, RowExclusiveLock);
    8348           6 :             return InvalidObjectAddress;
    8349             :         }
    8350             :     }
    8351             : 
    8352          44 :     attTup->attidentity = '\0';
    8353          44 :     CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
    8354             : 
    8355          44 :     InvokeObjectPostAlterHook(RelationRelationId,
    8356             :                               RelationGetRelid(rel),
    8357             :                               attTup->attnum);
    8358          44 :     ObjectAddressSubSet(address, RelationRelationId,
    8359             :                         RelationGetRelid(rel), attnum);
    8360          44 :     heap_freetuple(tuple);
    8361             : 
    8362          44 :     table_close(attrelation, RowExclusiveLock);
    8363             : 
    8364             :     /*
    8365             :      * Recurse to drop the identity from column in partitions.  Identity is
    8366             :      * not inherited in regular inheritance children so ignore them.
    8367             :      */
    8368          44 :     if (recurse && ispartitioned)
    8369             :     {
    8370             :         List       *children;
    8371             :         ListCell   *lc;
    8372             : 
    8373           6 :         children = find_inheritance_children(RelationGetRelid(rel), lockmode);
    8374             : 
    8375          12 :         foreach(lc, children)
    8376             :         {
    8377             :             Relation    childrel;
    8378             : 
    8379           6 :             childrel = table_open(lfirst_oid(lc), NoLock);
    8380           6 :             ATExecDropIdentity(childrel, colName, false, lockmode, recurse, true);
    8381           6 :             table_close(childrel, NoLock);
    8382             :         }
    8383             :     }
    8384             : 
    8385          44 :     if (!recursing)
    8386             :     {
    8387             :         /* drop the internal sequence */
    8388          32 :         seqid = getIdentitySequence(rel, attnum, false);
    8389          32 :         deleteDependencyRecordsForClass(RelationRelationId, seqid,
    8390             :                                         RelationRelationId, DEPENDENCY_INTERNAL);
    8391          32 :         CommandCounterIncrement();
    8392          32 :         seqaddress.classId = RelationRelationId;
    8393          32 :         seqaddress.objectId = seqid;
    8394          32 :         seqaddress.objectSubId = 0;
    8395          32 :         performDeletion(&seqaddress, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
    8396             :     }
    8397             : 
    8398          44 :     return address;
    8399             : }
    8400             : 
    8401             : /*
    8402             :  * ALTER TABLE ALTER COLUMN SET EXPRESSION
    8403             :  *
    8404             :  * Return the address of the affected column.
    8405             :  */
    8406             : static ObjectAddress
    8407          84 : ATExecSetExpression(AlteredTableInfo *tab, Relation rel, const char *colName,
    8408             :                     Node *newExpr, LOCKMODE lockmode)
    8409             : {
    8410             :     HeapTuple   tuple;
    8411             :     Form_pg_attribute attTup;
    8412             :     AttrNumber  attnum;
    8413             :     Oid         attrdefoid;
    8414             :     ObjectAddress address;
    8415             :     Expr       *defval;
    8416             :     NewColumnValue *newval;
    8417             :     RawColumnDefault *rawEnt;
    8418             : 
    8419          84 :     tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
    8420          84 :     if (!HeapTupleIsValid(tuple))
    8421           0 :         ereport(ERROR,
    8422             :                 (errcode(ERRCODE_UNDEFINED_COLUMN),
    8423             :                  errmsg("column \"%s\" of relation \"%s\" does not exist",
    8424             :                         colName, RelationGetRelationName(rel))));
    8425             : 
    8426          84 :     attTup = (Form_pg_attribute) GETSTRUCT(tuple);
    8427          84 :     attnum = attTup->attnum;
    8428             : 
    8429          84 :     if (attnum <= 0)
    8430           0 :         ereport(ERROR,
    8431             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    8432             :                  errmsg("cannot alter system column \"%s\"",
    8433             :                         colName)));
    8434             : 
    8435          84 :     if (attTup->attgenerated != ATTRIBUTE_GENERATED_STORED)
    8436           6 :         ereport(ERROR,
    8437             :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    8438             :                  errmsg("column \"%s\" of relation \"%s\" is not a generated column",
    8439             :                         colName, RelationGetRelationName(rel))));
    8440          78 :     ReleaseSysCache(tuple);
    8441             : 
    8442             :     /*
    8443             :      * Clear all the missing values if we're rewriting the table, since this
    8444             :      * renders them pointless.
    8445             :      */
    8446          78 :     RelationClearMissing(rel);
    8447             : 
    8448             :     /* make sure we don't conflict with later attribute modifications */
    8449          78 :     CommandCounterIncrement();
    8450             : 
    8451             :     /*
    8452             :      * Find everything that depends on the column (constraints, indexes, etc),
    8453             :      * and record enough information to let us recreate the objects after
    8454             :      * rewrite.
    8455             :      */
    8456          78 :     RememberAllDependentForRebuilding(tab, AT_SetExpression, rel, attnum, colName);
    8457             : 
    8458             :     /*
    8459             :      * Drop the dependency records of the GENERATED expression, in particular
    8460             :      * its INTERNAL dependency on the column, which would otherwise cause
    8461             :      * dependency.c to refuse to perform the deletion.
    8462             :      */
    8463          78 :     attrdefoid = GetAttrDefaultOid(RelationGetRelid(rel), attnum);
    8464          78 :     if (!OidIsValid(attrdefoid))
    8465           0 :         elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
    8466             :              RelationGetRelid(rel), attnum);
    8467          78 :     (void) deleteDependencyRecordsFor(AttrDefaultRelationId, attrdefoid, false);
    8468             : 
    8469             :     /* Make above changes visible */
    8470          78 :     CommandCounterIncrement();
    8471             : 
    8472             :     /*
    8473             :      * Get rid of the GENERATED expression itself.  We use RESTRICT here for
    8474             :      * safety, but at present we do not expect anything to depend on the
    8475             :      * expression.
    8476             :      */
    8477          78 :     RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT,
    8478             :                       false, false);
    8479             : 
    8480             :     /* Prepare to store the new expression, in the catalogs */
    8481          78 :     rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
    8482          78 :     rawEnt->attnum = attnum;
    8483          78 :     rawEnt->raw_default = newExpr;
    8484          78 :     rawEnt->missingMode = false;
    8485          78 :     rawEnt->generated = ATTRIBUTE_GENERATED_STORED;
    8486             : 
    8487             :     /* Store the generated expression */
    8488          78 :     AddRelationNewConstraints(rel, list_make1(rawEnt), NIL,
    8489             :                               false, true, false, NULL);
    8490             : 
    8491             :     /* Make above new expression visible */
    8492          78 :     CommandCounterIncrement();
    8493             : 
    8494             :     /* Prepare for table rewrite */
    8495          78 :     defval = (Expr *) build_column_default(rel, attnum);
    8496             : 
    8497          78 :     newval = (NewColumnValue *) palloc0(sizeof(NewColumnValue));
    8498          78 :     newval->attnum = attnum;
    8499          78 :     newval->expr = expression_planner(defval);
    8500          78 :     newval->is_generated = true;
    8501             : 
    8502          78 :     tab->newvals = lappend(tab->newvals, newval);
    8503          78 :     tab->rewrite |= AT_REWRITE_DEFAULT_VAL;
    8504             : 
    8505             :     /* Drop any pg_statistic entry for the column */
    8506          78 :     RemoveStatistics(RelationGetRelid(rel), attnum);
    8507             : 
    8508          78 :     InvokeObjectPostAlterHook(RelationRelationId,
    8509             :                               RelationGetRelid(rel), attnum);
    8510             : 
    8511          78 :     ObjectAddressSubSet(address, RelationRelationId,
    8512             :                         RelationGetRelid(rel), attnum);
    8513          78 :     return address;
    8514             : }
    8515             : 
    8516             : /*
    8517             :  * ALTER TABLE ALTER COLUMN DROP EXPRESSION
    8518             :  */
    8519             : static void
    8520          44 : ATPrepDropExpression(Relation rel, AlterTableCmd *cmd, bool recurse, bool recursing, LOCKMODE lockmode)
    8521             : {
    8522             :     /*
    8523             :      * Reject ONLY if there are child tables.  We could implement this, but it
    8524             :      * is a bit complicated.  GENERATED clauses must be attached to the column
    8525             :      * definition and cannot be added later like DEFAULT, so if a child table
    8526             :      * has a generation expression that the parent does not have, the child
    8527             :      * column will necessarily be an attislocal column.  So to implement ONLY
    8528             :      * here, we'd need extra code to update attislocal of the direct child
    8529             :      * tables, somewhat similar to how DROP COLUMN does it, so that the
    8530             :      * resulting state can be properly dumped and restored.
    8531             :      */
    8532          56 :     if (!recurse &&
    8533          12 :         find_inheritance_children(RelationGetRelid(rel), lockmode))
    8534           6 :         ereport(ERROR,
    8535             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    8536             :                  errmsg("ALTER TABLE / DROP EXPRESSION must be applied to child tables too")));
    8537             : 
    8538             :     /*
    8539             :      * Cannot drop generation expression from inherited columns.
    8540             :      */
    8541          38 :     if (!recursing)
    8542             :     {
    8543             :         HeapTuple   tuple;
    8544             :         Form_pg_attribute attTup;
    8545             : 
    8546          32 :         tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), cmd->name);
    8547          32 :         if (!HeapTupleIsValid(tuple))
    8548           0 :             ereport(ERROR,
    8549             :                     (errcode(ERRCODE_UNDEFINED_COLUMN),
    8550             :                      errmsg("column \"%s\" of relation \"%s\" does not exist",
    8551             :                             cmd->name, RelationGetRelationName(rel))));
    8552             : 
    8553          32 :         attTup = (Form_pg_attribute) GETSTRUCT(tuple);
    8554             : 
    8555          32 :         if (attTup->attinhcount > 0)
    8556           6 :             ereport(ERROR,
    8557             :                     (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    8558             :                      errmsg("cannot drop generation expression from inherited column")));
    8559             :     }
    8560          32 : }
    8561             : 
    8562             : /*
    8563             :  * Return the address of the affected column.
    8564             :  */
    8565             : static ObjectAddress
    8566          32 : ATExecDropExpression(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode)
    8567             : {
    8568             :     HeapTuple   tuple;
    8569             :     Form_pg_attribute attTup;
    8570             :     AttrNumber  attnum;
    8571             :     Relation    attrelation;
    8572             :     Oid         attrdefoid;
    8573             :     ObjectAddress address;
    8574             : 
    8575          32 :     attrelation = table_open(AttributeRelationId, RowExclusiveLock);
    8576          32 :     tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
    8577          32 :     if (!HeapTupleIsValid(tuple))
    8578           0 :         ereport(ERROR,
    8579             :                 (errcode(ERRCODE_UNDEFINED_COLUMN),
    8580             :                  errmsg("column \"%s\" of relation \"%s\" does not exist",
    8581             :                         colName, RelationGetRelationName(rel))));
    8582             : 
    8583          32 :     attTup = (Form_pg_attribute) GETSTRUCT(tuple);
    8584          32 :     attnum = attTup->attnum;
    8585             : 
    8586          32 :     if (attnum <= 0)
    8587           0 :         ereport(ERROR,
    8588             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    8589             :                  errmsg("cannot alter system column \"%s\"",
    8590             :                         colName)));
    8591             : 
    8592          32 :     if (attTup->attgenerated != ATTRIBUTE_GENERATED_STORED)
    8593             :     {
    8594          12 :         if (!missing_ok)
    8595           6 :             ereport(ERROR,
    8596             :                     (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    8597             :                      errmsg("column \"%s\" of relation \"%s\" is not a stored generated column",
    8598             :                             colName, RelationGetRelationName(rel))));
    8599             :         else
    8600             :         {
    8601           6 :             ereport(NOTICE,
    8602             :                     (errmsg("column \"%s\" of relation \"%s\" is not a stored generated column, skipping",
    8603             :                             colName, RelationGetRelationName(rel))));
    8604           6 :             heap_freetuple(tuple);
    8605           6 :             table_close(attrelation, RowExclusiveLock);
    8606           6 :             return InvalidObjectAddress;
    8607             :         }
    8608             :     }
    8609             : 
    8610             :     /*
    8611             :      * Mark the column as no longer generated.  (The atthasdef flag needs to
    8612             :      * get cleared too, but RemoveAttrDefault will handle that.)
    8613             :      */
    8614          20 :     attTup->attgenerated = '\0';
    8615          20 :     CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
    8616             : 
    8617          20 :     InvokeObjectPostAlterHook(RelationRelationId,
    8618             :                               RelationGetRelid(rel),
    8619             :                               attnum);
    8620          20 :     heap_freetuple(tuple);
    8621             : 
    8622          20 :     table_close(attrelation, RowExclusiveLock);
    8623             : 
    8624             :     /*
    8625             :      * Drop the dependency records of the GENERATED expression, in particular
    8626             :      * its INTERNAL dependency on the column, which would otherwise cause
    8627             :      * dependency.c to refuse to perform the deletion.
    8628             :      */
    8629          20 :     attrdefoid = GetAttrDefaultOid(RelationGetRelid(rel), attnum);
    8630          20 :     if (!OidIsValid(attrdefoid))
    8631           0 :         elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
    8632             :              RelationGetRelid(rel), attnum);
    8633          20 :     (void) deleteDependencyRecordsFor(AttrDefaultRelationId, attrdefoid, false);
    8634             : 
    8635             :     /* Make above changes visible */
    8636          20 :     CommandCounterIncrement();
    8637             : 
    8638             :     /*
    8639             :      * Get rid of the GENERATED expression itself.  We use RESTRICT here for
    8640             :      * safety, but at present we do not expect anything to depend on the
    8641             :      * default.
    8642             :      */
    8643          20 :     RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT,
    8644             :                       false, false);
    8645             : 
    8646          20 :     ObjectAddressSubSet(address, RelationRelationId,
    8647             :                         RelationGetRelid(rel), attnum);
    8648          20 :     return address;
    8649             : }
    8650             : 
    8651             : /*
    8652             :  * ALTER TABLE ALTER COLUMN SET STATISTICS
    8653             :  *
    8654             :  * Return value is the address of the modified column
    8655             :  */
    8656             : static ObjectAddress
    8657         164 : ATExecSetStatistics(Relation rel, const char *colName, int16 colNum, Node *newValue, LOCKMODE lockmode)
    8658             : {
    8659         164 :     int         newtarget = 0;
    8660             :     bool        newtarget_default;
    8661             :     Relation    attrelation;
    8662             :     HeapTuple   tuple,
    8663             :                 newtuple;
    8664             :     Form_pg_attribute attrtuple;
    8665             :     AttrNumber  attnum;
    8666             :     ObjectAddress address;
    8667             :     Datum       repl_val[Natts_pg_attribute];
    8668             :     bool        repl_null[Natts_pg_attribute];
    8669             :     bool        repl_repl[Natts_pg_attribute];
    8670             : 
    8671             :     /*
    8672             :      * We allow referencing columns by numbers only for indexes, since table
    8673             :      * column numbers could contain gaps if columns are later dropped.
    8674             :      */
    8675         164 :     if (rel->rd_rel->relkind != RELKIND_INDEX &&
    8676         100 :         rel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX &&
    8677             :         !colName)
    8678           0 :         ereport(ERROR,
    8679             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    8680             :                  errmsg("cannot refer to non-index column by number")));
    8681             : 
    8682             :     /* -1 was used in previous versions for the default setting */
    8683         164 :     if (newValue && intVal(newValue) != -1)
    8684             :     {
    8685         120 :         newtarget = intVal(newValue);
    8686         120 :         newtarget_default = false;
    8687             :     }
    8688             :     else
    8689          44 :         newtarget_default = true;
    8690             : 
    8691         164 :     if (!newtarget_default)
    8692             :     {
    8693             :         /*
    8694             :          * Limit target to a sane range
    8695             :          */
    8696         120 :         if (newtarget < 0)
    8697             :         {
    8698           0 :             ereport(ERROR,
    8699             :                     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    8700             :                      errmsg("statistics target %d is too low",
    8701             :                             newtarget)));
    8702             :         }
    8703         120 :         else if (newtarget > MAX_STATISTICS_TARGET)
    8704             :         {
    8705           0 :             newtarget = MAX_STATISTICS_TARGET;
    8706           0 :             ereport(WARNING,
    8707             :                     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    8708             :                      errmsg("lowering statistics target to %d",
    8709             :                             newtarget)));
    8710             :         }
    8711             :     }
    8712             : 
    8713         164 :     attrelation = table_open(AttributeRelationId, RowExclusiveLock);
    8714             : 
    8715         164 :     if (colName)
    8716             :     {
    8717         100 :         tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
    8718             : 
    8719         100 :         if (!HeapTupleIsValid(tuple))
    8720          12 :             ereport(ERROR,
    8721             :                     (errcode(ERRCODE_UNDEFINED_COLUMN),
    8722             :                      errmsg("column \"%s\" of relation \"%s\" does not exist",
    8723             :                             colName, RelationGetRelationName(rel))));
    8724             :     }
    8725             :     else
    8726             :     {
    8727          64 :         tuple = SearchSysCacheAttNum(RelationGetRelid(rel), colNum);
    8728             : 
    8729          64 :         if (!HeapTupleIsValid(tuple))
    8730          12 :             ereport(ERROR,
    8731             :                     (errcode(ERRCODE_UNDEFINED_COLUMN),
    8732             :                      errmsg("column number %d of relation \"%s\" does not exist",
    8733             :                             colNum, RelationGetRelationName(rel))));
    8734             :     }
    8735             : 
    8736         140 :     attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
    8737             : 
    8738         140 :     attnum = attrtuple->attnum;
    8739         140 :     if (attnum <= 0)
    8740           0 :         ereport(ERROR,
    8741             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    8742             :                  errmsg("cannot alter system column \"%s\"",
    8743             :                         colName)));
    8744             : 
    8745         140 :     if (rel->rd_rel->relkind == RELKIND_INDEX ||
    8746          88 :         rel->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
    8747             :     {
    8748          52 :         if (attnum > rel->rd_index->indnkeyatts)
    8749           6 :             ereport(ERROR,
    8750             :                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    8751             :                      errmsg("cannot alter statistics on included column \"%s\" of index \"%s\"",
    8752             :                             NameStr(attrtuple->attname), RelationGetRelationName(rel))));
    8753          46 :         else if (rel->rd_index->indkey.values[attnum - 1] != 0)
    8754          18 :             ereport(ERROR,
    8755             :                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    8756             :                      errmsg("cannot alter statistics on non-expression column \"%s\" of index \"%s\"",
    8757             :                             NameStr(attrtuple->attname), RelationGetRelationName(rel)),
    8758             :                      errhint("Alter statistics on table column instead.")));
    8759             :     }
    8760             : 
    8761             :     /* Build new tuple. */
    8762         116 :     memset(repl_null, false, sizeof(repl_null));
    8763         116 :     memset(repl_repl, false, sizeof(repl_repl));
    8764         116 :     if (!newtarget_default)
    8765          72 :         repl_val[Anum_pg_attribute_attstattarget - 1] = newtarget;
    8766             :     else
    8767          44 :         repl_null[Anum_pg_attribute_attstattarget - 1] = true;
    8768         116 :     repl_repl[Anum_pg_attribute_attstattarget - 1] = true;
    8769         116 :     newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrelation),
    8770             :                                  repl_val, repl_null, repl_repl);
    8771         116 :     CatalogTupleUpdate(attrelation, &tuple->t_self, newtuple);
    8772             : 
    8773         116 :     InvokeObjectPostAlterHook(RelationRelationId,
    8774             :                               RelationGetRelid(rel),
    8775             :                               attrtuple->attnum);
    8776         116 :     ObjectAddressSubSet(address, RelationRelationId,
    8777             :                         RelationGetRelid(rel), attnum);
    8778             : 
    8779         116 :     heap_freetuple(newtuple);
    8780             : 
    8781         116 :     ReleaseSysCache(tuple);
    8782             : 
    8783         116 :     table_close(attrelation, RowExclusiveLock);
    8784             : 
    8785         116 :     return address;
    8786             : }
    8787             : 
    8788             : /*
    8789             :  * Return value is the address of the modified column
    8790             :  */
    8791             : static ObjectAddress
    8792          32 : ATExecSetOptions(Relation rel, const char *colName, Node *options,
    8793             :                  bool isReset, LOCKMODE lockmode)
    8794             : {
    8795             :     Relation    attrelation;
    8796             :     HeapTuple   tuple,
    8797             :                 newtuple;
    8798             :     Form_pg_attribute attrtuple;
    8799             :     AttrNumber  attnum;
    8800             :     Datum       datum,
    8801             :                 newOptions;
    8802             :     bool        isnull;
    8803             :     ObjectAddress address;
    8804             :     Datum       repl_val[Natts_pg_attribute];
    8805             :     bool        repl_null[Natts_pg_attribute];
    8806             :     bool        repl_repl[Natts_pg_attribute];
    8807             : 
    8808          32 :     attrelation = table_open(AttributeRelationId, RowExclusiveLock);
    8809             : 
    8810          32 :     tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
    8811             : 
    8812          32 :     if (!HeapTupleIsValid(tuple))
    8813           0 :         ereport(ERROR,
    8814             :                 (errcode(ERRCODE_UNDEFINED_COLUMN),
    8815             :                  errmsg("column \"%s\" of relation \"%s\" does not exist",
    8816             :                         colName, RelationGetRelationName(rel))));
    8817          32 :     attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
    8818             : 
    8819          32 :     attnum = attrtuple->attnum;
    8820          32 :     if (attnum <= 0)
    8821           0 :         ereport(ERROR,
    8822             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    8823             :                  errmsg("cannot alter system column \"%s\"",
    8824             :                         colName)));
    8825             : 
    8826             :     /* Generate new proposed attoptions (text array) */
    8827          32 :     datum = SysCacheGetAttr(ATTNAME, tuple, Anum_pg_attribute_attoptions,
    8828             :                             &isnull);
    8829          32 :     newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
    8830             :                                      castNode(List, options), NULL, NULL,
    8831             :                                      false, isReset);
    8832             :     /* Validate new options */
    8833          32 :     (void) attribute_reloptions(newOptions, true);
    8834             : 
    8835             :     /* Build new tuple. */
    8836          32 :     memset(repl_null, false, sizeof(repl_null));
    8837          32 :     memset(repl_repl, false, sizeof(repl_repl));
    8838          32 :     if (newOptions != (Datum) 0)
    8839          32 :         repl_val[Anum_pg_attribute_attoptions - 1] = newOptions;
    8840             :     else
    8841           0 :         repl_null[Anum_pg_attribute_attoptions - 1] = true;
    8842          32 :     repl_repl[Anum_pg_attribute_attoptions - 1] = true;
    8843          32 :     newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrelation),
    8844             :                                  repl_val, repl_null, repl_repl);
    8845             : 
    8846             :     /* Update system catalog. */
    8847          32 :     CatalogTupleUpdate(attrelation, &newtuple->t_self, newtuple);
    8848             : 
    8849          32 :     InvokeObjectPostAlterHook(RelationRelationId,
    8850             :                               RelationGetRelid(rel),
    8851             :                               attrtuple->attnum);
    8852          32 :     ObjectAddressSubSet(address, RelationRelationId,
    8853             :                         RelationGetRelid(rel), attnum);
    8854             : 
    8855          32 :     heap_freetuple(newtuple);
    8856             : 
    8857          32 :     ReleaseSysCache(tuple);
    8858             : 
    8859          32 :     table_close(attrelation, RowExclusiveLock);
    8860             : 
    8861          32 :     return address;
    8862             : }
    8863             : 
    8864             : /*
    8865             :  * Helper function for ATExecSetStorage and ATExecSetCompression
    8866             :  *
    8867             :  * Set the attstorage and/or attcompression fields for index columns
    8868             :  * associated with the specified table column.
    8869             :  */
    8870             : static void
    8871         284 : SetIndexStorageProperties(Relation rel, Relation attrelation,
    8872             :                           AttrNumber attnum,
    8873             :                           bool setstorage, char newstorage,
    8874             :                           bool setcompression, char newcompression,
    8875             :                           LOCKMODE lockmode)
    8876             : {
    8877             :     ListCell   *lc;
    8878             : 
    8879         356 :     foreach(lc, RelationGetIndexList(rel))
    8880             :     {
    8881          72 :         Oid         indexoid = lfirst_oid(lc);
    8882             :         Relation    indrel;
    8883          72 :         AttrNumber  indattnum = 0;
    8884             :         HeapTuple   tuple;
    8885             : 
    8886          72 :         indrel = index_open(indexoid, lockmode);
    8887             : 
    8888         120 :         for (int i = 0; i < indrel->rd_index->indnatts; i++)
    8889             :         {
    8890          78 :             if (indrel->rd_index->indkey.values[i] == attnum)
    8891             :             {
    8892          30 :                 indattnum = i + 1;
    8893          30 :                 break;
    8894             :             }
    8895             :         }
    8896             : 
    8897          72 :         if (indattnum == 0)
    8898             :         {
    8899          42 :             index_close(indrel, lockmode);
    8900          42 :             continue;
    8901             :         }
    8902             : 
    8903          30 :         tuple = SearchSysCacheCopyAttNum(RelationGetRelid(indrel), indattnum);
    8904             : 
    8905          30 :         if (HeapTupleIsValid(tuple))
    8906             :         {
    8907          30 :             Form_pg_attribute attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
    8908             : 
    8909          30 :             if (setstorage)
    8910          24 :                 attrtuple->attstorage = newstorage;
    8911             : 
    8912          30 :             if (setcompression)
    8913           6 :                 attrtuple->attcompression = newcompression;
    8914             : 
    8915          30 :             CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
    8916             : 
    8917          30 :             InvokeObjectPostAlterHook(RelationRelationId,
    8918             :                                       RelationGetRelid(rel),
    8919             :                                       attrtuple->attnum);
    8920             : 
    8921          30 :             heap_freetuple(tuple);
    8922             :         }
    8923             : 
    8924          30 :         index_close(indrel, lockmode);
    8925             :     }
    8926         284 : }
    8927             : 
    8928             : /*
    8929             :  * ALTER TABLE ALTER COLUMN SET STORAGE
    8930             :  *
    8931             :  * Return value is the address of the modified column
    8932             :  */
    8933             : static ObjectAddress
    8934         234 : ATExecSetStorage(Relation rel, const char *colName, Node *newValue, LOCKMODE lockmode)
    8935             : {
    8936             :     Relation    attrelation;
    8937             :     HeapTuple   tuple;
    8938             :     Form_pg_attribute attrtuple;
    8939             :     AttrNumber  attnum;
    8940             :     ObjectAddress address;
    8941             : 
    8942         234 :     attrelation = table_open(AttributeRelationId, RowExclusiveLock);
    8943             : 
    8944         234 :     tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
    8945             : 
    8946         234 :     if (!HeapTupleIsValid(tuple))
    8947          12 :         ereport(ERROR,
    8948             :                 (errcode(ERRCODE_UNDEFINED_COLUMN),
    8949             :                  errmsg("column \"%s\" of relation \"%s\" does not exist",
    8950             :                         colName, RelationGetRelationName(rel))));
    8951         222 :     attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
    8952             : 
    8953         222 :     attnum = attrtuple->attnum;
    8954         222 :     if (attnum <= 0)
    8955           0 :         ereport(ERROR,
    8956             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    8957             :                  errmsg("cannot alter system column \"%s\"",
    8958             :                         colName)));
    8959             : 
    8960         222 :     attrtuple->attstorage = GetAttributeStorage(attrtuple->atttypid, strVal(newValue));
    8961             : 
    8962         222 :     CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
    8963             : 
    8964         222 :     InvokeObjectPostAlterHook(RelationRelationId,
    8965             :                               RelationGetRelid(rel),
    8966             :                               attrtuple->attnum);
    8967             : 
    8968             :     /*
    8969             :      * Apply the change to indexes as well (only for simple index columns,
    8970             :      * matching behavior of index.c ConstructTupleDescriptor()).
    8971             :      */
    8972         222 :     SetIndexStorageProperties(rel, attrelation, attnum,
    8973         222 :                               true, attrtuple->attstorage,
    8974             :                               false, 0,
    8975             :                               lockmode);
    8976             : 
    8977         222 :     heap_freetuple(tuple);
    8978             : 
    8979         222 :     table_close(attrelation, RowExclusiveLock);
    8980             : 
    8981         222 :     ObjectAddressSubSet(address, RelationRelationId,
    8982             :                         RelationGetRelid(rel), attnum);
    8983         222 :     return address;
    8984             : }
    8985             : 
    8986             : 
    8987             : /*
    8988             :  * ALTER TABLE DROP COLUMN
    8989             :  *
    8990             :  * DROP COLUMN cannot use the normal ALTER TABLE recursion mechanism,
    8991             :  * because we have to decide at runtime whether to recurse or not depending
    8992             :  * on whether attinhcount goes to zero or not.  (We can't check this in a
    8993             :  * static pre-pass because it won't handle multiple inheritance situations
    8994             :  * correctly.)
    8995             :  */
    8996             : static void
    8997        1604 : ATPrepDropColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
    8998             :                  AlterTableCmd *cmd, LOCKMODE lockmode,
    8999             :                  AlterTableUtilityContext *context)
    9000             : {
    9001        1604 :     if (rel->rd_rel->reloftype && !recursing)
    9002           6 :         ereport(ERROR,
    9003             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    9004             :                  errmsg("cannot drop column from typed table")));
    9005             : 
    9006        1598 :     if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
    9007          82 :         ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
    9008             : 
    9009        1592 :     if (recurse)
    9010        1324 :         cmd->recurse = true;
    9011        1592 : }
    9012             : 
    9013             : /*
    9014             :  * Drops column 'colName' from relation 'rel' and returns the address of the
    9015             :  * dropped column.  The column is also dropped (or marked as no longer
    9016             :  * inherited from relation) from the relation's inheritance children, if any.
    9017             :  *
    9018             :  * In the recursive invocations for inheritance child relations, instead of
    9019             :  * dropping the column directly (if to be dropped at all), its object address
    9020             :  * is added to 'addrs', which must be non-NULL in such invocations.  All
    9021             :  * columns are dropped at the same time after all the children have been
    9022             :  * checked recursively.
    9023             :  */
    9024             : static ObjectAddress
    9025        2148 : ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
    9026             :                  DropBehavior behavior,
    9027             :                  bool recurse, bool recursing,
    9028             :                  bool missing_ok, LOCKMODE lockmode,
    9029             :                  ObjectAddresses *addrs)
    9030             : {
    9031             :     HeapTuple   tuple;
    9032             :     Form_pg_attribute targetatt;
    9033             :     AttrNumber  attnum;
    9034             :     List       *children;
    9035             :     ObjectAddress object;
    9036             :     bool        is_expr;
    9037             : 
    9038             :     /* At top level, permission check was done in ATPrepCmd, else do it */
    9039        2148 :     if (recursing)
    9040         556 :         ATSimplePermissions(AT_DropColumn, rel,
    9041             :                             ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
    9042             : 
    9043             :     /* Initialize addrs on the first invocation */
    9044             :     Assert(!recursing || addrs != NULL);
    9045             : 
    9046             :     /* since this function recurses, it could be driven to stack overflow */
    9047        2148 :     check_stack_depth();
    9048             : 
    9049        2148 :     if (!recursing)
    9050        1592 :         addrs = new_object_addresses();
    9051             : 
    9052             :     /*
    9053             :      * get the number of the attribute
    9054             :      */
    9055        2148 :     tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
    9056        2148 :     if (!HeapTupleIsValid(tuple))
    9057             :     {
    9058          54 :         if (!missing_ok)
    9059             :         {
    9060          36 :             ereport(ERROR,
    9061             :                     (errcode(ERRCODE_UNDEFINED_COLUMN),
    9062             :                      errmsg("column \"%s\" of relation \"%s\" does not exist",
    9063             :                             colName, RelationGetRelationName(rel))));
    9064             :         }
    9065             :         else
    9066             :         {
    9067          18 :             ereport(NOTICE,
    9068             :                     (errmsg("column \"%s\" of relation \"%s\" does not exist, skipping",
    9069             :                             colName, RelationGetRelationName(rel))));
    9070          18 :             return InvalidObjectAddress;
    9071             :         }
    9072             :     }
    9073        2094 :     targetatt = (Form_pg_attribute) GETSTRUCT(tuple);
    9074             : 
    9075        2094 :     attnum = targetatt->attnum;
    9076             : 
    9077             :     /* Can't drop a system attribute */
    9078        2094 :     if (attnum <= 0)
    9079           6 :         ereport(ERROR,
    9080             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    9081             :                  errmsg("cannot drop system column \"%s\"",
    9082             :                         colName)));
    9083             : 
    9084             :     /*
    9085             :      * Don't drop inherited columns, unless recursing (presumably from a drop
    9086             :      * of the parent column)
    9087             :      */
    9088        2088 :     if (targetatt->attinhcount > 0 && !recursing)
    9089          48 :         ereport(ERROR,
    9090             :                 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    9091             :                  errmsg("cannot drop inherited column \"%s\"",
    9092             :                         colName)));
    9093             : 
    9094             :     /*
    9095             :      * Don't drop columns used in the partition key, either.  (If we let this
    9096             :      * go through, the key column's dependencies would cause a cascaded drop
    9097             :      * of the whole table, which is surely not what the user expected.)
    9098             :      */
    9099        2040 :     if (has_partition_attrs(rel,
    9100             :                             bms_make_singleton(attnum - FirstLowInvalidHeapAttributeNumber),
    9101             :                             &is_expr))
    9102          30 :         ereport(ERROR,
    9103             :                 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    9104             :                  errmsg("cannot drop column \"%s\" because it is part of the partition key of relation \"%s\"",
    9105             :                         colName, RelationGetRelationName(rel))));
    9106             : 
    9107        2010 :     ReleaseSysCache(tuple);
    9108             : 
    9109             :     /*
    9110             :      * Propagate to children as appropriate.  Unlike most other ALTER
    9111             :      * routines, we have to do this one level of recursion at a time; we can't
    9112             :      * use find_all_inheritors to do it in one pass.
    9113             :      */
    9114             :     children =
    9115        2010 :         find_inheritance_children(RelationGetRelid(rel), lockmode);
    9116             : 
    9117        2010 :     if (children)
    9118             :     {
    9119             :         Relation    attr_rel;
    9120             :         ListCell   *child;
    9121             : 
    9122             :         /*
    9123             :          * In case of a partitioned table, the column must be dropped from the
    9124             :          * partitions as well.
    9125             :          */
    9126         302 :         if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && !recurse)
    9127           6 :             ereport(ERROR,
    9128             :                     (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    9129             :                      errmsg("cannot drop column from only the partitioned table when partitions exist"),
    9130             :                      errhint("Do not specify the ONLY keyword.")));
    9131             : 
    9132         296 :         attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
    9133         882 :         foreach(child, children)
    9134             :         {
    9135         592 :             Oid         childrelid = lfirst_oid(child);
    9136             :             Relation    childrel;
    9137             :             Form_pg_attribute childatt;
    9138             : 
    9139             :             /* find_inheritance_children already got lock */
    9140         592 :             childrel = table_open(childrelid, NoLock);
    9141         592 :             CheckAlterTableIsSafe(childrel);
    9142             : 
    9143         592 :             tuple = SearchSysCacheCopyAttName(childrelid, colName);
    9144         592 :             if (!HeapTupleIsValid(tuple))   /* shouldn't happen */
    9145           0 :                 elog(ERROR, "cache lookup failed for attribute \"%s\" of relation %u",
    9146             :                      colName, childrelid);
    9147         592 :             childatt = (Form_pg_attribute) GETSTRUCT(tuple);
    9148             : 
    9149         592 :             if (childatt->attinhcount <= 0) /* shouldn't happen */
    9150           0 :                 elog(ERROR, "relation %u has non-inherited attribute \"%s\"",
    9151             :                      childrelid, colName);
    9152             : 
    9153         592 :             if (recurse)
    9154             :             {
    9155             :                 /*
    9156             :                  * If the child column has other definition sources, just
    9157             :                  * decrement its inheritance count; if not, recurse to delete
    9158             :                  * it.
    9159             :                  */
    9160         568 :                 if (childatt->attinhcount == 1 && !childatt->attislocal)
    9161             :                 {
    9162             :                     /* Time to delete this child column, too */
    9163         556 :                     ATExecDropColumn(wqueue, childrel, colName,
    9164             :                                      behavior, true, true,
    9165             :                                      false, lockmode, addrs);
    9166             :                 }
    9167             :                 else
    9168             :                 {
    9169             :                     /* Child column must survive my deletion */
    9170          12 :                     childatt->attinhcount--;
    9171             : 
    9172          12 :                     CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
    9173             : 
    9174             :                     /* Make update visible */
    9175          12 :                     CommandCounterIncrement();
    9176             :                 }
    9177             :             }
    9178             :             else
    9179             :             {
    9180             :                 /*
    9181             :                  * If we were told to drop ONLY in this table (no recursion),
    9182             :                  * we need to mark the inheritors' attributes as locally
    9183             :                  * defined rather than inherited.
    9184             :                  */
    9185          24 :                 childatt->attinhcount--;
    9186          24 :                 childatt->attislocal = true;
    9187             : 
    9188          24 :                 CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
    9189             : 
    9190             :                 /* Make update visible */
    9191          24 :                 CommandCounterIncrement();
    9192             :             }
    9193             : 
    9194         586 :             heap_freetuple(tuple);
    9195             : 
    9196         586 :             table_close(childrel, NoLock);
    9197             :         }
    9198         290 :         table_close(attr_rel, RowExclusiveLock);
    9199             :     }
    9200             : 
    9201             :     /* Add object to delete */
    9202        1998 :     object.classId = RelationRelationId;
    9203        1998 :     object.objectId = RelationGetRelid(rel);
    9204        1998 :     object.objectSubId = attnum;
    9205        1998 :     add_exact_object_address(&object, addrs);
    9206             : 
    9207        1998 :     if (!recursing)
    9208             :     {
    9209             :         /* Recursion has ended, drop everything that was collected */
    9210        1448 :         performMultipleDeletions(addrs, behavior, 0);
    9211        1400 :         free_object_addresses(addrs);
    9212             :     }
    9213             : 
    9214        1950 :     return object;
    9215             : }
    9216             : 
    9217             : /*
    9218             :  * Prepare to add a primary key on table, by adding not-null constraints
    9219             :  * on all columns.
    9220             :  */
    9221             : static void
    9222       14938 : ATPrepAddPrimaryKey(List **wqueue, Relation rel, AlterTableCmd *cmd,
    9223             :                     bool recurse, LOCKMODE lockmode,
    9224             :                     AlterTableUtilityContext *context)
    9225             : {
    9226             :     ListCell   *lc;
    9227             :     Constraint *pkconstr;
    9228             : 
    9229       14938 :     pkconstr = castNode(Constraint, cmd->def);
    9230       14938 :     if (pkconstr->contype != CONSTR_PRIMARY)
    9231        8776 :         return;
    9232             : 
    9233             :     /*
    9234             :      * If not recursing, we must ensure that all children have a NOT NULL
    9235             :      * constraint on the columns, and error out if not.
    9236             :      */
    9237        6162 :     if (!recurse)
    9238             :     {
    9239             :         List       *children;
    9240             : 
    9241         254 :         children = find_inheritance_children(RelationGetRelid(rel),
    9242             :                                              lockmode);
    9243         628 :         foreach_oid(childrelid, children)
    9244             :         {
    9245         260 :             foreach(lc, pkconstr->keys)
    9246             :             {
    9247             :                 HeapTuple   tup;
    9248             :                 Form_pg_attribute attrForm;
    9249         134 :                 char       *attname = strVal(lfirst(lc));
    9250             : 
    9251         134 :                 tup = SearchSysCacheAttName(childrelid, attname);
    9252         134 :                 if (!tup)
    9253           0 :                     elog(ERROR, "cache lookup failed for attribute %s of relation %u",
    9254             :                          attname, childrelid);
    9255         134 :                 attrForm = (Form_pg_attribute) GETSTRUCT(tup);
    9256         134 :                 if (!attrForm->attnotnull)
    9257           6 :                     ereport(ERROR,
    9258             :                             errmsg("column \"%s\" of table \"%s\" is not marked NOT NULL",
    9259             :                                    attname, get_rel_name(childrelid)));
    9260         128 :                 ReleaseSysCache(tup);
    9261             :             }
    9262             :         }
    9263             :     }
    9264             : 
    9265             :     /* Insert not-null constraints in the queue for the PK columns */
    9266        7106 :     foreach(lc, pkconstr->keys)
    9267             :     {
    9268             :         AlterTableCmd *newcmd;
    9269             :         Constraint *nnconstr;
    9270             : 
    9271         950 :         nnconstr = makeNotNullConstraint(lfirst(lc));
    9272             : 
    9273         950 :         newcmd = makeNode(AlterTableCmd);
    9274         950 :         newcmd->subtype = AT_AddConstraint;
    9275         950 :         newcmd->recurse = true;
    9276         950 :         newcmd->def = (Node *) nnconstr;
    9277             : 
    9278         950 :         ATPrepCmd(wqueue, rel, newcmd, true, false, lockmode, context);
    9279             :     }
    9280             : }
    9281             : 
    9282             : /*
    9283             :  * ALTER TABLE ADD INDEX
    9284             :  *
    9285             :  * There is no such command in the grammar, but parse_utilcmd.c converts
    9286             :  * UNIQUE and PRIMARY KEY constraints into AT_AddIndex subcommands.  This lets
    9287             :  * us schedule creation of the index at the appropriate time during ALTER.
    9288             :  *
    9289             :  * Return value is the address of the new index.
    9290             :  */
    9291             : static ObjectAddress
    9292        1588 : ATExecAddIndex(AlteredTableInfo *tab, Relation rel,
    9293             :                IndexStmt *stmt, bool is_rebuild, LOCKMODE lockmode)
    9294             : {
    9295             :     bool        check_rights;
    9296             :     bool        skip_build;
    9297             :     bool        quiet;
    9298             :     ObjectAddress address;
    9299             : 
    9300             :     Assert(IsA(stmt, IndexStmt));
    9301             :     Assert(!stmt->concurrent);
    9302             : 
    9303             :     /* The IndexStmt has already been through transformIndexStmt */
    9304             :     Assert(stmt->transformed);
    9305             : 
    9306             :     /* suppress schema rights check when rebuilding existing index */
    9307        1588 :     check_rights = !is_rebuild;
    9308             :     /* skip index build if phase 3 will do it or we're reusing an old one */
    9309        1588 :     skip_build = tab->rewrite > 0 || RelFileNumberIsValid(stmt->oldNumber);
    9310             :     /* suppress notices when rebuilding existing index */
    9311        1588 :     quiet = is_rebuild;
    9312             : 
    9313        1588 :     address = DefineIndex(RelationGetRelid(rel),
    9314             :                           stmt,
    9315             :                           InvalidOid,   /* no predefined OID */
    9316             :                           InvalidOid,   /* no parent index */
    9317             :                           InvalidOid,   /* no parent constraint */
    9318             :                           -1,   /* total_parts unknown */
    9319             :                           true, /* is_alter_table */
    9320             :                           check_rights,
    9321             :                           false,    /* check_not_in_use - we did it already */
    9322             :                           skip_build,
    9323             :                           quiet);
    9324             : 
    9325             :     /*
    9326             :      * If TryReuseIndex() stashed a relfilenumber for us, we used it for the
    9327             :      * new index instead of building from scratch.  Restore associated fields.
    9328             :      * This may store InvalidSubTransactionId in both fields, in which case
    9329             :      * relcache.c will assume it can rebuild the relcache entry.  Hence, do
    9330             :      * this after the CCI that made catalog rows visible to any rebuild.  The
    9331             :      * DROP of the old edition of this index will have scheduled the storage
    9332             :      * for deletion at commit, so cancel that pending deletion.
    9333             :      */
    9334        1418 :     if (RelFileNumberIsValid(stmt->oldNumber))
    9335             :     {
    9336          74 :         Relation    irel = index_open(address.objectId, NoLock);
    9337             : 
    9338          74 :         irel->rd_createSubid = stmt->oldCreateSubid;
    9339          74 :         irel->rd_firstRelfilelocatorSubid = stmt->oldFirstRelfilelocatorSubid;
    9340          74 :         RelationPreserveStorage(irel->rd_locator, true);
    9341          74 :         index_close(irel, NoLock);
    9342             :     }
    9343             : 
    9344        1418 :     return address;
    9345             : }
    9346             : 
    9347             : /*
    9348             :  * ALTER TABLE ADD STATISTICS
    9349             :  *
    9350             :  * This is no such command in the grammar, but we use this internally to add
    9351             :  * AT_ReAddStatistics subcommands to rebuild extended statistics after a table
    9352             :  * column type change.
    9353             :  */
    9354             : static ObjectAddress
    9355          14 : ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
    9356             :                     CreateStatsStmt *stmt, bool is_rebuild, LOCKMODE lockmode)
    9357             : {
    9358             :     ObjectAddress address;
    9359             : 
    9360             :     Assert(IsA(stmt, CreateStatsStmt));
    9361             : 
    9362             :     /* The CreateStatsStmt has already been through transformStatsStmt */
    9363             :     Assert(stmt->transformed);
    9364             : 
    9365          14 :     address = CreateStatistics(stmt);
    9366             : 
    9367          14 :     return address;
    9368             : }
    9369             : 
    9370             : /*
    9371             :  * ALTER TABLE ADD CONSTRAINT USING INDEX
    9372             :  *
    9373             :  * Returns the address of the new constraint.
    9374             :  */
    9375             : static ObjectAddress
    9376        9540 : ATExecAddIndexConstraint(AlteredTableInfo *tab, Relation rel,
    9377             :                          IndexStmt *stmt, LOCKMODE lockmode)
    9378             : {
    9379        9540 :     Oid         index_oid = stmt->indexOid;
    9380             :     Relation    indexRel;
    9381             :     char       *indexName;
    9382             :     IndexInfo  *indexInfo;
    9383             :     char       *constraintName;
    9384             :     char        constraintType;
    9385             :     ObjectAddress address;
    9386             :     bits16      flags;
    9387             : 
    9388             :     Assert(IsA(stmt, IndexStmt));
    9389             :     Assert(OidIsValid(index_oid));
    9390             :     Assert(stmt->isconstraint);
    9391             : 
    9392             :     /*
    9393             :      * Doing this on partitioned tables is not a simple feature to implement,
    9394             :      * so let's punt for now.
    9395             :      */
    9396        9540 :     if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
    9397           6 :         ereport(ERROR,
    9398             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    9399             :                  errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX is not supported on partitioned tables")));
    9400             : 
    9401        9534 :     indexRel = index_open(index_oid, AccessShareLock);
    9402             : 
    9403        9534 :     indexName = pstrdup(RelationGetRelationName(indexRel));
    9404             : 
    9405        9534 :     indexInfo = BuildIndexInfo(indexRel);
    9406             : 
    9407             :     /* this should have been checked at parse time */
    9408        9534 :     if (!indexInfo->ii_Unique)
    9409           0 :         elog(ERROR, "index \"%s\" is not unique", indexName);
    9410             : 
    9411             :     /*
    9412             :      * Determine name to assign to constraint.  We require a constraint to
    9413             :      * have the same name as the underlying index; therefore, use the index's
    9414             :      * existing name as the default constraint name, and if the user
    9415             :      * explicitly gives some other name for the constraint, rename the index
    9416             :      * to match.
    9417             :      */
    9418        9534 :     constraintName = stmt->idxname;
    9419        9534 :     if (constraintName == NULL)
    9420        9508 :         constraintName = indexName;
    9421          26 :     else if (strcmp(constraintName, indexName) != 0)
    9422             :     {
    9423          20 :         ereport(NOTICE,
    9424             :                 (errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX will rename index \"%s\" to \"%s\"",
    9425             :                         indexName, constraintName)));
    9426          20 :         RenameRelationInternal(index_oid, constraintName, false, true);
    9427             :     }
    9428             : 
    9429             :     /* Extra checks needed if making primary key */
    9430        9534 :     if (stmt->primary)
    9431        5386 :         index_check_primary_key(rel, indexInfo, true, stmt);
    9432             : 
    9433             :     /* Note we currently don't support EXCLUSION constraints here */
    9434        9528 :     if (stmt->primary)
    9435        5380 :         constraintType = CONSTRAINT_PRIMARY;
    9436             :     else
    9437        4148 :         constraintType = CONSTRAINT_UNIQUE;
    9438             : 
    9439             :     /* Create the catalog entries for the constraint */
    9440        9528 :     flags = INDEX_CONSTR_CREATE_UPDATE_INDEX |
    9441             :         INDEX_CONSTR_CREATE_REMOVE_OLD_DEPS |
    9442       19056 :         (stmt->initdeferred ? INDEX_CONSTR_CREATE_INIT_DEFERRED : 0) |
    9443        9528 :         (stmt->deferrable ? INDEX_CONSTR_CREATE_DEFERRABLE : 0) |
    9444        9528 :         (stmt->primary ? INDEX_CONSTR_CREATE_MARK_AS_PRIMARY : 0);
    9445             : 
    9446        9528 :     address = index_constraint_create(rel,
    9447             :                                       index_oid,
    9448             :                                       InvalidOid,
    9449             :                                       indexInfo,
    9450             :                                       constraintName,
    9451             :                                       constraintType,
    9452             :                                       flags,
    9453             :                                       allowSystemTableMods,
    9454             :                                       false);   /* is_internal */
    9455             : 
    9456        9528 :     index_close(indexRel, NoLock);
    9457             : 
    9458        9528 :     return address;
    9459             : }
    9460             : 
    9461             : /*
    9462             :  * ALTER TABLE ADD CONSTRAINT
    9463             :  *
    9464             :  * Return value is the address of the new constraint; if no constraint was
    9465             :  * added, InvalidObjectAddress is returned.
    9466             :  */
    9467             : static ObjectAddress
    9468       11986 : ATExecAddConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
    9469             :                     Constraint *newConstraint, bool recurse, bool is_readd,
    9470             :                     LOCKMODE lockmode)
    9471             : {
    9472       11986 :     ObjectAddress address = InvalidObjectAddress;
    9473             : 
    9474             :     Assert(IsA(newConstraint, Constraint));
    9475             : 
    9476             :     /*
    9477             :      * Currently, we only expect to see CONSTR_CHECK, CONSTR_NOTNULL and
    9478             :      * CONSTR_FOREIGN nodes arriving here (see the preprocessing done in
    9479             :      * parse_utilcmd.c).
    9480             :      */
    9481       11986 :     switch (newConstraint->contype)
    9482             :     {
    9483        9468 :         case CONSTR_CHECK:
    9484             :         case CONSTR_NOTNULL:
    9485             :             address =
    9486        9468 :                 ATAddCheckNNConstraint(wqueue, tab, rel,
    9487             :                                        newConstraint, recurse, false, is_readd,
    9488             :                                        lockmode);
    9489        9342 :             break;
    9490             : 
    9491        2518 :         case CONSTR_FOREIGN:
    9492             : 
    9493             :             /*
    9494             :              * Assign or validate constraint name
    9495             :              */
    9496        2518 :             if (newConstraint->conname)
    9497             :             {
    9498        1172 :                 if (ConstraintNameIsUsed(CONSTRAINT_RELATION,
    9499             :                                          RelationGetRelid(rel),
    9500        1172 :                                          newConstraint->conname))
    9501           0 :                     ereport(ERROR,
    9502             :                             (errcode(ERRCODE_DUPLICATE_OBJECT),
    9503             :                              errmsg("constraint \"%s\" for relation \"%s\" already exists",
    9504             :                                     newConstraint->conname,
    9505             :                                     RelationGetRelationName(rel))));
    9506             :             }
    9507             :             else
    9508        1346 :                 newConstraint->conname =
    9509        1346 :                     ChooseConstraintName(RelationGetRelationName(rel),
    9510        1346 :                                          ChooseForeignKeyConstraintNameAddition(newConstraint->fk_attrs),
    9511             :                                          "fkey",
    9512        1346 :                                          RelationGetNamespace(rel),
    9513             :                                          NIL);
    9514             : 
    9515        2518 :             address = ATAddForeignKeyConstraint(wqueue, tab, rel,
    9516             :                                                 newConstraint,
    9517             :                                                 recurse, false,
    9518             :                                                 lockmode);
    9519        2006 :             break;
    9520             : 
    9521           0 :         default:
    9522           0 :             elog(ERROR, "unrecognized constraint type: %d",
    9523             :                  (int) newConstraint->contype);
    9524             :     }
    9525             : 
    9526       11348 :     return address;
    9527             : }
    9528             : 
    9529             : /*
    9530             :  * Generate the column-name portion of the constraint name for a new foreign
    9531             :  * key given the list of column names that reference the referenced
    9532             :  * table.  This will be passed to ChooseConstraintName along with the parent
    9533             :  * table name and the "fkey" suffix.
    9534             :  *
    9535             :  * We know that less than NAMEDATALEN characters will actually be used, so we
    9536             :  * can truncate the result once we've generated that many.
    9537             :  *
    9538             :  * XXX see also ChooseExtendedStatisticNameAddition and
    9539             :  * ChooseIndexNameAddition.
    9540             :  */
    9541             : static char *
    9542        2222 : ChooseForeignKeyConstraintNameAddition(List *colnames)
    9543             : {
    9544             :     char        buf[NAMEDATALEN * 2];
    9545        2222 :     int         buflen = 0;
    9546             :     ListCell   *lc;
    9547             : 
    9548        2222 :     buf[0] = '\0';
    9549        5004 :     foreach(lc, colnames)
    9550             :     {
    9551        2782 :         const char *name = strVal(lfirst(lc));
    9552             : 
    9553        2782 :         if (buflen > 0)
    9554         560 :             buf[buflen++] = '_';    /* insert _ between names */
    9555             : 
    9556             :         /*
    9557             :          * At this point we have buflen <= NAMEDATALEN.  name should be less
    9558             :          * than NAMEDATALEN already, but use strlcpy for paranoia.
    9559             :          */
    9560        2782 :         strlcpy(buf + buflen, name, NAMEDATALEN);
    9561        2782 :         buflen += strlen(buf + buflen);
    9562        2782 :         if (buflen >= NAMEDATALEN)
    9563           0 :             break;
    9564             :     }
    9565        2222 :     return pstrdup(buf);
    9566             : }
    9567             : 
    9568             : /*
    9569             :  * Add a check or not-null constraint to a single table and its children.
    9570             :  * Returns the address of the constraint added to the parent relation,
    9571             :  * if one gets added, or InvalidObjectAddress otherwise.
    9572             :  *
    9573             :  * Subroutine for ATExecAddConstraint.
    9574             :  *
    9575             :  * We must recurse to child tables during execution, rather than using
    9576             :  * ALTER TABLE's normal prep-time recursion.  The reason is that all the
    9577             :  * constraints *must* be given the same name, else they won't be seen as
    9578             :  * related later.  If the user didn't explicitly specify a name, then
    9579             :  * AddRelationNewConstraints would normally assign different names to the
    9580             :  * child constraints.  To fix that, we must capture the name assigned at
    9581             :  * the parent table and pass that down.
    9582             :  */
    9583             : static ObjectAddress
    9584       10280 : ATAddCheckNNConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
    9585             :                        Constraint *constr, bool recurse, bool recursing,
    9586             :                        bool is_readd, LOCKMODE lockmode)
    9587             : {
    9588             :     List       *newcons;
    9589             :     ListCell   *lcon;
    9590             :     List       *children;
    9591             :     ListCell   *child;
    9592       10280 :     ObjectAddress address = InvalidObjectAddress;
    9593             : 
    9594             :     /* Guard against stack overflow due to overly deep inheritance tree. */
    9595       10280 :     check_stack_depth();
    9596             : 
    9597             :     /* At top level, permission check was done in ATPrepCmd, else do it */
    9598       10280 :     if (recursing)
    9599         678 :         ATSimplePermissions(AT_AddConstraint, rel,
    9600             :                             ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
    9601             : 
    9602             :     /*
    9603             :      * Call AddRelationNewConstraints to do the work, making sure it works on
    9604             :      * a copy of the Constraint so transformExpr can't modify the original. It
    9605             :      * returns a list of cooked constraints.
    9606             :      *
    9607             :      * If the constraint ends up getting merged with a pre-existing one, it's
    9608             :      * omitted from the returned list, which is what we want: we do not need
    9609             :      * to do any validation work.  That can only happen at child tables,
    9610             :      * though, since we disallow merging at the top level.
    9611             :      */
    9612       10280 :     newcons = AddRelationNewConstraints(rel, NIL,
    9613       10280 :                                         list_make1(copyObject(constr)),
    9614       10280 :                                         recursing || is_readd,  /* allow_merge */
    9615       10280 :                                         !recursing, /* is_local */
    9616             :                                         is_readd,   /* is_internal */
    9617       10280 :                                         NULL);  /* queryString not available
    9618             :                                                  * here */
    9619             : 
    9620             :     /* we don't expect more than one constraint here */
    9621             :     Assert(list_length(newcons) <= 1);
    9622             : 
    9623             :     /* Add each to-be-validated constraint to Phase 3's queue */
    9624       19708 :     foreach(lcon, newcons)
    9625             :     {
    9626        9548 :         CookedConstraint *ccon = (CookedConstraint *) lfirst(lcon);
    9627             : 
    9628        9548 :         if (!ccon->skip_validation && ccon->contype != CONSTR_NOTNULL)
    9629             :         {
    9630             :             NewConstraint *newcon;
    9631             : 
    9632         832 :             newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
    9633         832 :             newcon->name = ccon->name;
    9634         832 :             newcon->contype = ccon->contype;
    9635         832 :             newcon->qual = ccon->expr;
    9636             : 
    9637         832 :             tab->constraints = lappend(tab->constraints, newcon);
    9638             :         }
    9639             : 
    9640             :         /* Save the actually assigned name if it was defaulted */
    9641        9548 :         if (constr->conname == NULL)
    9642        8128 :             constr->conname = ccon->name;
    9643             : 
    9644             :         /*
    9645             :          * If adding a not-null constraint, set the pg_attribute flag and tell
    9646             :          * phase 3 to verify existing rows, if needed.
    9647             :          */
    9648        9548 :         if (constr->contype == CONSTR_NOTNULL)
    9649        8240 :             set_attnotnull(wqueue, rel, ccon->attnum, lockmode);
    9650             : 
    9651        9548 :         ObjectAddressSet(address, ConstraintRelationId, ccon->conoid);
    9652             :     }
    9653             : 
    9654             :     /* At this point we must have a locked-down name to use */
    9655             :     Assert(newcons == NIL || constr->conname != NULL);
    9656             : 
    9657             :     /* Advance command counter in case same table is visited multiple times */
    9658       10160 :     CommandCounterIncrement();
    9659             : 
    9660             :     /*
    9661             :      * If the constraint got merged with an existing constraint, we're done.
    9662             :      * We mustn't recurse to child tables in this case, because they've
    9663             :      * already got the constraint, and visiting them again would lead to an
    9664             :      * incorrect value for coninhcount.
    9665             :      */
    9666       10160 :     if (newcons == NIL)
    9667         612 :         return address;
    9668             : 
    9669             :     /*
    9670             :      * If adding a NO INHERIT constraint, no need to find our children.
    9671             :      */
    9672        9548 :     if (constr->is_no_inherit)
    9673          72 :         return address;
    9674             : 
    9675             :     /*
    9676             :      * Propagate to children as appropriate.  Unlike most other ALTER
    9677             :      * routines, we have to do this one level of recursion at a time; we can't
    9678             :      * use find_all_inheritors to do it in one pass.
    9679             :      */
    9680             :     children =
    9681        9476 :         find_inheritance_children(RelationGetRelid(rel), lockmode);
    9682             : 
    9683             :     /*
    9684             :      * Check if ONLY was specified with ALTER TABLE.  If so, allow the
    9685             :      * constraint creation only if there are no children currently. Error out
    9686             :      * otherwise.
    9687             :      */
    9688        9476 :     if (!recurse && children != NIL)
    9689           6 :         ereport(ERROR,
    9690             :                 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    9691             :                  errmsg("constraint must be added to child tables too")));
    9692             : 
    9693             :     /*
    9694             :      * Recurse to create the constraint on each child.
    9695             :      */
    9696       10118 :     foreach(child, children)
    9697             :     {
    9698         678 :         Oid         childrelid = lfirst_oid(child);
    9699             :         Relation    childrel;
    9700             :         AlteredTableInfo *childtab;
    9701             : 
    9702             :         /* find_inheritance_children already got lock */
    9703         678 :         childrel = table_open(childrelid, NoLock);
    9704         678 :         CheckAlterTableIsSafe(childrel);
    9705             : 
    9706             :         /* Find or create work queue entry for this table */
    9707         678 :         childtab = ATGetQueueEntry(wqueue, childrel);
    9708             : 
    9709             :         /* Recurse to this child */
    9710         678 :         ATAddCheckNNConstraint(wqueue, childtab, childrel,
    9711             :                                constr, recurse, true, is_readd, lockmode);
    9712             : 
    9713         648 :         table_close(childrel, NoLock);
    9714             :     }
    9715             : 
    9716        9440 :     return address;
    9717             : }
    9718             : 
    9719             : /*
    9720             :  * Add a foreign-key constraint to a single table; return the new constraint's
    9721             :  * address.
    9722             :  *
    9723             :  * Subroutine for ATExecAddConstraint.  Must already hold exclusive
    9724             :  * lock on the rel, and have done appropriate validity checks for it.
    9725             :  * We do permissions checks here, however.
    9726             :  *
    9727             :  * When the referenced or referencing tables (or both) are partitioned,
    9728             :  * multiple pg_constraint rows are required -- one for each partitioned table
    9729             :  * and each partition on each side (fortunately, not one for every combination
    9730             :  * thereof).  We also need action triggers on each leaf partition on the
    9731             :  * referenced side, and check triggers on each leaf partition on the
    9732             :  * referencing side.
    9733             :  */
    9734             : static ObjectAddress
    9735        2518 : ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
    9736             :                           Constraint *fkconstraint,
    9737             :                           bool recurse, bool recursing, LOCKMODE lockmode)
    9738             : {
    9739             :     Relation    pkrel;
    9740        2518 :     int16       pkattnum[INDEX_MAX_KEYS] = {0};
    9741        2518 :     int16       fkattnum[INDEX_MAX_KEYS] = {0};
    9742        2518 :     Oid         pktypoid[INDEX_MAX_KEYS] = {0};
    9743        2518 :     Oid         fktypoid[INDEX_MAX_KEYS] = {0};
    9744        2518 :     Oid         pkcolloid[INDEX_MAX_KEYS] = {0};
    9745        2518 :     Oid         fkcolloid[INDEX_MAX_KEYS] = {0};
    9746        2518 :     Oid         opclasses[INDEX_MAX_KEYS] = {0};
    9747        2518 :     Oid         pfeqoperators[INDEX_MAX_KEYS] = {0};
    9748        2518 :     Oid         ppeqoperators[INDEX_MAX_KEYS] = {0};
    9749        2518 :     Oid         ffeqoperators[INDEX_MAX_KEYS] = {0};
    9750        2518 :     int16       fkdelsetcols[INDEX_MAX_KEYS] = {0};
    9751             :     bool        with_period;
    9752             :     bool        pk_has_without_overlaps;
    9753             :     int         i;
    9754             :     int         numfks,
    9755             :                 numpks,
    9756             :                 numfkdelsetcols;
    9757             :     Oid         indexOid;
    9758             :     bool        old_check_ok;
    9759             :     ObjectAddress address;
    9760        2518 :     ListCell   *old_pfeqop_item = list_head(fkconstraint->old_conpfeqop);
    9761             : 
    9762             :     /*
    9763             :      * Grab ShareRowExclusiveLock on the pk table, so that someone doesn't
    9764             :      * delete rows out from under us.
    9765             :      */
    9766        2518 :     if (OidIsValid(fkconstraint->old_pktable_oid))
    9767          72 :         pkrel = table_open(fkconstraint->old_pktable_oid, ShareRowExclusiveLock);
    9768             :     else
    9769        2446 :         pkrel = table_openrv(fkconstraint->pktable, ShareRowExclusiveLock);
    9770             : 
    9771             :     /*
    9772             :      * Validity checks (permission checks wait till we have the column
    9773             :      * numbers)
    9774             :      */
    9775        2518 :     if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
    9776             :     {
    9777         372 :         if (!recurse)
    9778           6 :             ereport(ERROR,
    9779             :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    9780             :                      errmsg("cannot use ONLY for foreign key on partitioned table \"%s\" referencing relation \"%s\"",
    9781             :                             RelationGetRelationName(rel),
    9782             :                             RelationGetRelationName(pkrel))));
    9783         366 :         if (fkconstraint->skip_validation && !fkconstraint->initially_valid)
    9784           6 :             ereport(ERROR,
    9785             :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    9786             :                      errmsg("cannot add NOT VALID foreign key on partitioned table \"%s\" referencing relation \"%s\"",
    9787             :                             RelationGetRelationName(rel),
    9788             :                             RelationGetRelationName(pkrel)),
    9789             :                      errdetail("This feature is not yet supported on partitioned tables.")));
    9790             :     }
    9791             : 
    9792        2506 :     if (pkrel->rd_rel->relkind != RELKIND_RELATION &&
    9793         326 :         pkrel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
    9794           0 :         ereport(ERROR,
    9795             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    9796             :                  errmsg("referenced relation \"%s\" is not a table",
    9797             :                         RelationGetRelationName(pkrel))));
    9798             : 
    9799        2506 :     if (!allowSystemTableMods && IsSystemRelation(pkrel))
    9800           2 :         ereport(ERROR,
    9801             :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
    9802             :                  errmsg("permission denied: \"%s\" is a system catalog",
    9803             :                         RelationGetRelationName(pkrel))));
    9804             : 
    9805             :     /*
    9806             :      * References from permanent or unlogged tables to temp tables, and from
    9807             :      * permanent tables to unlogged tables, are disallowed because the
    9808             :      * referenced data can vanish out from under us.  References from temp
    9809             :      * tables to any other table type are also disallowed, because other
    9810             :      * backends might need to run the RI triggers on the perm table, but they
    9811             :      * can't reliably see tuples in the local buffers of other backends.
    9812             :      */
    9813        2504 :     switch (rel->rd_rel->relpersistence)
    9814             :     {
    9815        2214 :         case RELPERSISTENCE_PERMANENT:
    9816        2214 :             if (!RelationIsPermanent(pkrel))
    9817           0 :                 ereport(ERROR,
    9818             :                         (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    9819             :                          errmsg("constraints on permanent tables may reference only permanent tables")));
    9820        2214 :             break;
    9821          12 :         case RELPERSISTENCE_UNLOGGED:
    9822          12 :             if (!RelationIsPermanent(pkrel)
    9823          12 :                 && pkrel->rd_rel->relpersistence != RELPERSISTENCE_UNLOGGED)
    9824           0 :                 ereport(ERROR,
    9825             :                         (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    9826             :                          errmsg("constraints on unlogged tables may reference only permanent or unlogged tables")));
    9827          12 :             break;
    9828         278 :         case RELPERSISTENCE_TEMP:
    9829         278 :             if (pkrel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
    9830           0 :                 ereport(ERROR,
    9831             :                         (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    9832             :                          errmsg("constraints on temporary tables may reference only temporary tables")));
    9833         278 :             if (!pkrel->rd_islocaltemp || !rel->rd_islocaltemp)
    9834           0 :                 ereport(ERROR,
    9835             :                         (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    9836             :                          errmsg("constraints on temporary tables must involve temporary tables of this session")));
    9837         278 :             break;
    9838             :     }
    9839             : 
    9840             :     /*
    9841             :      * Look up the referencing attributes to make sure they exist, and record
    9842             :      * their attnums and type and collation OIDs.
    9843             :      */
    9844        2504 :     numfks = transformColumnNameList(RelationGetRelid(rel),
    9845             :                                      fkconstraint->fk_attrs,
    9846             :                                      fkattnum, fktypoid, fkcolloid);
    9847        2474 :     with_period = fkconstraint->fk_with_period || fkconstraint->pk_with_period;
    9848        2474 :     if (with_period && !fkconstraint->fk_with_period)
    9849          24 :         ereport(ERROR,
    9850             :                 errcode(ERRCODE_INVALID_FOREIGN_KEY),
    9851             :                 errmsg("foreign key uses PERIOD on the referenced table but not the referencing table"));
    9852             : 
    9853        2450 :     numfkdelsetcols = transformColumnNameList(RelationGetRelid(rel),
    9854             :                                               fkconstraint->fk_del_set_cols,
    9855             :                                               fkdelsetcols, NULL, NULL);
    9856        2444 :     validateFkOnDeleteSetColumns(numfks, fkattnum,
    9857             :                                  numfkdelsetcols, fkdelsetcols,
    9858             :                                  fkconstraint->fk_del_set_cols);
    9859             : 
    9860             :     /*
    9861             :      * If the attribute list for the referenced table was omitted, lookup the
    9862             :      * definition of the primary key and use it.  Otherwise, validate the
    9863             :      * supplied attribute list.  In either case, discover the index OID and
    9864             :      * index opclasses, and the attnums and type and collation OIDs of the
    9865             :      * attributes.
    9866             :      */
    9867        2438 :     if (fkconstraint->pk_attrs == NIL)
    9868             :     {
    9869        1112 :         numpks = transformFkeyGetPrimaryKey(pkrel, &indexOid,
    9870             :                                             &fkconstraint->pk_attrs,
    9871             :                                             pkattnum, pktypoid, pkcolloid,
    9872             :                                             opclasses, &pk_has_without_overlaps);
    9873             : 
    9874             :         /* If the primary key uses WITHOUT OVERLAPS, the fk must use PERIOD */
    9875        1112 :         if (pk_has_without_overlaps && !fkconstraint->fk_with_period)
    9876          24 :             ereport(ERROR,
    9877             :                     errcode(ERRCODE_INVALID_FOREIGN_KEY),
    9878             :                     errmsg("foreign key uses PERIOD on the referenced table but not the referencing table"));
    9879             :     }
    9880             :     else
    9881             :     {
    9882        1326 :         numpks = transformColumnNameList(RelationGetRelid(pkrel),
    9883             :                                          fkconstraint->pk_attrs,
    9884             :                                          pkattnum, pktypoid, pkcolloid);
    9885             : 
    9886             :         /* Since we got pk_attrs, one should be a period. */
    9887        1296 :         if (with_period && !fkconstraint->pk_with_period)
    9888          24 :             ereport(ERROR,
    9889             :                     errcode(ERRCODE_INVALID_FOREIGN_KEY),
    9890             :                     errmsg("foreign key uses PERIOD on the referencing table but not the referenced table"));
    9891             : 
    9892             :         /* Look for an index matching the column list */
    9893        1272 :         indexOid = transformFkeyCheckAttrs(pkrel, numpks, pkattnum,
    9894             :                                            with_period, opclasses, &pk_has_without_overlaps);
    9895             :     }
    9896             : 
    9897             :     /*
    9898             :      * If the referenced primary key has WITHOUT OVERLAPS, the foreign key
    9899             :      * must use PERIOD.
    9900             :      */
    9901        2324 :     if (pk_has_without_overlaps && !with_period)
    9902          12 :         ereport(ERROR,
    9903             :                 errcode(ERRCODE_INVALID_FOREIGN_KEY),
    9904             :                 errmsg("foreign key must use PERIOD when referencing a primary using WITHOUT OVERLAPS"));
    9905             : 
    9906             :     /*
    9907             :      * Now we can check permissions.
    9908             :      */
    9909        2312 :     checkFkeyPermissions(pkrel, pkattnum, numpks);
    9910             : 
    9911             :     /*
    9912             :      * Check some things for generated columns.
    9913             :      */
    9914        5424 :     for (i = 0; i < numfks; i++)
    9915             :     {
    9916        3124 :         char        attgenerated = TupleDescAttr(RelationGetDescr(rel), fkattnum[i] - 1)->attgenerated;
    9917             : 
    9918        3124 :         if (attgenerated)
    9919             :         {
    9920             :             /*
    9921             :              * Check restrictions on UPDATE/DELETE actions, per SQL standard
    9922             :              */
    9923          30 :             if (fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETNULL ||
    9924          30 :                 fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETDEFAULT ||
    9925          30 :                 fkconstraint->fk_upd_action == FKCONSTR_ACTION_CASCADE)
    9926           6 :                 ereport(ERROR,
    9927             :                         (errcode(ERRCODE_SYNTAX_ERROR),
    9928             :                          errmsg("invalid %s action for foreign key constraint containing generated column",
    9929             :                                 "ON UPDATE")));
    9930          24 :             if (fkconstraint->fk_del_action == FKCONSTR_ACTION_SETNULL ||
    9931          18 :                 fkconstraint->fk_del_action == FKCONSTR_ACTION_SETDEFAULT)
    9932           6 :                 ereport(ERROR,
    9933             :                         (errcode(ERRCODE_SYNTAX_ERROR),
    9934             :                          errmsg("invalid %s action for foreign key constraint containing generated column",
    9935             :                                 "ON DELETE")));
    9936             :         }
    9937             :     }
    9938             : 
    9939             :     /*
    9940             :      * Some actions are currently unsupported for foreign keys using PERIOD.
    9941             :      */
    9942        2300 :     if (fkconstraint->fk_with_period)
    9943             :     {
    9944         262 :         if (fkconstraint->fk_upd_action == FKCONSTR_ACTION_CASCADE ||
    9945         244 :             fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETNULL ||
    9946         226 :             fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETDEFAULT)
    9947          54 :             ereport(ERROR,
    9948             :                     errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    9949             :                     errmsg("unsupported %s action for foreign key constraint using PERIOD",
    9950             :                            "ON UPDATE"));
    9951             : 
    9952         208 :         if (fkconstraint->fk_del_action == FKCONSTR_ACTION_CASCADE ||
    9953         208 :             fkconstraint->fk_del_action == FKCONSTR_ACTION_SETNULL ||
    9954         208 :             fkconstraint->fk_del_action == FKCONSTR_ACTION_SETDEFAULT)
    9955           0 :             ereport(ERROR,
    9956             :                     errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    9957             :                     errmsg("unsupported %s action for foreign key constraint using PERIOD",
    9958             :                            "ON DELETE"));
    9959             :     }
    9960             : 
    9961             :     /*
    9962             :      * Look up the equality operators to use in the constraint.
    9963             :      *
    9964             :      * Note that we have to be careful about the difference between the actual
    9965             :      * PK column type and the opclass' declared input type, which might be
    9966             :      * only binary-compatible with it.  The declared opcintype is the right
    9967             :      * thing to probe pg_amop with.
    9968             :      */
    9969        2246 :     if (numfks != numpks)
    9970           0 :         ereport(ERROR,
    9971             :                 (errcode(ERRCODE_INVALID_FOREIGN_KEY),
    9972             :                  errmsg("number of referencing and referenced columns for foreign key disagree")));
    9973             : 
    9974             :     /*
    9975             :      * On the strength of a previous constraint, we might avoid scanning
    9976             :      * tables to validate this one.  See below.
    9977             :      */
    9978        2246 :     old_check_ok = (fkconstraint->old_conpfeqop != NIL);
    9979             :     Assert(!old_check_ok || numfks == list_length(fkconstraint->old_conpfeqop));
    9980             : 
    9981        4866 :     for (i = 0; i < numpks; i++)
    9982             :     {
    9983        2860 :         Oid         pktype = pktypoid[i];
    9984        2860 :         Oid         fktype = fktypoid[i];
    9985             :         Oid         fktyped;
    9986        2860 :         Oid         pkcoll = pkcolloid[i];
    9987        2860 :         Oid         fkcoll = fkcolloid[i];
    9988             :         HeapTuple   cla_ht;
    9989             :         Form_pg_opclass cla_tup;
    9990             :         Oid         amid;
    9991             :         Oid         opfamily;
    9992             :         Oid         opcintype;
    9993             :         Oid         pfeqop;
    9994             :         Oid         ppeqop;
    9995             :         Oid         ffeqop;
    9996             :         int16       eqstrategy;
    9997             :         Oid         pfeqop_right;
    9998             : 
    9999             :         /* We need several fields out of the pg_opclass entry */
   10000        2860 :         cla_ht = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclasses[i]));
   10001        2860 :         if (!HeapTupleIsValid(cla_ht))
   10002           0 :             elog(ERROR, "cache lookup failed for opclass %u", opclasses[i]);
   10003        2860 :         cla_tup = (Form_pg_opclass) GETSTRUCT(cla_ht);
   10004        2860 :         amid = cla_tup->opcmethod;
   10005        2860 :         opfamily = cla_tup->opcfamily;
   10006        2860 :         opcintype = cla_tup->opcintype;
   10007        2860 :         ReleaseSysCache(cla_ht);
   10008             : 
   10009        2860 :         if (with_period)
   10010             :         {
   10011             :             CompareType cmptype;
   10012         444 :             bool        for_overlaps = with_period && i == numpks - 1;
   10013             : 
   10014             :             /*
   10015             :              * GiST indexes are required to support temporal foreign keys
   10016             :              * because they combine equals and overlaps.
   10017             :              */
   10018         444 :             if (amid != GIST_AM_OID)
   10019           0 :                 elog(ERROR, "only GiST indexes are supported for temporal foreign keys");
   10020             : 
   10021         444 :             cmptype = for_overlaps ? COMPARE_OVERLAP : COMPARE_EQ;
   10022             : 
   10023             :             /*
   10024             :              * An opclass can use whatever strategy numbers it wants, so we
   10025             :              * ask the opclass what number it actually uses instead of our RT*
   10026             :              * constants.
   10027             :              */
   10028         444 :             eqstrategy = GistTranslateStratnum(opclasses[i], cmptype);
   10029         444 :             if (eqstrategy == InvalidStrategy)
   10030             :             {
   10031             :                 HeapTuple   tuple;
   10032             : 
   10033           0 :                 tuple = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclasses[i]));
   10034           0 :                 if (!HeapTupleIsValid(tuple))
   10035           0 :                     elog(ERROR, "cache lookup failed for operator class %u", opclasses[i]);
   10036             : 
   10037           0 :                 ereport(ERROR,
   10038             :                         errcode(ERRCODE_UNDEFINED_OBJECT),
   10039             :                         for_overlaps
   10040             :                         ? errmsg("could not identify an overlaps operator for foreign key")
   10041             :                         : errmsg("could not identify an equality operator for foreign key"),
   10042             :                         errdetail("Could not translate compare type %d for operator class \"%s\" for access method \"%s\".",
   10043             :                                   cmptype, NameStr(((Form_pg_opclass) GETSTRUCT(tuple))->opcname), "gist"));
   10044             :             }
   10045             :         }
   10046             :         else
   10047             :         {
   10048             :             /*
   10049             :              * Check it's a btree; currently this can never fail since no
   10050             :              * other index AMs support unique indexes.  If we ever did have
   10051             :              * other types of unique indexes, we'd need a way to determine
   10052             :              * which operator strategy number is equality.  (We could use
   10053             :              * something like GistTranslateStratnum.)
   10054             :              */
   10055        2416 :             if (amid != BTREE_AM_OID)
   10056           0 :                 elog(ERROR, "only b-tree indexes are supported for foreign keys");
   10057        2416 :             eqstrategy = BTEqualStrategyNumber;
   10058             :         }
   10059             : 
   10060             :         /*
   10061             :          * There had better be a primary equality operator for the index.
   10062             :          * We'll use it for PK = PK comparisons.
   10063             :          */
   10064        2860 :         ppeqop = get_opfamily_member(opfamily, opcintype, opcintype,
   10065             :                                      eqstrategy);
   10066             : 
   10067        2860 :         if (!OidIsValid(ppeqop))
   10068           0 :             elog(ERROR, "missing operator %d(%u,%u) in opfamily %u",
   10069             :                  eqstrategy, opcintype, opcintype, opfamily);
   10070             : 
   10071             :         /*
   10072             :          * Are there equality operators that take exactly the FK type? Assume
   10073             :          * we should look through any domain here.
   10074             :          */
   10075        2860 :         fktyped = getBaseType(fktype);
   10076             : 
   10077        2860 :         pfeqop = get_opfamily_member(opfamily, opcintype, fktyped,
   10078             :                                      eqstrategy);
   10079        2860 :         if (OidIsValid(pfeqop))
   10080             :         {
   10081        2160 :             pfeqop_right = fktyped;
   10082        2160 :             ffeqop = get_opfamily_member(opfamily, fktyped, fktyped,
   10083             :                                          eqstrategy);
   10084             :         }
   10085             :         else
   10086             :         {
   10087             :             /* keep compiler quiet */
   10088         700 :             pfeqop_right = InvalidOid;
   10089         700 :             ffeqop = InvalidOid;
   10090             :         }
   10091             : 
   10092        2860 :         if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
   10093             :         {
   10094             :             /*
   10095             :              * Otherwise, look for an implicit cast from the FK type to the
   10096             :              * opcintype, and if found, use the primary equality operator.
   10097             :              * This is a bit tricky because opcintype might be a polymorphic
   10098             :              * type such as ANYARRAY or ANYENUM; so what we have to test is
   10099             :              * whether the two actual column types can be concurrently cast to
   10100             :              * that type.  (Otherwise, we'd fail to reject combinations such
   10101             :              * as int[] and point[].)
   10102             :              */
   10103             :             Oid         input_typeids[2];
   10104             :             Oid         target_typeids[2];
   10105             : 
   10106         700 :             input_typeids[0] = pktype;
   10107         700 :             input_typeids[1] = fktype;
   10108         700 :             target_typeids[0] = opcintype;
   10109         700 :             target_typeids[1] = opcintype;
   10110         700 :             if (can_coerce_type(2, input_typeids, target_typeids,
   10111             :                                 COERCION_IMPLICIT))
   10112             :             {
   10113         472 :                 pfeqop = ffeqop = ppeqop;
   10114         472 :                 pfeqop_right = opcintype;
   10115             :             }
   10116             :         }
   10117             : 
   10118        2860 :         if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
   10119         228 :             ereport(ERROR,
   10120             :                     (errcode(ERRCODE_DATATYPE_MISMATCH),
   10121             :                      errmsg("foreign key constraint \"%s\" cannot be implemented",
   10122             :                             fkconstraint->conname),
   10123             :                      errdetail("Key columns \"%s\" of the referencing table and \"%s\" of the referenced table "
   10124             :                                "are of incompatible types: %s and %s.",
   10125             :                                strVal(list_nth(fkconstraint->fk_attrs, i)),
   10126             :                                strVal(list_nth(fkconstraint->pk_attrs, i)),
   10127             :                                format_type_be(fktype),
   10128             :                                format_type_be(pktype))));
   10129             : 
   10130             :         /*
   10131             :          * This shouldn't be possible, but better check to make sure we have a
   10132             :          * consistent state for the check below.
   10133             :          */
   10134        2632 :         if ((OidIsValid(pkcoll) && !OidIsValid(fkcoll)) || (!OidIsValid(pkcoll) && OidIsValid(fkcoll)))
   10135           0 :             elog(ERROR, "key columns are not both collatable");
   10136             : 
   10137        2632 :         if (OidIsValid(pkcoll) && OidIsValid(fkcoll))
   10138             :         {
   10139             :             bool        pkcolldet;
   10140             :             bool        fkcolldet;
   10141             : 
   10142         104 :             pkcolldet = get_collation_isdeterministic(pkcoll);
   10143         104 :             fkcolldet = get_collation_isdeterministic(fkcoll);
   10144             : 
   10145             :             /*
   10146             :              * SQL requires that both collations are the same.  This is
   10147             :              * because we need a consistent notion of equality on both
   10148             :              * columns.  We relax this by allowing different collations if
   10149             :              * they are both deterministic.  (This is also for backward
   10150             :              * compatibility, because PostgreSQL has always allowed this.)
   10151             :              */
   10152         104 :             if ((!pkcolldet || !fkcolldet) && pkcoll != fkcoll)
   10153          12 :                 ereport(ERROR,
   10154             :                         (errcode(ERRCODE_COLLATION_MISMATCH),
   10155             :                          errmsg("foreign key constraint \"%s\" cannot be implemented", fkconstraint->conname),
   10156             :                          errdetail("Key columns \"%s\" of the referencing table and \"%s\" of the referenced table "
   10157             :                                    "have incompatible collations: \"%s\" and \"%s\".  "
   10158             :                                    "If either collation is nondeterministic, then both collations have to be the same.",
   10159             :                                    strVal(list_nth(fkconstraint->fk_attrs, i)),
   10160             :                                    strVal(list_nth(fkconstraint->pk_attrs, i)),
   10161             :                                    get_collation_name(fkcoll),
   10162             :                                    get_collation_name(pkcoll))));
   10163             :         }
   10164             : 
   10165        2620 :         if (old_check_ok)
   10166             :         {
   10167             :             /*
   10168             :              * When a pfeqop changes, revalidate the constraint.  We could
   10169             :              * permit intra-opfamily changes, but that adds subtle complexity
   10170             :              * without any concrete benefit for core types.  We need not
   10171             :              * assess ppeqop or ffeqop, which RI_Initial_Check() does not use.
   10172             :              */
   10173           6 :             old_check_ok = (pfeqop == lfirst_oid(old_pfeqop_item));
   10174           6 :             old_pfeqop_item = lnext(fkconstraint->old_conpfeqop,
   10175             :                                     old_pfeqop_item);
   10176             :         }
   10177        2620 :         if (old_check_ok)
   10178             :         {
   10179             :             Oid         old_fktype;
   10180             :             Oid         new_fktype;
   10181             :             CoercionPathType old_pathtype;
   10182             :             CoercionPathType new_pathtype;
   10183             :             Oid         old_castfunc;
   10184             :             Oid         new_castfunc;
   10185             :             Oid         old_fkcoll;
   10186             :             Oid         new_fkcoll;
   10187           6 :             Form_pg_attribute attr = TupleDescAttr(tab->oldDesc,
   10188           6 :                                                    fkattnum[i] - 1);
   10189             : 
   10190             :             /*
   10191             :              * Identify coercion pathways from each of the old and new FK-side
   10192             :              * column types to the right (foreign) operand type of the pfeqop.
   10193             :              * We may assume that pg_constraint.conkey is not changing.
   10194             :              */
   10195           6 :             old_fktype = attr->atttypid;
   10196           6 :             new_fktype = fktype;
   10197           6 :             old_pathtype = findFkeyCast(pfeqop_right, old_fktype,
   10198             :                                         &old_castfunc);
   10199           6 :             new_pathtype = findFkeyCast(pfeqop_right, new_fktype,
   10200             :                                         &new_castfunc);
   10201             : 
   10202           6 :             old_fkcoll = attr->attcollation;
   10203           6 :             new_fkcoll = fkcoll;
   10204             : 
   10205             :             /*
   10206             :              * Upon a change to the cast from the FK column to its pfeqop
   10207             :              * operand, revalidate the constraint.  For this evaluation, a
   10208             :              * binary coercion cast is equivalent to no cast at all.  While
   10209             :              * type implementors should design implicit casts with an eye
   10210             :              * toward consistency of operations like equality, we cannot
   10211             :              * assume here that they have done so.
   10212             :              *
   10213             :              * A function with a polymorphic argument could change behavior
   10214             :              * arbitrarily in response to get_fn_expr_argtype().  Therefore,
   10215             :              * when the cast destination is polymorphic, we only avoid
   10216             :              * revalidation if the input type has not changed at all.  Given
   10217             :              * just the core data types and operator classes, this requirement
   10218             :              * prevents no would-be optimizations.
   10219             :              *
   10220             :              * If the cast converts from a base type to a domain thereon, then
   10221             :              * that domain type must be the opcintype of the unique index.
   10222             :              * Necessarily, the primary key column must then be of the domain
   10223             :              * type.  Since the constraint was previously valid, all values on
   10224             :              * the foreign side necessarily exist on the primary side and in
   10225             :              * turn conform to the domain.  Consequently, we need not treat
   10226             :              * domains specially here.
   10227             :              *
   10228             :              * If the collation changes, revalidation is required, unless both
   10229             :              * collations are deterministic, because those share the same
   10230             :              * notion of equality (because texteq reduces to bitwise
   10231             :              * equality).
   10232             :              *
   10233             :              * We need not directly consider the PK type.  It's necessarily
   10234             :              * binary coercible to the opcintype of the unique index column,
   10235             :              * and ri_triggers.c will only deal with PK datums in terms of
   10236             :              * that opcintype.  Changing the opcintype also changes pfeqop.
   10237             :              */
   10238           6 :             old_check_ok = (new_pathtype == old_pathtype &&
   10239           6 :                             new_castfunc == old_castfunc &&
   10240           6 :                             (!IsPolymorphicType(pfeqop_right) ||
   10241          12 :                              new_fktype == old_fktype) &&
   10242           0 :                             (new_fkcoll == old_fkcoll ||
   10243           0 :                              (get_collation_isdeterministic(old_fkcoll) && get_collation_isdeterministic(new_fkcoll))));
   10244             :         }
   10245             : 
   10246        2620 :         pfeqoperators[i] = pfeqop;
   10247        2620 :         ppeqoperators[i] = ppeqop;
   10248        2620 :         ffeqoperators[i] = ffeqop;
   10249             :     }
   10250             : 
   10251             :     /*
   10252             :      * For FKs with PERIOD we need additional operators to check whether the
   10253             :      * referencing row's range is contained by the aggregated ranges of the
   10254             :      * referenced row(s). For rangetypes and multirangetypes this is
   10255             :      * fk.periodatt <@ range_agg(pk.periodatt). Those are the only types we
   10256             :      * support for now. FKs will look these up at "runtime", but we should
   10257             :      * make sure the lookup works here, even if we don't use the values.
   10258             :      */
   10259        2006 :     if (with_period)
   10260             :     {
   10261             :         Oid         periodoperoid;
   10262             :         Oid         aggedperiodoperoid;
   10263             : 
   10264         190 :         FindFKPeriodOpers(opclasses[numpks - 1], &periodoperoid, &aggedperiodoperoid);
   10265             :     }
   10266             : 
   10267             :     /* First, create the constraint catalog entry itself. */
   10268        2006 :     address = addFkConstraint(addFkBothSides,
   10269             :                               fkconstraint->conname, fkconstraint, rel, pkrel,
   10270             :                               indexOid,
   10271             :                               InvalidOid,   /* no parent constraint */
   10272             :                               numfks,
   10273             :                               pkattnum,
   10274             :                               fkattnum,
   10275             :                               pfeqoperators,
   10276             :                               ppeqoperators,
   10277             :                               ffeqoperators,
   10278             :                               numfkdelsetcols,
   10279             :                               fkdelsetcols,
   10280             :                               false,
   10281             :                               with_period);
   10282             : 
   10283             :     /* Next process the action triggers at the referenced side and recurse */
   10284        2006 :     addFkRecurseReferenced(fkconstraint, rel, pkrel,
   10285             :                            indexOid,
   10286             :                            address.objectId,
   10287             :                            numfks,
   10288             :                            pkattnum,
   10289             :                            fkattnum,
   10290             :                            pfeqoperators,
   10291             :                            ppeqoperators,
   10292             :                            ffeqoperators,
   10293             :                            numfkdelsetcols,
   10294             :                            fkdelsetcols,
   10295             :                            old_check_ok,
   10296             :                            InvalidOid, InvalidOid,
   10297             :                            with_period);
   10298             : 
   10299             :     /* Lastly create the check triggers at the referencing side and recurse */
   10300        2006 :     addFkRecurseReferencing(wqueue, fkconstraint, rel, pkrel,
   10301             :                             indexOid,
   10302             :                             address.objectId,
   10303             :                             numfks,
   10304             :                             pkattnum,
   10305             :                             fkattnum,
   10306             :                             pfeqoperators,
   10307             :                             ppeqoperators,
   10308             :                             ffeqoperators,
   10309             :                             numfkdelsetcols,
   10310             :                             fkdelsetcols,
   10311             :                             old_check_ok,
   10312             :                             lockmode,
   10313             :                             InvalidOid, InvalidOid,
   10314             :                             with_period);
   10315             : 
   10316             :     /*
   10317             :      * Done.  Close pk table, but keep lock until we've committed.
   10318             :      */
   10319        2006 :     table_close(pkrel, NoLock);
   10320             : 
   10321        2006 :     return address;
   10322             : }
   10323             : 
   10324             : /*
   10325             :  * validateFkOnDeleteSetColumns
   10326             :  *      Verifies that columns used in ON DELETE SET NULL/DEFAULT (...)
   10327             :  *      column lists are valid.
   10328             :  */
   10329             : void
   10330        2444 : validateFkOnDeleteSetColumns(int numfks, const int16 *fkattnums,
   10331             :                              int numfksetcols, const int16 *fksetcolsattnums,
   10332             :                              List *fksetcols)
   10333             : {
   10334        2468 :     for (int i = 0; i < numfksetcols; i++)
   10335             :     {
   10336          30 :         int16       setcol_attnum = fksetcolsattnums[i];
   10337          30 :         bool        seen = false;
   10338             : 
   10339          54 :         for (int j = 0; j < numfks; j++)
   10340             :         {
   10341          48 :             if (fkattnums[j] == setcol_attnum)
   10342             :             {
   10343          24 :                 seen = true;
   10344          24 :                 break;
   10345             :             }
   10346             :         }
   10347             : 
   10348          30 :         if (!seen)
   10349             :         {
   10350           6 :             char       *col = strVal(list_nth(fksetcols, i));
   10351             : 
   10352           6 :             ereport(ERROR,
   10353             :                     (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
   10354             :                      errmsg("column \"%s\" referenced in ON DELETE SET action must be part of foreign key", col)));
   10355             :         }
   10356             :     }
   10357        2438 : }
   10358             : 
   10359             : /*
   10360             :  * addFkConstraint
   10361             :  *      Install pg_constraint entries to implement a foreign key constraint.
   10362             :  *      Caller must separately invoke addFkRecurseReferenced and
   10363             :  *      addFkRecurseReferencing, as appropriate, to install pg_trigger entries
   10364             :  *      and (for partitioned tables) recurse to partitions.
   10365             :  *
   10366             :  * fkside: the side of the FK (or both) to create.  Caller should
   10367             :  *      call addFkRecurseReferenced if this is addFkReferencedSide,
   10368             :  *      addFkRecurseReferencing if it's addFkReferencingSide, or both if it's
   10369             :  *      addFkBothSides.
   10370             :  * constraintname: the base name for the constraint being added,
   10371             :  *      copied to fkconstraint->conname if the latter is not set
   10372             :  * fkconstraint: the constraint being added
   10373             :  * rel: the root referencing relation
   10374             :  * pkrel: the referenced relation; might be a partition, if recursing
   10375             :  * indexOid: the OID of the index (on pkrel) implementing this constraint
   10376             :  * parentConstr: the OID of a parent constraint; InvalidOid if this is a
   10377             :  *      top-level constraint
   10378             :  * numfks: the number of columns in the foreign key
   10379             :  * pkattnum: the attnum array of referenced attributes
   10380             :  * fkattnum: the attnum array of referencing attributes
   10381             :  * pf/pp/ffeqoperators: OID array of operators between columns
   10382             :  * numfkdelsetcols: the number of columns in the ON DELETE SET NULL/DEFAULT
   10383             :  *      (...) clause
   10384             :  * fkdelsetcols: the attnum array of the columns in the ON DELETE SET
   10385             :  *      NULL/DEFAULT clause
   10386             :  * with_period: true if this is a temporal FK
   10387             :  */
   10388             : static ObjectAddress
   10389        3658 : addFkConstraint(addFkConstraintSides fkside,
   10390             :                 char *constraintname, Constraint *fkconstraint,
   10391             :                 Relation rel, Relation pkrel, Oid indexOid, Oid parentConstr,
   10392             :                 int numfks, int16 *pkattnum,
   10393             :                 int16 *fkattnum, Oid *pfeqoperators, Oid *ppeqoperators,
   10394             :                 Oid *ffeqoperators, int numfkdelsetcols, int16 *fkdelsetcols,
   10395             :                 bool is_internal, bool with_period)
   10396             : {
   10397             :     ObjectAddress address;
   10398             :     Oid         constrOid;
   10399             :     char       *conname;
   10400             :     bool        conislocal;
   10401             :     int16       coninhcount;
   10402             :     bool        connoinherit;
   10403             : 
   10404             :     /*
   10405             :      * Verify relkind for each referenced partition.  At the top level, this
   10406             :      * is redundant with a previous check, but we need it when recursing.
   10407             :      */
   10408        3658 :     if (pkrel->rd_rel->relkind != RELKIND_RELATION &&
   10409         784 :         pkrel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
   10410           0 :         ereport(ERROR,
   10411             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   10412             :                  errmsg("referenced relation \"%s\" is not a table",
   10413             :                         RelationGetRelationName(pkrel))));
   10414             : 
   10415             :     /*
   10416             :      * Caller supplies us with a constraint name; however, it may be used in
   10417             :      * this partition, so come up with a different one in that case.
   10418             :      */
   10419        3658 :     if (ConstraintNameIsUsed(CONSTRAINT_RELATION,
   10420             :                              RelationGetRelid(rel),
   10421             :                              constraintname))
   10422         876 :         conname = ChooseConstraintName(RelationGetRelationName(rel),
   10423         876 :                                        ChooseForeignKeyConstraintNameAddition(fkconstraint->fk_attrs),
   10424             :                                        "fkey",
   10425         876 :                                        RelationGetNamespace(rel), NIL);
   10426             :     else
   10427        2782 :         conname = constraintname;
   10428             : 
   10429        3658 :     if (fkconstraint->conname == NULL)
   10430         400 :         fkconstraint->conname = pstrdup(conname);
   10431             : 
   10432        3658 :     if (OidIsValid(parentConstr))
   10433             :     {
   10434        1652 :         conislocal = false;
   10435        1652 :         coninhcount = 1;
   10436        1652 :         connoinherit = false;
   10437             :     }
   10438             :     else
   10439             :     {
   10440        2006 :         conislocal = true;
   10441        2006 :         coninhcount = 0;
   10442             : 
   10443             :         /*
   10444             :          * always inherit for partitioned tables, never for legacy inheritance
   10445             :          */
   10446        2006 :         connoinherit = rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE;
   10447             :     }
   10448             : 
   10449             :     /*
   10450             :      * Record the FK constraint in pg_constraint.
   10451             :      */
   10452        3658 :     constrOid = CreateConstraintEntry(conname,
   10453        3658 :                                       RelationGetNamespace(rel),
   10454             :                                       CONSTRAINT_FOREIGN,
   10455        3658 :                                       fkconstraint->deferrable,
   10456        3658 :                                       fkconstraint->initdeferred,
   10457             :                                       true, /* Is Enforced */
   10458        3658 :                                       fkconstraint->initially_valid,
   10459             :                                       parentConstr,
   10460             :                                       RelationGetRelid(rel),
   10461             :                                       fkattnum,
   10462             :                                       numfks,
   10463             :                                       numfks,
   10464             :                                       InvalidOid,   /* not a domain constraint */
   10465             :                                       indexOid,
   10466             :                                       RelationGetRelid(pkrel),
   10467             :                                       pkattnum,
   10468             :                                       pfeqoperators,
   10469             :                                       ppeqoperators,
   10470             :                                       ffeqoperators,
   10471             :                                       numfks,
   10472        3658 :                                       fkconstraint->fk_upd_action,
   10473        3658 :                                       fkconstraint->fk_del_action,
   10474             :                                       fkdelsetcols,
   10475             :                                       numfkdelsetcols,
   10476        3658 :                                       fkconstraint->fk_matchtype,
   10477             :                                       NULL, /* no exclusion constraint */
   10478             :                                       NULL, /* no check constraint */
   10479             :                                       NULL,
   10480             :                                       conislocal,   /* islocal */
   10481             :                                       coninhcount,  /* inhcount */
   10482             :                                       connoinherit, /* conNoInherit */
   10483             :                                       with_period,  /* conPeriod */
   10484             :                                       is_internal); /* is_internal */
   10485             : 
   10486        3658 :     ObjectAddressSet(address, ConstraintRelationId, constrOid);
   10487             : 
   10488             :     /*
   10489             :      * In partitioning cases, create the dependency entries for this
   10490             :      * constraint.  (For non-partitioned cases, relevant entries were created
   10491             :      * by CreateConstraintEntry.)
   10492             :      *
   10493             :      * On the referenced side, we need the constraint to have an internal
   10494             :      * dependency on its parent constraint; this means that this constraint
   10495             :      * cannot be dropped on its own -- only through the parent constraint. It
   10496             :      * also means the containing partition cannot be dropped on its own, but
   10497             :      * it can be detached, at which point this dependency is removed (after
   10498             :      * verifying that no rows are referenced via this FK.)
   10499             :      *
   10500             :      * When processing the referencing side, we link the constraint via the
   10501             :      * special partitioning dependencies: the parent constraint is the primary
   10502             :      * dependent, and the partition on which the foreign key exists is the
   10503             :      * secondary dependency.  That way, this constraint is dropped if either
   10504             :      * of these objects is.
   10505             :      *
   10506             :      * Note that this is only necessary for the subsidiary pg_constraint rows
   10507             :      * in partitions; the topmost row doesn't need any of this.
   10508             :      */
   10509        3658 :     if (OidIsValid(parentConstr))
   10510             :     {
   10511             :         ObjectAddress referenced;
   10512             : 
   10513        1652 :         ObjectAddressSet(referenced, ConstraintRelationId, parentConstr);
   10514             : 
   10515             :         Assert(fkside != addFkBothSides);
   10516        1652 :         if (fkside == addFkReferencedSide)
   10517         870 :             recordDependencyOn(&address, &referenced, DEPENDENCY_INTERNAL);
   10518             :         else
   10519             :         {
   10520         782 :             recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_PRI);
   10521         782 :             ObjectAddressSet(referenced, RelationRelationId, RelationGetRelid(rel));
   10522         782 :             recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_SEC);
   10523             :         }
   10524             :     }
   10525             : 
   10526             :     /* make new constraint visible, in case we add more */
   10527        3658 :     CommandCounterIncrement();
   10528             : 
   10529        3658 :     return address;
   10530             : }
   10531             : 
   10532             : /*
   10533             :  * addFkRecurseReferenced
   10534             :  *      Recursive helper for the referenced side of foreign key creation,
   10535             :  *      which creates the action triggers and recurses
   10536             :  *
   10537             :  * If the referenced relation is a plain relation, create the necessary action
   10538             :  * triggers that implement the constraint.  If the referenced relation is a
   10539             :  * partitioned table, then we create a pg_constraint row referencing the parent
   10540             :  * of the referencing side for it and recurse on this routine for each
   10541             :  * partition.
   10542             :  *
   10543             :  * fkconstraint: the constraint being added
   10544             :  * rel: the root referencing relation
   10545             :  * pkrel: the referenced relation; might be a partition, if recursing
   10546             :  * indexOid: the OID of the index (on pkrel) implementing this constraint
   10547             :  * parentConstr: the OID of a parent constraint; InvalidOid if this is a
   10548             :  *      top-level constraint
   10549             :  * numfks: the number of columns in the foreign key
   10550             :  * pkattnum: the attnum array of referenced attributes
   10551             :  * fkattnum: the attnum array of referencing attributes
   10552             :  * numfkdelsetcols: the number of columns in the ON DELETE SET
   10553             :  *      NULL/DEFAULT (...) clause
   10554             :  * fkdelsetcols: the attnum array of the columns in the ON DELETE SET
   10555             :  *      NULL/DEFAULT clause
   10556             :  * pf/pp/ffeqoperators: OID array of operators between columns
   10557             :  * old_check_ok: true if this constraint replaces an existing one that
   10558             :  *      was already validated (thus this one doesn't need validation)
   10559             :  * parentDelTrigger and parentUpdTrigger: when recursively called on a
   10560             :  *      partition, the OIDs of the parent action triggers for DELETE and
   10561             :  *      UPDATE respectively.
   10562             :  * with_period: true if this is a temporal FK
   10563             :  */
   10564             : static void
   10565        2948 : addFkRecurseReferenced(Constraint *fkconstraint, Relation rel,
   10566             :                        Relation pkrel, Oid indexOid, Oid parentConstr,
   10567             :                        int numfks,
   10568             :                        int16 *pkattnum, int16 *fkattnum, Oid *pfeqoperators,
   10569             :                        Oid *ppeqoperators, Oid *ffeqoperators,
   10570             :                        int numfkdelsetcols, int16 *fkdelsetcols,
   10571             :                        bool old_check_ok,
   10572             :                        Oid parentDelTrigger, Oid parentUpdTrigger,
   10573             :                        bool with_period)
   10574             : {
   10575             :     Oid         deleteTriggerOid,
   10576             :                 updateTriggerOid;
   10577             : 
   10578             :     Assert(CheckRelationLockedByMe(pkrel, ShareRowExclusiveLock, true));
   10579             :     Assert(CheckRelationLockedByMe(rel, ShareRowExclusiveLock, true));
   10580             : 
   10581             :     /*
   10582             :      * Create the action triggers that enforce the constraint.
   10583             :      */
   10584        2948 :     createForeignKeyActionTriggers(rel, RelationGetRelid(pkrel),
   10585             :                                    fkconstraint,
   10586             :                                    parentConstr, indexOid,
   10587             :                                    parentDelTrigger, parentUpdTrigger,
   10588             :                                    &deleteTriggerOid, &updateTriggerOid);
   10589             : 
   10590             :     /*
   10591             :      * If the referenced table is partitioned, recurse on ourselves to handle
   10592             :      * each partition.  We need one pg_constraint row created for each
   10593             :      * partition in addition to the pg_constraint row for the parent table.
   10594             :      */
   10595        2948 :     if (pkrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
   10596             :     {
   10597         462 :         PartitionDesc pd = RelationGetPartitionDesc(pkrel, true);
   10598             : 
   10599        1206 :         for (int i = 0; i < pd->nparts; i++)
   10600             :         {
   10601             :             Relation    partRel;
   10602             :             AttrMap    *map;
   10603             :             AttrNumber *mapped_pkattnum;
   10604             :             Oid         partIndexId;
   10605             :             ObjectAddress address;
   10606             : 
   10607             :             /* XXX would it be better to acquire these locks beforehand? */
   10608         744 :             partRel = table_open(pd->oids[i], ShareRowExclusiveLock);
   10609             : 
   10610             :             /*
   10611             :              * Map the attribute numbers in the referenced side of the FK
   10612             :              * definition to match the partition's column layout.
   10613             :              */
   10614         744 :             map = build_attrmap_by_name_if_req(RelationGetDescr(partRel),
   10615             :                                                RelationGetDescr(pkrel),
   10616             :                                                false);
   10617         744 :             if (map)
   10618             :             {
   10619          58 :                 mapped_pkattnum = palloc(sizeof(AttrNumber) * numfks);
   10620         128 :                 for (int j = 0; j < numfks; j++)
   10621          70 :                     mapped_pkattnum[j] = map->attnums[pkattnum[j] - 1];
   10622             :             }
   10623             :             else
   10624         686 :                 mapped_pkattnum = pkattnum;
   10625             : 
   10626             :             /* Determine the index to use at this level */
   10627         744 :             partIndexId = index_get_partition(partRel, indexOid);
   10628         744 :             if (!OidIsValid(partIndexId))
   10629           0 :                 elog(ERROR, "index for %u not found in partition %s",
   10630             :                      indexOid, RelationGetRelationName(partRel));
   10631             : 
   10632             :             /* Create entry at this level ... */
   10633         744 :             address = addFkConstraint(addFkReferencedSide,
   10634             :                                       fkconstraint->conname, fkconstraint, rel,
   10635             :                                       partRel, partIndexId, parentConstr,
   10636             :                                       numfks, mapped_pkattnum,
   10637             :                                       fkattnum, pfeqoperators, ppeqoperators,
   10638             :                                       ffeqoperators, numfkdelsetcols,
   10639             :                                       fkdelsetcols, true, with_period);
   10640             :             /* ... and recurse to our children */
   10641         744 :             addFkRecurseReferenced(fkconstraint, rel, partRel,
   10642             :                                    partIndexId, address.objectId, numfks,
   10643             :                                    mapped_pkattnum, fkattnum,
   10644             :                                    pfeqoperators, ppeqoperators, ffeqoperators,
   10645             :                                    numfkdelsetcols, fkdelsetcols,
   10646             :                                    old_check_ok,
   10647             :                                    deleteTriggerOid, updateTriggerOid,
   10648             :                                    with_period);
   10649             : 
   10650             :             /* Done -- clean up (but keep the lock) */
   10651         744 :             table_close(partRel, NoLock);
   10652         744 :             if (map)
   10653             :             {
   10654          58 :                 pfree(mapped_pkattnum);
   10655          58 :                 free_attrmap(map);
   10656             :             }
   10657             :         }
   10658             :     }
   10659        2948 : }
   10660             : 
   10661             : /*
   10662             :  * addFkRecurseReferencing
   10663             :  *      Recursive helper for the referencing side of foreign key creation,
   10664             :  *      which creates the check triggers and recurses
   10665             :  *
   10666             :  * If the referencing relation is a plain relation, create the necessary check
   10667             :  * triggers that implement the constraint, and set up for Phase 3 constraint
   10668             :  * verification.  If the referencing relation is a partitioned table, then
   10669             :  * we create a pg_constraint row for it and recurse on this routine for each
   10670             :  * partition.
   10671             :  *
   10672             :  * We assume that the referenced relation is locked against concurrent
   10673             :  * deletions.  If it's a partitioned relation, every partition must be so
   10674             :  * locked.
   10675             :  *
   10676             :  * wqueue: the ALTER TABLE work queue; NULL when not running as part
   10677             :  *      of an ALTER TABLE sequence.
   10678             :  * fkconstraint: the constraint being added
   10679             :  * rel: the referencing relation; might be a partition, if recursing
   10680             :  * pkrel: the root referenced relation
   10681             :  * indexOid: the OID of the index (on pkrel) implementing this constraint
   10682             :  * parentConstr: the OID of the parent constraint (there is always one)
   10683             :  * numfks: the number of columns in the foreign key
   10684             :  * pkattnum: the attnum array of referenced attributes
   10685             :  * fkattnum: the attnum array of referencing attributes
   10686             :  * pf/pp/ffeqoperators: OID array of operators between columns
   10687             :  * numfkdelsetcols: the number of columns in the ON DELETE SET NULL/DEFAULT
   10688             :  *      (...) clause
   10689             :  * fkdelsetcols: the attnum array of the columns in the ON DELETE SET
   10690             :  *      NULL/DEFAULT clause
   10691             :  * old_check_ok: true if this constraint replaces an existing one that
   10692             :  *      was already validated (thus this one doesn't need validation)
   10693             :  * lockmode: the lockmode to acquire on partitions when recursing
   10694             :  * parentInsTrigger and parentUpdTrigger: when being recursively called on
   10695             :  *      a partition, the OIDs of the parent check triggers for INSERT and
   10696             :  *      UPDATE respectively.
   10697             :  * with_period: true if this is a temporal FK
   10698             :  */
   10699             : static void
   10700        2788 : addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint, Relation rel,
   10701             :                         Relation pkrel, Oid indexOid, Oid parentConstr,
   10702             :                         int numfks, int16 *pkattnum, int16 *fkattnum,
   10703             :                         Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators,
   10704             :                         int numfkdelsetcols, int16 *fkdelsetcols,
   10705             :                         bool old_check_ok, LOCKMODE lockmode,
   10706             :                         Oid parentInsTrigger, Oid parentUpdTrigger,
   10707             :                         bool with_period)
   10708             : {
   10709             :     Oid         insertTriggerOid,
   10710             :                 updateTriggerOid;
   10711             : 
   10712             :     Assert(OidIsValid(parentConstr));
   10713             :     Assert(CheckRelationLockedByMe(rel, ShareRowExclusiveLock, true));
   10714             :     Assert(CheckRelationLockedByMe(pkrel, ShareRowExclusiveLock, true));
   10715             : 
   10716        2788 :     if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
   10717           0 :         ereport(ERROR,
   10718             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   10719             :                  errmsg("foreign key constraints are not supported on foreign tables")));
   10720             : 
   10721             :     /*
   10722             :      * Add the check triggers to it and, if necessary, schedule it to be
   10723             :      * checked in Phase 3.
   10724             :      *
   10725             :      * If the relation is partitioned, drill down to do it to its partitions.
   10726             :      */
   10727        2788 :     createForeignKeyCheckTriggers(RelationGetRelid(rel),
   10728             :                                   RelationGetRelid(pkrel),
   10729             :                                   fkconstraint,
   10730             :                                   parentConstr,
   10731             :                                   indexOid,
   10732             :                                   parentInsTrigger, parentUpdTrigger,
   10733             :                                   &insertTriggerOid, &updateTriggerOid);
   10734             : 
   10735        2788 :     if (rel->rd_rel->relkind == RELKIND_RELATION)
   10736             :     {
   10737             :         /*
   10738             :          * Tell Phase 3 to check that the constraint is satisfied by existing
   10739             :          * rows. We can skip this during table creation, when requested
   10740             :          * explicitly by specifying NOT VALID in an ADD FOREIGN KEY command,
   10741             :          * and when we're recreating a constraint following a SET DATA TYPE
   10742             :          * operation that did not impugn its validity.
   10743             :          */
   10744        2340 :         if (wqueue && !old_check_ok && !fkconstraint->skip_validation)
   10745             :         {
   10746             :             NewConstraint *newcon;
   10747             :             AlteredTableInfo *tab;
   10748             : 
   10749         816 :             tab = ATGetQueueEntry(wqueue, rel);
   10750             : 
   10751         816 :             newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
   10752         816 :             newcon->name = get_constraint_name(parentConstr);
   10753         816 :             newcon->contype = CONSTR_FOREIGN;
   10754         816 :             newcon->refrelid = RelationGetRelid(pkrel);
   10755         816 :             newcon->refindid = indexOid;
   10756         816 :             newcon->conid = parentConstr;
   10757         816 :             newcon->conwithperiod = fkconstraint->fk_with_period;
   10758         816 :             newcon->qual = (Node *) fkconstraint;
   10759             : 
   10760         816 :             tab->constraints = lappend(tab->constraints, newcon);
   10761             :         }
   10762             :     }
   10763         448 :     else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
   10764             :     {
   10765         448 :         PartitionDesc pd = RelationGetPartitionDesc(rel, true);
   10766             :         Relation    trigrel;
   10767             : 
   10768             :         /*
   10769             :          * Triggers of the foreign keys will be manipulated a bunch of times
   10770             :          * in the loop below.  To avoid repeatedly opening/closing the trigger
   10771             :          * catalog relation, we open it here and pass it to the subroutines
   10772             :          * called below.
   10773             :          */
   10774         448 :         trigrel = table_open(TriggerRelationId, RowExclusiveLock);
   10775             : 
   10776             :         /*
   10777             :          * Recurse to take appropriate action on each partition; either we
   10778             :          * find an existing constraint to reparent to ours, or we create a new
   10779             :          * one.
   10780             :          */
   10781         842 :         for (int i = 0; i < pd->nparts; i++)
   10782             :         {
   10783         400 :             Oid         partitionId = pd->oids[i];
   10784         400 :             Relation    partition = table_open(partitionId, lockmode);
   10785             :             List       *partFKs;
   10786             :             AttrMap    *attmap;
   10787             :             AttrNumber  mapped_fkattnum[INDEX_MAX_KEYS];
   10788             :             bool        attached;
   10789             :             ObjectAddress address;
   10790             :             ListCell   *cell;
   10791             : 
   10792         400 :             CheckAlterTableIsSafe(partition);
   10793             : 
   10794         394 :             attmap = build_attrmap_by_name(RelationGetDescr(partition),
   10795             :                                            RelationGetDescr(rel),
   10796             :                                            false);
   10797        1022 :             for (int j = 0; j < numfks; j++)
   10798         628 :                 mapped_fkattnum[j] = attmap->attnums[fkattnum[j] - 1];
   10799             : 
   10800             :             /* Check whether an existing constraint can be repurposed */
   10801         394 :             partFKs = copyObject(RelationGetFKeyList(partition));
   10802         394 :             attached = false;
   10803         412 :             foreach(cell, partFKs)
   10804             :             {
   10805             :                 ForeignKeyCacheInfo *fk;
   10806             : 
   10807          30 :                 fk = lfirst_node(ForeignKeyCacheInfo, cell);
   10808          30 :                 if (tryAttachPartitionForeignKey(fk,
   10809             :                                                  partitionId,
   10810             :                                                  parentConstr,
   10811             :                                                  numfks,
   10812             :                                                  mapped_fkattnum,
   10813             :                                                  pkattnum,
   10814             :                                                  pfeqoperators,
   10815             :                                                  insertTriggerOid,
   10816             :                                                  updateTriggerOid,
   10817             :                                                  trigrel))
   10818             :                 {
   10819          12 :                     attached = true;
   10820          12 :                     break;
   10821             :                 }
   10822             :             }
   10823         394 :             if (attached)
   10824             :             {
   10825          12 :                 table_close(partition, NoLock);
   10826          12 :                 continue;
   10827             :             }
   10828             : 
   10829             :             /*
   10830             :              * No luck finding a good constraint to reuse; create our own.
   10831             :              */
   10832         382 :             address = addFkConstraint(addFkReferencingSide,
   10833             :                                       fkconstraint->conname, fkconstraint,
   10834             :                                       partition, pkrel, indexOid, parentConstr,
   10835             :                                       numfks, pkattnum,
   10836             :                                       mapped_fkattnum, pfeqoperators,
   10837             :                                       ppeqoperators, ffeqoperators,
   10838             :                                       numfkdelsetcols, fkdelsetcols, true,
   10839             :                                       with_period);
   10840             : 
   10841             :             /* call ourselves to finalize the creation and we're done */
   10842         382 :             addFkRecurseReferencing(wqueue, fkconstraint, partition, pkrel,
   10843             :                                     indexOid,
   10844             :                                     address.objectId,
   10845             :                                     numfks,
   10846             :                                     pkattnum,
   10847             :                                     mapped_fkattnum,
   10848             :                                     pfeqoperators,
   10849             :                                     ppeqoperators,
   10850             :                                     ffeqoperators,
   10851             :                                     numfkdelsetcols,
   10852             :                                     fkdelsetcols,
   10853             :                                     old_check_ok,
   10854             :                                     lockmode,
   10855             :                                     insertTriggerOid,
   10856             :                                     updateTriggerOid,
   10857             :                                     with_period);
   10858             : 
   10859         382 :             table_close(partition, NoLock);
   10860             :         }
   10861             : 
   10862         442 :         table_close(trigrel, RowExclusiveLock);
   10863             :     }
   10864        2782 : }
   10865             : 
   10866             : /*
   10867             :  * CloneForeignKeyConstraints
   10868             :  *      Clone foreign keys from a partitioned table to a newly acquired
   10869             :  *      partition.
   10870             :  *
   10871             :  * partitionRel is a partition of parentRel, so we can be certain that it has
   10872             :  * the same columns with the same datatypes.  The columns may be in different
   10873             :  * order, though.
   10874             :  *
   10875             :  * wqueue must be passed to set up phase 3 constraint checking, unless the
   10876             :  * referencing-side partition is known to be empty (such as in CREATE TABLE /
   10877             :  * PARTITION OF).
   10878             :  */
   10879             : static void
   10880        9286 : CloneForeignKeyConstraints(List **wqueue, Relation parentRel,
   10881             :                            Relation partitionRel)
   10882             : {
   10883             :     /* This only works for declarative partitioning */
   10884             :     Assert(parentRel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
   10885             : 
   10886             :     /*
   10887             :      * Clone constraints for which the parent is on the referenced side.
   10888             :      */
   10889        9286 :     CloneFkReferenced(parentRel, partitionRel);
   10890             : 
   10891             :     /*
   10892             :      * Now clone constraints where the parent is on the referencing side.
   10893             :      */
   10894        9286 :     CloneFkReferencing(wqueue, parentRel, partitionRel);
   10895        9274 : }
   10896             : 
   10897             : /*
   10898             :  * CloneFkReferenced
   10899             :  *      Subroutine for CloneForeignKeyConstraints
   10900             :  *
   10901             :  * Find all the FKs that have the parent relation on the referenced side;
   10902             :  * clone those constraints to the given partition.  This is to be called
   10903             :  * when the partition is being created or attached.
   10904             :  *
   10905             :  * This ignores self-referencing FKs; those are handled by CloneFkReferencing.
   10906             :  *
   10907             :  * This recurses to partitions, if the relation being attached is partitioned.
   10908             :  * Recursion is done by calling addFkRecurseReferenced.
   10909             :  */
   10910             : static void
   10911        9286 : CloneFkReferenced(Relation parentRel, Relation partitionRel)
   10912             : {
   10913             :     Relation    pg_constraint;
   10914             :     AttrMap    *attmap;
   10915             :     ListCell   *cell;
   10916             :     SysScanDesc scan;
   10917             :     ScanKeyData key[2];
   10918             :     HeapTuple   tuple;
   10919        9286 :     List       *clone = NIL;
   10920             :     Relation    trigrel;
   10921             : 
   10922             :     /*
   10923             :      * Search for any constraints where this partition's parent is in the
   10924             :      * referenced side.  However, we must not clone any constraint whose
   10925             :      * parent constraint is also going to be cloned, to avoid duplicates.  So
   10926             :      * do it in two steps: first construct the list of constraints to clone,
   10927             :      * then go over that list cloning those whose parents are not in the list.
   10928             :      * (We must not rely on the parent being seen first, since the catalog
   10929             :      * scan could return children first.)
   10930             :      */
   10931        9286 :     pg_constraint = table_open(ConstraintRelationId, RowShareLock);
   10932        9286 :     ScanKeyInit(&key[0],
   10933             :                 Anum_pg_constraint_confrelid, BTEqualStrategyNumber,
   10934             :                 F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(parentRel)));
   10935        9286 :     ScanKeyInit(&key[1],
   10936             :                 Anum_pg_constraint_contype, BTEqualStrategyNumber,
   10937             :                 F_CHAREQ, CharGetDatum(CONSTRAINT_FOREIGN));
   10938             :     /* This is a seqscan, as we don't have a usable index ... */
   10939        9286 :     scan = systable_beginscan(pg_constraint, InvalidOid, true,
   10940             :                               NULL, 2, key);
   10941        9592 :     while ((tuple = systable_getnext(scan)) != NULL)
   10942             :     {
   10943         306 :         Form_pg_constraint constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
   10944             : 
   10945         306 :         clone = lappend_oid(clone, constrForm->oid);
   10946             :     }
   10947        9286 :     systable_endscan(scan);
   10948        9286 :     table_close(pg_constraint, RowShareLock);
   10949             : 
   10950             :     /*
   10951             :      * Triggers of the foreign keys will be manipulated a bunch of times in
   10952             :      * the loop below.  To avoid repeatedly opening/closing the trigger
   10953             :      * catalog relation, we open it here and pass it to the subroutines called
   10954             :      * below.
   10955             :      */
   10956        9286 :     trigrel = table_open(TriggerRelationId, RowExclusiveLock);
   10957             : 
   10958        9286 :     attmap = build_attrmap_by_name(RelationGetDescr(partitionRel),
   10959             :                                    RelationGetDescr(parentRel),
   10960             :                                    false);
   10961        9592 :     foreach(cell, clone)
   10962             :     {
   10963         306 :         Oid         constrOid = lfirst_oid(cell);
   10964             :         Form_pg_constraint constrForm;
   10965             :         Relation    fkRel;
   10966             :         Oid         indexOid;
   10967             :         Oid         partIndexId;
   10968             :         int         numfks;
   10969             :         AttrNumber  conkey[INDEX_MAX_KEYS];
   10970             :         AttrNumber  mapped_confkey[INDEX_MAX_KEYS];
   10971             :         AttrNumber  confkey[INDEX_MAX_KEYS];
   10972             :         Oid         conpfeqop[INDEX_MAX_KEYS];
   10973             :         Oid         conppeqop[INDEX_MAX_KEYS];
   10974             :         Oid         conffeqop[INDEX_MAX_KEYS];
   10975             :         int         numfkdelsetcols;
   10976             :         AttrNumber  confdelsetcols[INDEX_MAX_KEYS];
   10977             :         Constraint *fkconstraint;
   10978             :         ObjectAddress address;
   10979             :         Oid         deleteTriggerOid,
   10980             :                     updateTriggerOid;
   10981             : 
   10982         306 :         tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constrOid));
   10983         306 :         if (!HeapTupleIsValid(tuple))
   10984           0 :             elog(ERROR, "cache lookup failed for constraint %u", constrOid);
   10985         306 :         constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
   10986             : 
   10987             :         /*
   10988             :          * As explained above: don't try to clone a constraint for which we're
   10989             :          * going to clone the parent.
   10990             :          */
   10991         306 :         if (list_member_oid(clone, constrForm->conparentid))
   10992             :         {
   10993         126 :             ReleaseSysCache(tuple);
   10994         180 :             continue;
   10995             :         }
   10996             : 
   10997             :         /*
   10998             :          * Don't clone self-referencing foreign keys, which can be in the
   10999             :          * partitioned table or in the partition-to-be.
   11000             :          */
   11001         180 :         if (constrForm->conrelid == RelationGetRelid(parentRel) ||
   11002         138 :             constrForm->conrelid == RelationGetRelid(partitionRel))
   11003             :         {
   11004          54 :             ReleaseSysCache(tuple);
   11005          54 :             continue;
   11006             :         }
   11007             : 
   11008             :         /* We need the same lock level that CreateTrigger will acquire */
   11009         126 :         fkRel = table_open(constrForm->conrelid, ShareRowExclusiveLock);
   11010             : 
   11011         126 :         indexOid = constrForm->conindid;
   11012         126 :         DeconstructFkConstraintRow(tuple,
   11013             :                                    &numfks,
   11014             :                                    conkey,
   11015             :                                    confkey,
   11016             :                                    conpfeqop,
   11017             :                                    conppeqop,
   11018             :                                    conffeqop,
   11019             :                                    &numfkdelsetcols,
   11020             :                                    confdelsetcols);
   11021             : 
   11022         258 :         for (int i = 0; i < numfks; i++)
   11023         132 :             mapped_confkey[i] = attmap->attnums[confkey[i] - 1];
   11024             : 
   11025         126 :         fkconstraint = makeNode(Constraint);
   11026         126 :         fkconstraint->contype = CONSTRAINT_FOREIGN;
   11027         126 :         fkconstraint->conname = NameStr(constrForm->conname);
   11028         126 :         fkconstraint->deferrable = constrForm->condeferrable;
   11029         126 :         fkconstraint->initdeferred = constrForm->condeferred;
   11030         126 :         fkconstraint->location = -1;
   11031         126 :         fkconstraint->pktable = NULL;
   11032             :         /* ->fk_attrs determined below */
   11033         126 :         fkconstraint->pk_attrs = NIL;
   11034         126 :         fkconstraint->fk_matchtype = constrForm->confmatchtype;
   11035         126 :         fkconstraint->fk_upd_action = constrForm->confupdtype;
   11036         126 :         fkconstraint->fk_del_action = constrForm->confdeltype;
   11037         126 :         fkconstraint->fk_del_set_cols = NIL;
   11038         126 :         fkconstraint->old_conpfeqop = NIL;
   11039         126 :         fkconstraint->old_pktable_oid = InvalidOid;
   11040         126 :         fkconstraint->skip_validation = false;
   11041         126 :         fkconstraint->initially_valid = true;
   11042             : 
   11043             :         /* set up colnames that are used to generate the constraint name */
   11044         258 :         for (int i = 0; i < numfks; i++)
   11045             :         {
   11046             :             Form_pg_attribute att;
   11047             : 
   11048         132 :             att = TupleDescAttr(RelationGetDescr(fkRel),
   11049         132 :                                 conkey[i] - 1);
   11050         132 :             fkconstraint->fk_attrs = lappend(fkconstraint->fk_attrs,
   11051         132 :                                              makeString(NameStr(att->attname)));
   11052             :         }
   11053             : 
   11054             :         /*
   11055             :          * Add the new foreign key constraint pointing to the new partition.
   11056             :          * Because this new partition appears in the referenced side of the
   11057             :          * constraint, we don't need to set up for Phase 3 check.
   11058             :          */
   11059         126 :         partIndexId = index_get_partition(partitionRel, indexOid);
   11060         126 :         if (!OidIsValid(partIndexId))
   11061           0 :             elog(ERROR, "index for %u not found in partition %s",
   11062             :                  indexOid, RelationGetRelationName(partitionRel));
   11063             : 
   11064             :         /*
   11065             :          * Get the "action" triggers belonging to the constraint to pass as
   11066             :          * parent OIDs for similar triggers that will be created on the
   11067             :          * partition in addFkRecurseReferenced().
   11068             :          */
   11069         126 :         GetForeignKeyActionTriggers(trigrel, constrOid,
   11070             :                                     constrForm->confrelid, constrForm->conrelid,
   11071             :                                     &deleteTriggerOid, &updateTriggerOid);
   11072             : 
   11073             :         /* Add this constraint ... */
   11074         126 :         address = addFkConstraint(addFkReferencedSide,
   11075             :                                   fkconstraint->conname, fkconstraint, fkRel,
   11076             :                                   partitionRel, partIndexId, constrOid,
   11077             :                                   numfks, mapped_confkey,
   11078             :                                   conkey, conpfeqop, conppeqop, conffeqop,
   11079             :                                   numfkdelsetcols, confdelsetcols, false,
   11080         126 :                                   constrForm->conperiod);
   11081             :         /* ... and recurse */
   11082         126 :         addFkRecurseReferenced(fkconstraint,
   11083             :                                fkRel,
   11084             :                                partitionRel,
   11085             :                                partIndexId,
   11086             :                                address.objectId,
   11087             :                                numfks,
   11088             :                                mapped_confkey,
   11089             :                                conkey,
   11090             :                                conpfeqop,
   11091             :                                conppeqop,
   11092             :                                conffeqop,
   11093             :                                numfkdelsetcols,
   11094             :                                confdelsetcols,
   11095             :                                true,
   11096             :                                deleteTriggerOid,
   11097             :                                updateTriggerOid,
   11098         126 :                                constrForm->conperiod);
   11099             : 
   11100         126 :         table_close(fkRel, NoLock);
   11101         126 :         ReleaseSysCache(tuple);
   11102             :     }
   11103             : 
   11104        9286 :     table_close(trigrel, RowExclusiveLock);
   11105        9286 : }
   11106             : 
   11107             : /*
   11108             :  * CloneFkReferencing
   11109             :  *      Subroutine for CloneForeignKeyConstraints
   11110             :  *
   11111             :  * For each FK constraint of the parent relation in the given list, find an
   11112             :  * equivalent constraint in its partition relation that can be reparented;
   11113             :  * if one cannot be found, create a new constraint in the partition as its
   11114             :  * child.
   11115             :  *
   11116             :  * If wqueue is given, it is used to set up phase-3 verification for each
   11117             :  * cloned constraint; omit it if such verification is not needed
   11118             :  * (example: the partition is being created anew).
   11119             :  */
   11120             : static void
   11121        9286 : CloneFkReferencing(List **wqueue, Relation parentRel, Relation partRel)
   11122             : {
   11123             :     AttrMap    *attmap;
   11124             :     List       *partFKs;
   11125        9286 :     List       *clone = NIL;
   11126             :     ListCell   *cell;
   11127             :     Relation    trigrel;
   11128             : 
   11129             :     /* obtain a list of constraints that we need to clone */
   11130       10356 :     foreach(cell, RelationGetFKeyList(parentRel))
   11131             :     {
   11132        1076 :         ForeignKeyCacheInfo *fk = lfirst(cell);
   11133             : 
   11134             :         /*
   11135             :          * Refuse to attach a table as partition that this partitioned table
   11136             :          * already has a foreign key to.  This isn't useful schema, which is
   11137             :          * proven by the fact that there have been no user complaints that
   11138             :          * it's already impossible to achieve this in the opposite direction,
   11139             :          * i.e., creating a foreign key that references a partition.  This
   11140             :          * restriction allows us to dodge some complexities around
   11141             :          * pg_constraint and pg_trigger row creations that would be needed
   11142             :          * during ATTACH/DETACH for this kind of relationship.
   11143             :          */
   11144        1076 :         if (fk->confrelid == RelationGetRelid(partRel))
   11145           6 :             ereport(ERROR,
   11146             :                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   11147             :                      errmsg("cannot attach table \"%s\" as a partition because it is referenced by foreign key \"%s\"",
   11148             :                             RelationGetRelationName(partRel),
   11149             :                             get_constraint_name(fk->conoid))));
   11150             : 
   11151        1070 :         clone = lappend_oid(clone, fk->conoid);
   11152             :     }
   11153             : 
   11154             :     /*
   11155             :      * Silently do nothing if there's nothing to do.  In particular, this
   11156             :      * avoids throwing a spurious error for foreign tables.
   11157             :      */
   11158        9280 :     if (clone == NIL)
   11159        8826 :         return;
   11160             : 
   11161         454 :     if (partRel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
   11162           0 :         ereport(ERROR,
   11163             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   11164             :                  errmsg("foreign key constraints are not supported on foreign tables")));
   11165             : 
   11166             :     /*
   11167             :      * Triggers of the foreign keys will be manipulated a bunch of times in
   11168             :      * the loop below.  To avoid repeatedly opening/closing the trigger
   11169             :      * catalog relation, we open it here and pass it to the subroutines called
   11170             :      * below.
   11171             :      */
   11172         454 :     trigrel = table_open(TriggerRelationId, RowExclusiveLock);
   11173             : 
   11174             :     /*
   11175             :      * The constraint key may differ, if the columns in the partition are
   11176             :      * different.  This map is used to convert them.
   11177             :      */
   11178         454 :     attmap = build_attrmap_by_name(RelationGetDescr(partRel),
   11179             :                                    RelationGetDescr(parentRel),
   11180             :                                    false);
   11181             : 
   11182         454 :     partFKs = copyObject(RelationGetFKeyList(partRel));
   11183             : 
   11184        1518 :     foreach(cell, clone)
   11185             :     {
   11186        1070 :         Oid         parentConstrOid = lfirst_oid(cell);
   11187             :         Form_pg_constraint constrForm;
   11188             :         Relation    pkrel;
   11189             :         HeapTuple   tuple;
   11190             :         int         numfks;
   11191             :         AttrNumber  conkey[INDEX_MAX_KEYS];
   11192             :         AttrNumber  mapped_conkey[INDEX_MAX_KEYS];
   11193             :         AttrNumber  confkey[INDEX_MAX_KEYS];
   11194             :         Oid         conpfeqop[INDEX_MAX_KEYS];
   11195             :         Oid         conppeqop[INDEX_MAX_KEYS];
   11196             :         Oid         conffeqop[INDEX_MAX_KEYS];
   11197             :         int         numfkdelsetcols;
   11198             :         AttrNumber  confdelsetcols[INDEX_MAX_KEYS];
   11199             :         Constraint *fkconstraint;
   11200             :         bool        attached;
   11201             :         Oid         indexOid;
   11202             :         ObjectAddress address;
   11203             :         ListCell   *lc;
   11204             :         Oid         insertTriggerOid,
   11205             :                     updateTriggerOid;
   11206             :         bool        with_period;
   11207             : 
   11208        1070 :         tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(parentConstrOid));
   11209        1070 :         if (!HeapTupleIsValid(tuple))
   11210           0 :             elog(ERROR, "cache lookup failed for constraint %u",
   11211             :                  parentConstrOid);
   11212        1070 :         constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
   11213             : 
   11214             :         /* Don't clone constraints whose parents are being cloned */
   11215        1070 :         if (list_member_oid(clone, constrForm->conparentid))
   11216             :         {
   11217         592 :             ReleaseSysCache(tuple);
   11218         670 :             continue;
   11219             :         }
   11220             : 
   11221             :         /*
   11222             :          * Need to prevent concurrent deletions.  If pkrel is a partitioned
   11223             :          * relation, that means to lock all partitions.
   11224             :          */
   11225         478 :         pkrel = table_open(constrForm->confrelid, ShareRowExclusiveLock);
   11226         478 :         if (pkrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
   11227         220 :             (void) find_all_inheritors(RelationGetRelid(pkrel),
   11228             :                                        ShareRowExclusiveLock, NULL);
   11229             : 
   11230         478 :         DeconstructFkConstraintRow(tuple, &numfks, conkey, confkey,
   11231             :                                    conpfeqop, conppeqop, conffeqop,
   11232             :                                    &numfkdelsetcols, confdelsetcols);
   11233        1130 :         for (int i = 0; i < numfks; i++)
   11234         652 :             mapped_conkey[i] = attmap->attnums[conkey[i] - 1];
   11235             : 
   11236             :         /*
   11237             :          * Get the "check" triggers belonging to the constraint to pass as
   11238             :          * parent OIDs for similar triggers that will be created on the
   11239             :          * partition in addFkRecurseReferencing().  They are also passed to
   11240             :          * tryAttachPartitionForeignKey() below to simply assign as parents to
   11241             :          * the partition's existing "check" triggers, that is, if the
   11242             :          * corresponding constraints is deemed attachable to the parent
   11243             :          * constraint.
   11244             :          */
   11245         478 :         GetForeignKeyCheckTriggers(trigrel, constrForm->oid,
   11246             :                                    constrForm->confrelid, constrForm->conrelid,
   11247             :                                    &insertTriggerOid, &updateTriggerOid);
   11248             : 
   11249             :         /*
   11250             :          * Before creating a new constraint, see whether any existing FKs are
   11251             :          * fit for the purpose.  If one is, attach the parent constraint to
   11252             :          * it, and don't clone anything.  This way we avoid the expensive
   11253             :          * verification step and don't end up with a duplicate FK, and we
   11254             :          * don't need to recurse to partitions for this constraint.
   11255             :          */
   11256         478 :         attached = false;
   11257         682 :         foreach(lc, partFKs)
   11258             :         {
   11259         282 :             ForeignKeyCacheInfo *fk = lfirst_node(ForeignKeyCacheInfo, lc);
   11260             : 
   11261         282 :             if (tryAttachPartitionForeignKey(fk,
   11262             :                                              RelationGetRelid(partRel),
   11263             :                                              parentConstrOid,
   11264             :                                              numfks,
   11265             :                                              mapped_conkey,
   11266             :                                              confkey,
   11267             :                                              conpfeqop,
   11268             :                                              insertTriggerOid,
   11269             :                                              updateTriggerOid,
   11270             :                                              trigrel))
   11271             :             {
   11272          78 :                 attached = true;
   11273          78 :                 table_close(pkrel, NoLock);
   11274          78 :                 break;
   11275             :             }
   11276             :         }
   11277         478 :         if (attached)
   11278             :         {
   11279          78 :             ReleaseSysCache(tuple);
   11280          78 :             continue;
   11281             :         }
   11282             : 
   11283             :         /* No dice.  Set up to create our own constraint */
   11284         400 :         fkconstraint = makeNode(Constraint);
   11285         400 :         fkconstraint->contype = CONSTRAINT_FOREIGN;
   11286             :         /* ->conname determined below */
   11287         400 :         fkconstraint->deferrable = constrForm->condeferrable;
   11288         400 :         fkconstraint->initdeferred = constrForm->condeferred;
   11289         400 :         fkconstraint->location = -1;
   11290         400 :         fkconstraint->pktable = NULL;
   11291             :         /* ->fk_attrs determined below */
   11292         400 :         fkconstraint->pk_attrs = NIL;
   11293         400 :         fkconstraint->fk_matchtype = constrForm->confmatchtype;
   11294         400 :         fkconstraint->fk_upd_action = constrForm->confupdtype;
   11295         400 :         fkconstraint->fk_del_action = constrForm->confdeltype;
   11296         400 :         fkconstraint->fk_del_set_cols = NIL;
   11297         400 :         fkconstraint->old_conpfeqop = NIL;
   11298         400 :         fkconstraint->old_pktable_oid = InvalidOid;
   11299         400 :         fkconstraint->skip_validation = false;
   11300         400 :         fkconstraint->initially_valid = constrForm->convalidated;
   11301         914 :         for (int i = 0; i < numfks; i++)
   11302             :         {
   11303             :             Form_pg_attribute att;
   11304             : 
   11305         514 :             att = TupleDescAttr(RelationGetDescr(partRel),
   11306         514 :                                 mapped_conkey[i] - 1);
   11307         514 :             fkconstraint->fk_attrs = lappend(fkconstraint->fk_attrs,
   11308         514 :                                              makeString(NameStr(att->attname)));
   11309             :         }
   11310             : 
   11311         400 :         indexOid = constrForm->conindid;
   11312         400 :         with_period = constrForm->conperiod;
   11313             : 
   11314             :         /* Create the pg_constraint entry at this level */
   11315         400 :         address = addFkConstraint(addFkReferencingSide,
   11316         400 :                                   NameStr(constrForm->conname), fkconstraint,
   11317             :                                   partRel, pkrel, indexOid, parentConstrOid,
   11318             :                                   numfks, confkey,
   11319             :                                   mapped_conkey, conpfeqop,
   11320             :                                   conppeqop, conffeqop,
   11321             :                                   numfkdelsetcols, confdelsetcols,
   11322             :                                   false, with_period);
   11323             : 
   11324             :         /* Done with the cloned constraint's tuple */
   11325         400 :         ReleaseSysCache(tuple);
   11326             : 
   11327             :         /* Create the check triggers, and recurse to partitions, if any */
   11328         400 :         addFkRecurseReferencing(wqueue,
   11329             :                                 fkconstraint,
   11330             :                                 partRel,
   11331             :                                 pkrel,
   11332             :                                 indexOid,
   11333             :                                 address.objectId,
   11334             :                                 numfks,
   11335             :                                 confkey,
   11336             :                                 mapped_conkey,
   11337             :                                 conpfeqop,
   11338             :                                 conppeqop,
   11339             :                                 conffeqop,
   11340             :                                 numfkdelsetcols,
   11341             :                                 confdelsetcols,
   11342             :                                 false,  /* no old check exists */
   11343             :                                 AccessExclusiveLock,
   11344             :                                 insertTriggerOid,
   11345             :                                 updateTriggerOid,
   11346             :                                 with_period);
   11347         394 :         table_close(pkrel, NoLock);
   11348             :     }
   11349             : 
   11350         448 :     table_close(trigrel, RowExclusiveLock);
   11351             : }
   11352             : 
   11353             : /*
   11354             :  * When the parent of a partition receives [the referencing side of] a foreign
   11355             :  * key, we must propagate that foreign key to the partition.  However, the
   11356             :  * partition might already have an equivalent foreign key; this routine
   11357             :  * compares the given ForeignKeyCacheInfo (in the partition) to the FK defined
   11358             :  * by the other parameters.  If they are equivalent, create the link between
   11359             :  * the two constraints and return true.
   11360             :  *
   11361             :  * If the given FK does not match the one defined by rest of the params,
   11362             :  * return false.
   11363             :  */
   11364             : static bool
   11365         312 : tryAttachPartitionForeignKey(ForeignKeyCacheInfo *fk,
   11366             :                              Oid partRelid,
   11367             :                              Oid parentConstrOid,
   11368             :                              int numfks,
   11369             :                              AttrNumber *mapped_conkey,
   11370             :                              AttrNumber *confkey,
   11371             :                              Oid *conpfeqop,
   11372             :                              Oid parentInsTrigger,
   11373             :                              Oid parentUpdTrigger,
   11374             :                              Relation trigrel)
   11375             : {
   11376             :     HeapTuple   parentConstrTup;
   11377             :     Form_pg_constraint parentConstr;
   11378             :     HeapTuple   partcontup;
   11379             :     Form_pg_constraint partConstr;
   11380             :     ScanKeyData key;
   11381             :     SysScanDesc scan;
   11382             :     HeapTuple   trigtup;
   11383             :     Oid         insertTriggerOid,
   11384             :                 updateTriggerOid;
   11385             : 
   11386         312 :     parentConstrTup = SearchSysCache1(CONSTROID,
   11387             :                                       ObjectIdGetDatum(parentConstrOid));
   11388         312 :     if (!HeapTupleIsValid(parentConstrTup))
   11389           0 :         elog(ERROR, "cache lookup failed for constraint %u", parentConstrOid);
   11390         312 :     parentConstr = (Form_pg_constraint) GETSTRUCT(parentConstrTup);
   11391             : 
   11392             :     /*
   11393             :      * Do some quick & easy initial checks.  If any of these fail, we cannot
   11394             :      * use this constraint.
   11395             :      */
   11396         312 :     if (fk->confrelid != parentConstr->confrelid || fk->nkeys != numfks)
   11397             :     {
   11398         120 :         ReleaseSysCache(parentConstrTup);
   11399         120 :         return false;
   11400             :     }
   11401         546 :     for (int i = 0; i < numfks; i++)
   11402             :     {
   11403         354 :         if (fk->conkey[i] != mapped_conkey[i] ||
   11404         354 :             fk->confkey[i] != confkey[i] ||
   11405         354 :             fk->conpfeqop[i] != conpfeqop[i])
   11406             :         {
   11407           0 :             ReleaseSysCache(parentConstrTup);
   11408           0 :             return false;
   11409             :         }
   11410             :     }
   11411             : 
   11412             :     /*
   11413             :      * Looks good so far; do some more extensive checks.  Presumably the check
   11414             :      * for 'convalidated' could be dropped, since we don't really care about
   11415             :      * that, but let's be careful for now.
   11416             :      */
   11417         192 :     partcontup = SearchSysCache1(CONSTROID,
   11418             :                                  ObjectIdGetDatum(fk->conoid));
   11419         192 :     if (!HeapTupleIsValid(partcontup))
   11420           0 :         elog(ERROR, "cache lookup failed for constraint %u", fk->conoid);
   11421         192 :     partConstr = (Form_pg_constraint) GETSTRUCT(partcontup);
   11422         192 :     if (OidIsValid(partConstr->conparentid) ||
   11423         168 :         !partConstr->convalidated ||
   11424         168 :         partConstr->condeferrable != parentConstr->condeferrable ||
   11425         140 :         partConstr->condeferred != parentConstr->condeferred ||
   11426         140 :         partConstr->confupdtype != parentConstr->confupdtype ||
   11427         104 :         partConstr->confdeltype != parentConstr->confdeltype ||
   11428         104 :         partConstr->confmatchtype != parentConstr->confmatchtype)
   11429             :     {
   11430         102 :         ReleaseSysCache(parentConstrTup);
   11431         102 :         ReleaseSysCache(partcontup);
   11432         102 :         return false;
   11433             :     }
   11434             : 
   11435          90 :     ReleaseSysCache(partcontup);
   11436          90 :     ReleaseSysCache(parentConstrTup);
   11437             : 
   11438             :     /*
   11439             :      * Looks good!  Attach this constraint.  The action triggers in the new
   11440             :      * partition become redundant -- the parent table already has equivalent
   11441             :      * ones, and those will be able to reach the partition.  Remove the ones
   11442             :      * in the partition.  We identify them because they have our constraint
   11443             :      * OID, as well as being on the referenced rel.
   11444             :      */
   11445          90 :     ScanKeyInit(&key,
   11446             :                 Anum_pg_trigger_tgconstraint,
   11447             :                 BTEqualStrategyNumber, F_OIDEQ,
   11448             :                 ObjectIdGetDatum(fk->conoid));
   11449          90 :     scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
   11450             :                               NULL, 1, &key);
   11451         450 :     while ((trigtup = systable_getnext(scan)) != NULL)
   11452             :     {
   11453         360 :         Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
   11454             :         ObjectAddress trigger;
   11455             : 
   11456         360 :         if (trgform->tgconstrrelid != fk->conrelid)
   11457         180 :             continue;
   11458         180 :         if (trgform->tgrelid != fk->confrelid)
   11459           0 :             continue;
   11460             : 
   11461             :         /*
   11462             :          * The constraint is originally set up to contain this trigger as an
   11463             :          * implementation object, so there's a dependency record that links
   11464             :          * the two; however, since the trigger is no longer needed, we remove
   11465             :          * the dependency link in order to be able to drop the trigger while
   11466             :          * keeping the constraint intact.
   11467             :          */
   11468         180 :         deleteDependencyRecordsFor(TriggerRelationId,
   11469             :                                    trgform->oid,
   11470             :                                    false);
   11471             :         /* make dependency deletion visible to performDeletion */
   11472         180 :         CommandCounterIncrement();
   11473         180 :         ObjectAddressSet(trigger, TriggerRelationId,
   11474             :                          trgform->oid);
   11475         180 :         performDeletion(&trigger, DROP_RESTRICT, 0);
   11476             :         /* make trigger drop visible, in case the loop iterates */
   11477         180 :         CommandCounterIncrement();
   11478             :     }
   11479             : 
   11480          90 :     systable_endscan(scan);
   11481             : 
   11482          90 :     ConstraintSetParentConstraint(fk->conoid, parentConstrOid, partRelid);
   11483             : 
   11484             :     /*
   11485             :      * Like the constraint, attach partition's "check" triggers to the
   11486             :      * corresponding parent triggers.
   11487             :      */
   11488          90 :     GetForeignKeyCheckTriggers(trigrel,
   11489             :                                fk->conoid, fk->confrelid, fk->conrelid,
   11490             :                                &insertTriggerOid, &updateTriggerOid);
   11491             :     Assert(OidIsValid(insertTriggerOid) && OidIsValid(parentInsTrigger));
   11492          90 :     TriggerSetParentTrigger(trigrel, insertTriggerOid, parentInsTrigger,
   11493             :                             partRelid);
   11494             :     Assert(OidIsValid(updateTriggerOid) && OidIsValid(parentUpdTrigger));
   11495          90 :     TriggerSetParentTrigger(trigrel, updateTriggerOid, parentUpdTrigger,
   11496             :                             partRelid);
   11497             : 
   11498             :     /*
   11499             :      * If the referenced table is partitioned, then the partition we're
   11500             :      * attaching now has extra pg_constraint rows and action triggers that are
   11501             :      * no longer needed.  Remove those.
   11502             :      */
   11503          90 :     if (get_rel_relkind(fk->confrelid) == RELKIND_PARTITIONED_TABLE)
   11504             :     {
   11505          24 :         Relation    pg_constraint = table_open(ConstraintRelationId, RowShareLock);
   11506             :         ObjectAddresses *objs;
   11507             :         HeapTuple   consttup;
   11508             : 
   11509          24 :         ScanKeyInit(&key,
   11510             :                     Anum_pg_constraint_conrelid,
   11511             :                     BTEqualStrategyNumber, F_OIDEQ,
   11512             :                     ObjectIdGetDatum(fk->conrelid));
   11513             : 
   11514          24 :         scan = systable_beginscan(pg_constraint,
   11515             :                                   ConstraintRelidTypidNameIndexId,
   11516             :                                   true, NULL, 1, &key);
   11517          24 :         objs = new_object_addresses();
   11518         240 :         while ((consttup = systable_getnext(scan)) != NULL)
   11519             :         {
   11520         216 :             Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(consttup);
   11521             : 
   11522         216 :             if (conform->conparentid != fk->conoid)
   11523         168 :                 continue;
   11524             :             else
   11525             :             {
   11526             :                 ObjectAddress addr;
   11527             :                 SysScanDesc scan2;
   11528             :                 ScanKeyData key2;
   11529             :                 int         n PG_USED_FOR_ASSERTS_ONLY;
   11530             : 
   11531          48 :                 ObjectAddressSet(addr, ConstraintRelationId, conform->oid);
   11532          48 :                 add_exact_object_address(&addr, objs);
   11533             : 
   11534             :                 /*
   11535             :                  * First we must delete the dependency record that binds the
   11536             :                  * constraint records together.
   11537             :                  */
   11538          48 :                 n = deleteDependencyRecordsForSpecific(ConstraintRelationId,
   11539             :                                                        conform->oid,
   11540             :                                                        DEPENDENCY_INTERNAL,
   11541             :                                                        ConstraintRelationId,
   11542             :                                                        fk->conoid);
   11543             :                 Assert(n == 1); /* actually only one is expected */
   11544             : 
   11545             :                 /*
   11546             :                  * Now search for the triggers for this constraint and set
   11547             :                  * them up for deletion too
   11548             :                  */
   11549          48 :                 ScanKeyInit(&key2,
   11550             :                             Anum_pg_trigger_tgconstraint,
   11551             :                             BTEqualStrategyNumber, F_OIDEQ,
   11552             :                             ObjectIdGetDatum(conform->oid));
   11553          48 :                 scan2 = systable_beginscan(trigrel, TriggerConstraintIndexId,
   11554             :                                            true, NULL, 1, &key2);
   11555         144 :                 while ((trigtup = systable_getnext(scan2)) != NULL)
   11556             :                 {
   11557          96 :                     ObjectAddressSet(addr, TriggerRelationId,
   11558             :                                      ((Form_pg_trigger) GETSTRUCT(trigtup))->oid);
   11559          96 :                     add_exact_object_address(&addr, objs);
   11560             :                 }
   11561          48 :                 systable_endscan(scan2);
   11562             :             }
   11563             :         }
   11564             :         /* make the dependency deletions visible */
   11565          24 :         CommandCounterIncrement();
   11566          24 :         performMultipleDeletions(objs, DROP_RESTRICT,
   11567             :                                  PERFORM_DELETION_INTERNAL);
   11568          24 :         systable_endscan(scan);
   11569             : 
   11570          24 :         table_close(pg_constraint, RowShareLock);
   11571             :     }
   11572             : 
   11573          90 :     CommandCounterIncrement();
   11574          90 :     return true;
   11575             : }
   11576             : 
   11577             : /*
   11578             :  * GetForeignKeyActionTriggers
   11579             :  *      Returns delete and update "action" triggers of the given relation
   11580             :  *      belonging to the given constraint
   11581             :  */
   11582             : static void
   11583         126 : GetForeignKeyActionTriggers(Relation trigrel,
   11584             :                             Oid conoid, Oid confrelid, Oid conrelid,
   11585             :                             Oid *deleteTriggerOid,
   11586             :                             Oid *updateTriggerOid)
   11587             : {
   11588             :     ScanKeyData key;
   11589             :     SysScanDesc scan;
   11590             :     HeapTuple   trigtup;
   11591             : 
   11592         126 :     *deleteTriggerOid = *updateTriggerOid = InvalidOid;
   11593         126 :     ScanKeyInit(&key,
   11594             :                 Anum_pg_trigger_tgconstraint,
   11595             :                 BTEqualStrategyNumber, F_OIDEQ,
   11596             :                 ObjectIdGetDatum(conoid));
   11597             : 
   11598         126 :     scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
   11599             :                               NULL, 1, &key);
   11600         264 :     while ((trigtup = systable_getnext(scan)) != NULL)
   11601             :     {
   11602         264 :         Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
   11603             : 
   11604         264 :         if (trgform->tgconstrrelid != conrelid)
   11605          12 :             continue;
   11606         252 :         if (trgform->tgrelid != confrelid)
   11607           0 :             continue;
   11608             :         /* Only ever look at "action" triggers on the PK side. */
   11609         252 :         if (RI_FKey_trigger_type(trgform->tgfoid) != RI_TRIGGER_PK)
   11610           0 :             continue;
   11611         252 :         if (TRIGGER_FOR_DELETE(trgform->tgtype))
   11612             :         {
   11613             :             Assert(*deleteTriggerOid == InvalidOid);
   11614         126 :             *deleteTriggerOid = trgform->oid;
   11615             :         }
   11616         126 :         else if (TRIGGER_FOR_UPDATE(trgform->tgtype))
   11617             :         {
   11618             :             Assert(*updateTriggerOid == InvalidOid);
   11619         126 :             *updateTriggerOid = trgform->oid;
   11620             :         }
   11621             : #ifndef USE_ASSERT_CHECKING
   11622             :         /* In an assert-enabled build, continue looking to find duplicates */
   11623         252 :         if (OidIsValid(*deleteTriggerOid) && OidIsValid(*updateTriggerOid))
   11624         126 :             break;
   11625             : #endif
   11626             :     }
   11627             : 
   11628         126 :     if (!OidIsValid(*deleteTriggerOid))
   11629           0 :         elog(ERROR, "could not find ON DELETE action trigger of foreign key constraint %u",
   11630             :              conoid);
   11631         126 :     if (!OidIsValid(*updateTriggerOid))
   11632           0 :         elog(ERROR, "could not find ON UPDATE action trigger of foreign key constraint %u",
   11633             :              conoid);
   11634             : 
   11635         126 :     systable_endscan(scan);
   11636         126 : }
   11637             : 
   11638             : /*
   11639             :  * GetForeignKeyCheckTriggers
   11640             :  *      Returns insert and update "check" triggers of the given relation
   11641             :  *      belonging to the given constraint
   11642             :  */
   11643             : static void
   11644         640 : GetForeignKeyCheckTriggers(Relation trigrel,
   11645             :                            Oid conoid, Oid confrelid, Oid conrelid,
   11646             :                            Oid *insertTriggerOid,
   11647             :                            Oid *updateTriggerOid)
   11648             : {
   11649             :     ScanKeyData key;
   11650             :     SysScanDesc scan;
   11651             :     HeapTuple   trigtup;
   11652             : 
   11653         640 :     *insertTriggerOid = *updateTriggerOid = InvalidOid;
   11654         640 :     ScanKeyInit(&key,
   11655             :                 Anum_pg_trigger_tgconstraint,
   11656             :                 BTEqualStrategyNumber, F_OIDEQ,
   11657             :                 ObjectIdGetDatum(conoid));
   11658             : 
   11659         640 :     scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
   11660             :                               NULL, 1, &key);
   11661        2100 :     while ((trigtup = systable_getnext(scan)) != NULL)
   11662             :     {
   11663        2100 :         Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
   11664             : 
   11665        2100 :         if (trgform->tgconstrrelid != confrelid)
   11666         736 :             continue;
   11667        1364 :         if (trgform->tgrelid != conrelid)
   11668           0 :             continue;
   11669             :         /* Only ever look at "check" triggers on the FK side. */
   11670        1364 :         if (RI_FKey_trigger_type(trgform->tgfoid) != RI_TRIGGER_FK)
   11671          84 :             continue;
   11672        1280 :         if (TRIGGER_FOR_INSERT(trgform->tgtype))
   11673             :         {
   11674             :             Assert(*insertTriggerOid == InvalidOid);
   11675         640 :             *insertTriggerOid = trgform->oid;
   11676             :         }
   11677         640 :         else if (TRIGGER_FOR_UPDATE(trgform->tgtype))
   11678             :         {
   11679             :             Assert(*updateTriggerOid == InvalidOid);
   11680         640 :             *updateTriggerOid = trgform->oid;
   11681             :         }
   11682             : #ifndef USE_ASSERT_CHECKING
   11683             :         /* In an assert-enabled build, continue looking to find duplicates. */
   11684        1280 :         if (OidIsValid(*insertTriggerOid) && OidIsValid(*updateTriggerOid))
   11685         640 :             break;
   11686             : #endif
   11687             :     }
   11688             : 
   11689         640 :     if (!OidIsValid(*insertTriggerOid))
   11690           0 :         elog(ERROR, "could not find ON INSERT check triggers of foreign key constraint %u",
   11691             :              conoid);
   11692         640 :     if (!OidIsValid(*updateTriggerOid))
   11693           0 :         elog(ERROR, "could not find ON UPDATE check triggers of foreign key constraint %u",
   11694             :              conoid);
   11695             : 
   11696         640 :     systable_endscan(scan);
   11697         640 : }
   11698             : 
   11699             : /*
   11700             :  * ALTER TABLE ALTER CONSTRAINT
   11701             :  *
   11702             :  * Update the attributes of a constraint.
   11703             :  *
   11704             :  * Currently only works for Foreign Key constraints.
   11705             :  *
   11706             :  * If the constraint is modified, returns its address; otherwise, return
   11707             :  * InvalidObjectAddress.
   11708             :  */
   11709             : static ObjectAddress
   11710         126 : ATExecAlterConstraint(Relation rel, AlterTableCmd *cmd, bool recurse,
   11711             :                       bool recursing, LOCKMODE lockmode)
   11712             : {
   11713             :     Constraint *cmdcon;
   11714             :     Relation    conrel;
   11715             :     Relation    tgrel;
   11716             :     SysScanDesc scan;
   11717             :     ScanKeyData skey[3];
   11718             :     HeapTuple   contuple;
   11719             :     Form_pg_constraint currcon;
   11720             :     ObjectAddress address;
   11721         126 :     List       *otherrelids = NIL;
   11722             :     ListCell   *lc;
   11723             : 
   11724         126 :     cmdcon = castNode(Constraint, cmd->def);
   11725             : 
   11726         126 :     conrel = table_open(ConstraintRelationId, RowExclusiveLock);
   11727         126 :     tgrel = table_open(TriggerRelationId, RowExclusiveLock);
   11728             : 
   11729             :     /*
   11730             :      * Find and check the target constraint
   11731             :      */
   11732         126 :     ScanKeyInit(&skey[0],
   11733             :                 Anum_pg_constraint_conrelid,
   11734             :                 BTEqualStrategyNumber, F_OIDEQ,
   11735             :                 ObjectIdGetDatum(RelationGetRelid(rel)));
   11736         126 :     ScanKeyInit(&skey[1],
   11737             :                 Anum_pg_constraint_contypid,
   11738             :                 BTEqualStrategyNumber, F_OIDEQ,
   11739             :                 ObjectIdGetDatum(InvalidOid));
   11740         126 :     ScanKeyInit(&skey[2],
   11741             :                 Anum_pg_constraint_conname,
   11742             :                 BTEqualStrategyNumber, F_NAMEEQ,
   11743         126 :                 CStringGetDatum(cmdcon->conname));
   11744         126 :     scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
   11745             :                               true, NULL, 3, skey);
   11746             : 
   11747             :     /* There can be at most one matching row */
   11748         126 :     if (!HeapTupleIsValid(contuple = systable_getnext(scan)))
   11749           0 :         ereport(ERROR,
   11750             :                 (errcode(ERRCODE_UNDEFINED_OBJECT),
   11751             :                  errmsg("constraint \"%s\" of relation \"%s\" does not exist",
   11752             :                         cmdcon->conname, RelationGetRelationName(rel))));
   11753             : 
   11754         126 :     currcon = (Form_pg_constraint) GETSTRUCT(contuple);
   11755         126 :     if (currcon->contype != CONSTRAINT_FOREIGN)
   11756           0 :         ereport(ERROR,
   11757             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   11758             :                  errmsg("constraint \"%s\" of relation \"%s\" is not a foreign key constraint",
   11759             :                         cmdcon->conname, RelationGetRelationName(rel))));
   11760             : 
   11761             :     /*
   11762             :      * If it's not the topmost constraint, raise an error.
   11763             :      *
   11764             :      * Altering a non-topmost constraint leaves some triggers untouched, since
   11765             :      * they are not directly connected to this constraint; also, pg_dump would
   11766             :      * ignore the deferrability status of the individual constraint, since it
   11767             :      * only dumps topmost constraints.  Avoid these problems by refusing this
   11768             :      * operation and telling the user to alter the parent constraint instead.
   11769             :      */
   11770         126 :     if (OidIsValid(currcon->conparentid))
   11771             :     {
   11772             :         HeapTuple   tp;
   11773          12 :         Oid         parent = currcon->conparentid;
   11774          12 :         char       *ancestorname = NULL;
   11775          12 :         char       *ancestortable = NULL;
   11776             : 
   11777             :         /* Loop to find the topmost constraint */
   11778          24 :         while (HeapTupleIsValid(tp = SearchSysCache1(CONSTROID, ObjectIdGetDatum(parent))))
   11779             :         {
   11780          24 :             Form_pg_constraint contup = (Form_pg_constraint) GETSTRUCT(tp);
   11781             : 
   11782             :             /* If no parent, this is the constraint we want */
   11783          24 :             if (!OidIsValid(contup->conparentid))
   11784             :             {
   11785          12 :                 ancestorname = pstrdup(NameStr(contup->conname));
   11786          12 :                 ancestortable = get_rel_name(contup->conrelid);
   11787          12 :                 ReleaseSysCache(tp);
   11788          12 :                 break;
   11789             :             }
   11790             : 
   11791          12 :             parent = contup->conparentid;
   11792          12 :             ReleaseSysCache(tp);
   11793             :         }
   11794             : 
   11795          12 :         ereport(ERROR,
   11796             :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
   11797             :                  errmsg("cannot alter constraint \"%s\" on relation \"%s\"",
   11798             :                         cmdcon->conname, RelationGetRelationName(rel)),
   11799             :                  ancestorname && ancestortable ?
   11800             :                  errdetail("Constraint \"%s\" is derived from constraint \"%s\" of relation \"%s\".",
   11801             :                            cmdcon->conname, ancestorname, ancestortable) : 0,
   11802             :                  errhint("You may alter the constraint it derives from instead.")));
   11803             :     }
   11804             : 
   11805             :     /*
   11806             :      * Do the actual catalog work.  We can skip changing if already in the
   11807             :      * desired state, but not if a partitioned table: partitions need to be
   11808             :      * processed regardless, in case they had the constraint locally changed.
   11809             :      */
   11810         114 :     address = InvalidObjectAddress;
   11811         114 :     if (currcon->condeferrable != cmdcon->deferrable ||
   11812           6 :         currcon->condeferred != cmdcon->initdeferred ||
   11813           0 :         rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
   11814             :     {
   11815         114 :         if (ATExecAlterConstrRecurse(cmdcon, conrel, tgrel, rel, contuple,
   11816             :                                      &otherrelids, lockmode))
   11817         114 :             ObjectAddressSet(address, ConstraintRelationId, currcon->oid);
   11818             :     }
   11819             : 
   11820             :     /*
   11821             :      * ATExecAlterConstrRecurse already invalidated relcache for the relations
   11822             :      * having the constraint itself; here we also invalidate for relations
   11823             :      * that have any triggers that are part of the constraint.
   11824             :      */
   11825         258 :     foreach(lc, otherrelids)
   11826         144 :         CacheInvalidateRelcacheByRelid(lfirst_oid(lc));
   11827             : 
   11828         114 :     systable_endscan(scan);
   11829             : 
   11830         114 :     table_close(tgrel, RowExclusiveLock);
   11831         114 :     table_close(conrel, RowExclusiveLock);
   11832             : 
   11833         114 :     return address;
   11834             : }
   11835             : 
   11836             : /*
   11837             :  * Recursive subroutine of ATExecAlterConstraint.  Returns true if the
   11838             :  * constraint is altered.
   11839             :  *
   11840             :  * *otherrelids is appended OIDs of relations containing affected triggers.
   11841             :  *
   11842             :  * Note that we must recurse even when the values are correct, in case
   11843             :  * indirect descendants have had their constraints altered locally.
   11844             :  * (This could be avoided if we forbade altering constraints in partitions
   11845             :  * but existing releases don't do that.)
   11846             :  */
   11847             : static bool
   11848         180 : ATExecAlterConstrRecurse(Constraint *cmdcon, Relation conrel, Relation tgrel,
   11849             :                          Relation rel, HeapTuple contuple, List **otherrelids,
   11850             :                          LOCKMODE lockmode)
   11851             : {
   11852             :     Form_pg_constraint currcon;
   11853             :     Oid         conoid;
   11854             :     Oid         refrelid;
   11855         180 :     bool        changed = false;
   11856             : 
   11857             :     /* since this function recurses, it could be driven to stack overflow */
   11858         180 :     check_stack_depth();
   11859             : 
   11860         180 :     currcon = (Form_pg_constraint) GETSTRUCT(contuple);
   11861         180 :     conoid = currcon->oid;
   11862         180 :     refrelid = currcon->confrelid;
   11863             : 
   11864             :     /*
   11865             :      * Update pg_constraint with the flags from cmdcon.
   11866             :      *
   11867             :      * If called to modify a constraint that's already in the desired state,
   11868             :      * silently do nothing.
   11869             :      */
   11870         180 :     if (currcon->condeferrable != cmdcon->deferrable ||
   11871           6 :         currcon->condeferred != cmdcon->initdeferred)
   11872             :     {
   11873             :         HeapTuple   copyTuple;
   11874             :         Form_pg_constraint copy_con;
   11875             : 
   11876         180 :         copyTuple = heap_copytuple(contuple);
   11877         180 :         copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
   11878         180 :         copy_con->condeferrable = cmdcon->deferrable;
   11879         180 :         copy_con->condeferred = cmdcon->initdeferred;
   11880         180 :         CatalogTupleUpdate(conrel, &copyTuple->t_self, copyTuple);
   11881             : 
   11882         180 :         InvokeObjectPostAlterHook(ConstraintRelationId,
   11883             :                                   conoid, 0);
   11884             : 
   11885         180 :         heap_freetuple(copyTuple);
   11886         180 :         changed = true;
   11887             : 
   11888             :         /* Make new constraint flags visible to others */
   11889         180 :         CacheInvalidateRelcache(rel);
   11890             : 
   11891             :         /*
   11892             :          * Now we need to update the multiple entries in pg_trigger that
   11893             :          * implement the constraint.
   11894             :          */
   11895         180 :         AlterConstrTriggerDeferrability(conoid, tgrel, rel, cmdcon->deferrable,
   11896         180 :                                         cmdcon->initdeferred, otherrelids);
   11897             :     }
   11898             : 
   11899             :     /*
   11900             :      * If the table at either end of the constraint is partitioned, we need to
   11901             :      * recurse and handle every constraint that is a child of this one.
   11902             :      *
   11903             :      * (This assumes that the recurse flag is forcibly set for partitioned
   11904             :      * tables, and not set for legacy inheritance, though we don't check for
   11905             :      * that here.)
   11906             :      */
   11907         336 :     if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
   11908         156 :         get_rel_relkind(refrelid) == RELKIND_PARTITIONED_TABLE)
   11909          42 :         ATExecAlterChildConstr(cmdcon, conrel, tgrel, rel, contuple,
   11910             :                                otherrelids, lockmode);
   11911             : 
   11912         180 :     return changed;
   11913             : }
   11914             : 
   11915             : /*
   11916             :  * A subroutine of ATExecAlterConstrRecurse that updated constraint trigger's
   11917             :  * deferrability.
   11918             :  *
   11919             :  * The arguments to this function have the same meaning as the arguments to
   11920             :  * ATExecAlterConstrRecurse.
   11921             :  */
   11922             : static void
   11923         180 : AlterConstrTriggerDeferrability(Oid conoid, Relation tgrel, Relation rel,
   11924             :                                 bool deferrable, bool initdeferred,
   11925             :                                 List **otherrelids)
   11926             : {
   11927             :     HeapTuple   tgtuple;
   11928             :     ScanKeyData tgkey;
   11929             :     SysScanDesc tgscan;
   11930             : 
   11931         180 :     ScanKeyInit(&tgkey,
   11932             :                 Anum_pg_trigger_tgconstraint,
   11933             :                 BTEqualStrategyNumber, F_OIDEQ,
   11934             :                 ObjectIdGetDatum(conoid));
   11935         180 :     tgscan = systable_beginscan(tgrel, TriggerConstraintIndexId, true,
   11936             :                                 NULL, 1, &tgkey);
   11937         768 :     while (HeapTupleIsValid(tgtuple = systable_getnext(tgscan)))
   11938             :     {
   11939         588 :         Form_pg_trigger tgform = (Form_pg_trigger) GETSTRUCT(tgtuple);
   11940             :         Form_pg_trigger copy_tg;
   11941             :         HeapTuple   tgCopyTuple;
   11942             : 
   11943             :         /*
   11944             :          * Remember OIDs of other relation(s) involved in FK constraint.
   11945             :          * (Note: it's likely that we could skip forcing a relcache inval for
   11946             :          * other rels that don't have a trigger whose properties change, but
   11947             :          * let's be conservative.)
   11948             :          */
   11949         588 :         if (tgform->tgrelid != RelationGetRelid(rel))
   11950         288 :             *otherrelids = list_append_unique_oid(*otherrelids,
   11951             :                                                   tgform->tgrelid);
   11952             : 
   11953             :         /*
   11954             :          * Update enable status and deferrability of RI_FKey_noaction_del,
   11955             :          * RI_FKey_noaction_upd, RI_FKey_check_ins and RI_FKey_check_upd
   11956             :          * triggers, but not others; see createForeignKeyActionTriggers and
   11957             :          * CreateFKCheckTrigger.
   11958             :          */
   11959         588 :         if (tgform->tgfoid != F_RI_FKEY_NOACTION_DEL &&
   11960         474 :             tgform->tgfoid != F_RI_FKEY_NOACTION_UPD &&
   11961         342 :             tgform->tgfoid != F_RI_FKEY_CHECK_INS &&
   11962         192 :             tgform->tgfoid != F_RI_FKEY_CHECK_UPD)
   11963          42 :             continue;
   11964             : 
   11965         546 :         tgCopyTuple = heap_copytuple(tgtuple);
   11966         546 :         copy_tg = (Form_pg_trigger) GETSTRUCT(tgCopyTuple);
   11967             : 
   11968         546 :         copy_tg->tgdeferrable = deferrable;
   11969         546 :         copy_tg->tginitdeferred = initdeferred;
   11970         546 :         CatalogTupleUpdate(tgrel, &tgCopyTuple->t_self, tgCopyTuple);
   11971             : 
   11972         546 :         InvokeObjectPostAlterHook(TriggerRelationId, tgform->oid, 0);
   11973             : 
   11974         546 :         heap_freetuple(tgCopyTuple);
   11975             :     }
   11976             : 
   11977         180 :     systable_endscan(tgscan);
   11978         180 : }
   11979             : 
   11980             : /*
   11981             :  * Invokes ATExecAlterConstrRecurse for each constraint that is a child of the
   11982             :  * specified constraint.
   11983             :  *
   11984             :  * The arguments to this function have the same meaning as the arguments to
   11985             :  * ATExecAlterConstrRecurse.
   11986             :  */
   11987             : static void
   11988          42 : ATExecAlterChildConstr(Constraint *cmdcon, Relation conrel, Relation tgrel,
   11989             :                        Relation rel, HeapTuple contuple, List **otherrelids,
   11990             :                        LOCKMODE lockmode)
   11991             : {
   11992             :     Form_pg_constraint currcon;
   11993             :     Oid         conoid;
   11994             :     ScanKeyData pkey;
   11995             :     SysScanDesc pscan;
   11996             :     HeapTuple   childtup;
   11997             : 
   11998          42 :     currcon = (Form_pg_constraint) GETSTRUCT(contuple);
   11999          42 :     conoid = currcon->oid;
   12000             : 
   12001          42 :     ScanKeyInit(&pkey,
   12002             :                 Anum_pg_constraint_conparentid,
   12003             :                 BTEqualStrategyNumber, F_OIDEQ,
   12004             :                 ObjectIdGetDatum(conoid));
   12005             : 
   12006          42 :     pscan = systable_beginscan(conrel, ConstraintParentIndexId,
   12007             :                                true, NULL, 1, &pkey);
   12008             : 
   12009         108 :     while (HeapTupleIsValid(childtup = systable_getnext(pscan)))
   12010             :     {
   12011          66 :         Form_pg_constraint childcon = (Form_pg_constraint) GETSTRUCT(childtup);
   12012             :         Relation    childrel;
   12013             : 
   12014          66 :         childrel = table_open(childcon->conrelid, lockmode);
   12015          66 :         ATExecAlterConstrRecurse(cmdcon, conrel, tgrel, childrel, childtup,
   12016             :                                  otherrelids, lockmode);
   12017          66 :         table_close(childrel, NoLock);
   12018             :     }
   12019             : 
   12020          42 :     systable_endscan(pscan);
   12021          42 : }
   12022             : 
   12023             : /*
   12024             :  * ALTER TABLE VALIDATE CONSTRAINT
   12025             :  *
   12026             :  * XXX The reason we handle recursion here rather than at Phase 1 is because
   12027             :  * there's no good way to skip recursing when handling foreign keys: there is
   12028             :  * no need to lock children in that case, yet we wouldn't be able to avoid
   12029             :  * doing so at that level.
   12030             :  *
   12031             :  * Return value is the address of the validated constraint.  If the constraint
   12032             :  * was already validated, InvalidObjectAddress is returned.
   12033             :  */
   12034             : static ObjectAddress
   12035         442 : ATExecValidateConstraint(List **wqueue, Relation rel, char *constrName,
   12036             :                          bool recurse, bool recursing, LOCKMODE lockmode)
   12037             : {
   12038             :     Relation    conrel;
   12039             :     SysScanDesc scan;
   12040             :     ScanKeyData skey[3];
   12041             :     HeapTuple   tuple;
   12042             :     Form_pg_constraint con;
   12043             :     ObjectAddress address;
   12044             : 
   12045         442 :     conrel = table_open(ConstraintRelationId, RowExclusiveLock);
   12046             : 
   12047             :     /*
   12048             :      * Find and check the target constraint
   12049             :      */
   12050         442 :     ScanKeyInit(&skey[0],
   12051             :                 Anum_pg_constraint_conrelid,
   12052             :                 BTEqualStrategyNumber, F_OIDEQ,
   12053             :                 ObjectIdGetDatum(RelationGetRelid(rel)));
   12054         442 :     ScanKeyInit(&skey[1],
   12055             :                 Anum_pg_constraint_contypid,
   12056             :                 BTEqualStrategyNumber, F_OIDEQ,
   12057             :                 ObjectIdGetDatum(InvalidOid));
   12058         442 :     ScanKeyInit(&skey[2],
   12059             :                 Anum_pg_constraint_conname,
   12060             :                 BTEqualStrategyNumber, F_NAMEEQ,
   12061             :                 CStringGetDatum(constrName));
   12062         442 :     scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
   12063             :                               true, NULL, 3, skey);
   12064             : 
   12065             :     /* There can be at most one matching row */
   12066         442 :     if (!HeapTupleIsValid(tuple = systable_getnext(scan)))
   12067           0 :         ereport(ERROR,
   12068             :                 (errcode(ERRCODE_UNDEFINED_OBJECT),
   12069             :                  errmsg("constraint \"%s\" of relation \"%s\" does not exist",
   12070             :                         constrName, RelationGetRelationName(rel))));
   12071             : 
   12072         442 :     con = (Form_pg_constraint) GETSTRUCT(tuple);
   12073         442 :     if (con->contype != CONSTRAINT_FOREIGN &&
   12074         138 :         con->contype != CONSTRAINT_CHECK)
   12075           0 :         ereport(ERROR,
   12076             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   12077             :                  errmsg("constraint \"%s\" of relation \"%s\" is not a foreign key or check constraint",
   12078             :                         constrName, RelationGetRelationName(rel))));
   12079             : 
   12080         442 :     if (!con->conenforced)
   12081           6 :         ereport(ERROR,
   12082             :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
   12083             :                  errmsg("cannot validate NOT ENFORCED constraint")));
   12084             : 
   12085         436 :     if (!con->convalidated)
   12086             :     {
   12087         418 :         if (con->contype == CONSTRAINT_FOREIGN)
   12088             :         {
   12089         298 :             QueueFKConstraintValidation(wqueue, conrel, rel, tuple, lockmode);
   12090             :         }
   12091         120 :         else if (con->contype == CONSTRAINT_CHECK)
   12092             :         {
   12093         120 :             QueueCheckConstraintValidation(wqueue, conrel, rel, constrName,
   12094             :                                            tuple, recurse, recursing, lockmode);
   12095             :         }
   12096             : 
   12097         418 :         ObjectAddressSet(address, ConstraintRelationId, con->oid);
   12098             :     }
   12099             :     else
   12100          18 :         address = InvalidObjectAddress; /* already validated */
   12101             : 
   12102         436 :     systable_endscan(scan);
   12103             : 
   12104         436 :     table_close(conrel, RowExclusiveLock);
   12105             : 
   12106         436 :     return address;
   12107             : }
   12108             : 
   12109             : /*
   12110             :  * QueueFKConstraintValidation
   12111             :  *
   12112             :  * Add an entry to the wqueue to validate the given foreign key constraint in
   12113             :  * Phase 3 and update the convalidated field in the pg_constraint catalog
   12114             :  * for the specified relation.
   12115             :  */
   12116             : static void
   12117         298 : QueueFKConstraintValidation(List **wqueue, Relation conrel, Relation rel,
   12118             :                             HeapTuple contuple, LOCKMODE lockmode)
   12119             : {
   12120             :     Form_pg_constraint con;
   12121             :     AlteredTableInfo *tab;
   12122             :     HeapTuple   copyTuple;
   12123             :     Form_pg_constraint copy_con;
   12124             : 
   12125         298 :     con = (Form_pg_constraint) GETSTRUCT(contuple);
   12126             :     Assert(con->contype == CONSTRAINT_FOREIGN);
   12127             : 
   12128         298 :     if (rel->rd_rel->relkind == RELKIND_RELATION)
   12129             :     {
   12130             :         NewConstraint *newcon;
   12131             :         Constraint *fkconstraint;
   12132             : 
   12133             :         /* Queue validation for phase 3 */
   12134         298 :         fkconstraint = makeNode(Constraint);
   12135             :         /* for now this is all we need */
   12136         298 :         fkconstraint->conname = pstrdup(NameStr(con->conname));
   12137             : 
   12138         298 :         newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
   12139         298 :         newcon->name = fkconstraint->conname;
   12140         298 :         newcon->contype = CONSTR_FOREIGN;
   12141         298 :         newcon->refrelid = con->confrelid;
   12142         298 :         newcon->refindid = con->conindid;
   12143         298 :         newcon->conid = con->oid;
   12144         298 :         newcon->qual = (Node *) fkconstraint;
   12145             : 
   12146             :         /* Find or create work queue entry for this table */
   12147         298 :         tab = ATGetQueueEntry(wqueue, rel);
   12148         298 :         tab->constraints = lappend(tab->constraints, newcon);
   12149             :     }
   12150             : 
   12151             :     /*
   12152             :      * We disallow creating invalid foreign keys to or from partitioned
   12153             :      * tables, so ignoring the recursion bit is okay.
   12154             :      */
   12155             : 
   12156             :     /*
   12157             :      * Now update the catalog, while we have the door open.
   12158             :      */
   12159         298 :     copyTuple = heap_copytuple(contuple);
   12160         298 :     copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
   12161         298 :     copy_con->convalidated = true;
   12162         298 :     CatalogTupleUpdate(conrel, &copyTuple->t_self, copyTuple);
   12163             : 
   12164         298 :     InvokeObjectPostAlterHook(ConstraintRelationId, con->oid, 0);
   12165             : 
   12166         298 :     heap_freetuple(copyTuple);
   12167         298 : }
   12168             : 
   12169             : /*
   12170             :  * QueueCheckConstraintValidation
   12171             :  *
   12172             :  * Add an entry to the wqueue to validate the given check constraint in Phase 3
   12173             :  * and update the convalidated field in the pg_constraint catalog for the
   12174             :  * specified relation and all its inheriting children.
   12175             :  */
   12176             : static void
   12177         120 : QueueCheckConstraintValidation(List **wqueue, Relation conrel, Relation rel,
   12178             :                                char *constrName, HeapTuple contuple,
   12179             :                                bool recurse, bool recursing, LOCKMODE lockmode)
   12180             : {
   12181             :     Form_pg_constraint con;
   12182             :     AlteredTableInfo *tab;
   12183             :     HeapTuple   copyTuple;
   12184             :     Form_pg_constraint copy_con;
   12185             : 
   12186         120 :     List       *children = NIL;
   12187             :     ListCell   *child;
   12188             :     NewConstraint *newcon;
   12189             :     Datum       val;
   12190             :     char       *conbin;
   12191             : 
   12192         120 :     con = (Form_pg_constraint) GETSTRUCT(contuple);
   12193             :     Assert(con->contype == CONSTRAINT_CHECK);
   12194             : 
   12195             :     /*
   12196             :      * If we're recursing, the parent has already done this, so skip it. Also,
   12197             :      * if the constraint is a NO INHERIT constraint, we shouldn't try to look
   12198             :      * for it in the children.
   12199             :      */
   12200         120 :     if (!recursing && !con->connoinherit)
   12201          66 :         children = find_all_inheritors(RelationGetRelid(rel),
   12202             :                                        lockmode, NULL);
   12203             : 
   12204             :     /*
   12205             :      * For CHECK constraints, we must ensure that we only mark the constraint
   12206             :      * as validated on the parent if it's already validated on the children.
   12207             :      *
   12208             :      * We recurse before validating on the parent, to reduce risk of
   12209             :      * deadlocks.
   12210             :      */
   12211         234 :     foreach(child, children)
   12212             :     {
   12213         114 :         Oid         childoid = lfirst_oid(child);
   12214             :         Relation    childrel;
   12215             : 
   12216         114 :         if (childoid == RelationGetRelid(rel))
   12217          66 :             continue;
   12218             : 
   12219             :         /*
   12220             :          * If we are told not to recurse, there had better not be any child
   12221             :          * tables, because we can't mark the constraint on the parent valid
   12222             :          * unless it is valid for all child tables.
   12223             :          */
   12224          48 :         if (!recurse)
   12225           0 :             ereport(ERROR,
   12226             :                     (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
   12227             :                      errmsg("constraint must be validated on child tables too")));
   12228             : 
   12229             :         /* find_all_inheritors already got lock */
   12230          48 :         childrel = table_open(childoid, NoLock);
   12231             : 
   12232          48 :         ATExecValidateConstraint(wqueue, childrel, constrName, false,
   12233             :                                  true, lockmode);
   12234          48 :         table_close(childrel, NoLock);
   12235             :     }
   12236             : 
   12237             :     /* Queue validation for phase 3 */
   12238         120 :     newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
   12239         120 :     newcon->name = constrName;
   12240         120 :     newcon->contype = CONSTR_CHECK;
   12241         120 :     newcon->refrelid = InvalidOid;
   12242         120 :     newcon->refindid = InvalidOid;
   12243         120 :     newcon->conid = con->oid;
   12244             : 
   12245         120 :     val = SysCacheGetAttrNotNull(CONSTROID, contuple,
   12246             :                                  Anum_pg_constraint_conbin);
   12247         120 :     conbin = TextDatumGetCString(val);
   12248         120 :     newcon->qual = (Node *) stringToNode(conbin);
   12249             : 
   12250             :     /* Find or create work queue entry for this table */
   12251         120 :     tab = ATGetQueueEntry(wqueue, rel);
   12252         120 :     tab->constraints = lappend(tab->constraints, newcon);
   12253             : 
   12254             :     /*
   12255             :      * Invalidate relcache so that others see the new validated constraint.
   12256             :      */
   12257         120 :     CacheInvalidateRelcache(rel);
   12258             : 
   12259             :     /*
   12260             :      * Now update the catalog, while we have the door open.
   12261             :      */
   12262         120 :     copyTuple = heap_copytuple(contuple);
   12263         120 :     copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
   12264         120 :     copy_con->convalidated = true;
   12265         120 :     CatalogTupleUpdate(conrel, &copyTuple->t_self, copyTuple);
   12266             : 
   12267         120 :     InvokeObjectPostAlterHook(ConstraintRelationId, con->oid, 0);
   12268             : 
   12269         120 :     heap_freetuple(copyTuple);
   12270         120 : }
   12271             : 
   12272             : /*
   12273             :  * transformColumnNameList - transform list of column names
   12274             :  *
   12275             :  * Lookup each name and return its attnum and, optionally, type and collation
   12276             :  * OIDs
   12277             :  *
   12278             :  * Note: the name of this function suggests that it's general-purpose,
   12279             :  * but actually it's only used to look up names appearing in foreign-key
   12280             :  * clauses.  The error messages would need work to use it in other cases,
   12281             :  * and perhaps the validity checks as well.
   12282             :  */
   12283             : static int
   12284        6280 : transformColumnNameList(Oid relId, List *colList,
   12285             :                         int16 *attnums, Oid *atttypids, Oid *attcollids)
   12286             : {
   12287             :     ListCell   *l;
   12288             :     int         attnum;
   12289             : 
   12290        6280 :     attnum = 0;
   12291       11484 :     foreach(l, colList)
   12292             :     {
   12293        5270 :         char       *attname = strVal(lfirst(l));
   12294             :         HeapTuple   atttuple;
   12295             :         Form_pg_attribute attform;
   12296             : 
   12297        5270 :         atttuple = SearchSysCacheAttName(relId, attname);
   12298        5270 :         if (!HeapTupleIsValid(atttuple))
   12299          54 :             ereport(ERROR,
   12300             :                     (errcode(ERRCODE_UNDEFINED_COLUMN),
   12301             :                      errmsg("column \"%s\" referenced in foreign key constraint does not exist",
   12302             :                             attname)));
   12303        5216 :         attform = (Form_pg_attribute) GETSTRUCT(atttuple);
   12304        5216 :         if (attform->attnum < 0)
   12305          12 :             ereport(ERROR,
   12306             :                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   12307             :                      errmsg("system columns cannot be used in foreign keys")));
   12308        5204 :         if (attnum >= INDEX_MAX_KEYS)
   12309           0 :             ereport(ERROR,
   12310             :                     (errcode(ERRCODE_TOO_MANY_COLUMNS),
   12311             :                      errmsg("cannot have more than %d keys in a foreign key",
   12312             :                             INDEX_MAX_KEYS)));
   12313        5204 :         attnums[attnum] = attform->attnum;
   12314        5204 :         if (atttypids != NULL)
   12315        5174 :             atttypids[attnum] = attform->atttypid;
   12316        5204 :         if (attcollids != NULL)
   12317        5174 :             attcollids[attnum] = attform->attcollation;
   12318        5204 :         ReleaseSysCache(atttuple);
   12319        5204 :         attnum++;
   12320             :     }
   12321             : 
   12322        6214 :     return attnum;
   12323             : }
   12324             : 
   12325             : /*
   12326             :  * transformFkeyGetPrimaryKey -
   12327             :  *
   12328             :  *  Look up the names, attnums, types, and collations of the primary key attributes
   12329             :  *  for the pkrel.  Also return the index OID and index opclasses of the
   12330             :  *  index supporting the primary key.  Also return whether the index has
   12331             :  *  WITHOUT OVERLAPS.
   12332             :  *
   12333             :  *  All parameters except pkrel are output parameters.  Also, the function
   12334             :  *  return value is the number of attributes in the primary key.
   12335             :  *
   12336             :  *  Used when the column list in the REFERENCES specification is omitted.
   12337             :  */
   12338             : static int
   12339        1112 : transformFkeyGetPrimaryKey(Relation pkrel, Oid *indexOid,
   12340             :                            List **attnamelist,
   12341             :                            int16 *attnums, Oid *atttypids, Oid *attcollids,
   12342             :                            Oid *opclasses, bool *pk_has_without_overlaps)
   12343             : {
   12344             :     List       *indexoidlist;
   12345             :     ListCell   *indexoidscan;
   12346        1112 :     HeapTuple   indexTuple = NULL;
   12347        1112 :     Form_pg_index indexStruct = NULL;
   12348             :     Datum       indclassDatum;
   12349             :     oidvector  *indclass;
   12350             :     int         i;
   12351             : 
   12352             :     /*
   12353             :      * Get the list of index OIDs for the table from the relcache, and look up
   12354             :      * each one in the pg_index syscache until we find one marked primary key
   12355             :      * (hopefully there isn't more than one such).  Insist it's valid, too.
   12356             :      */
   12357        1112 :     *indexOid = InvalidOid;
   12358             : 
   12359        1112 :     indexoidlist = RelationGetIndexList(pkrel);
   12360             : 
   12361        1118 :     foreach(indexoidscan, indexoidlist)
   12362             :     {
   12363        1118 :         Oid         indexoid = lfirst_oid(indexoidscan);
   12364             : 
   12365        1118 :         indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexoid));
   12366        1118 :         if (!HeapTupleIsValid(indexTuple))
   12367           0 :             elog(ERROR, "cache lookup failed for index %u", indexoid);
   12368        1118 :         indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
   12369        1118 :         if (indexStruct->indisprimary && indexStruct->indisvalid)
   12370             :         {
   12371             :             /*
   12372             :              * Refuse to use a deferrable primary key.  This is per SQL spec,
   12373             :              * and there would be a lot of interesting semantic problems if we
   12374             :              * tried to allow it.
   12375             :              */
   12376        1112 :             if (!indexStruct->indimmediate)
   12377           0 :                 ereport(ERROR,
   12378             :                         (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
   12379             :                          errmsg("cannot use a deferrable primary key for referenced table \"%s\"",
   12380             :                                 RelationGetRelationName(pkrel))));
   12381             : 
   12382        1112 :             *indexOid = indexoid;
   12383        1112 :             break;
   12384             :         }
   12385           6 :         ReleaseSysCache(indexTuple);
   12386             :     }
   12387             : 
   12388        1112 :     list_free(indexoidlist);
   12389             : 
   12390             :     /*
   12391             :      * Check that we found it
   12392             :      */
   12393        1112 :     if (!OidIsValid(*indexOid))
   12394           0 :         ereport(ERROR,
   12395             :                 (errcode(ERRCODE_UNDEFINED_OBJECT),
   12396             :                  errmsg("there is no primary key for referenced table \"%s\"",
   12397             :                         RelationGetRelationName(pkrel))));
   12398             : 
   12399             :     /* Must get indclass the hard way */
   12400        1112 :     indclassDatum = SysCacheGetAttrNotNull(INDEXRELID, indexTuple,
   12401             :                                            Anum_pg_index_indclass);
   12402        1112 :     indclass = (oidvector *) DatumGetPointer(indclassDatum);
   12403             : 
   12404             :     /*
   12405             :      * Now build the list of PK attributes from the indkey definition (we
   12406             :      * assume a primary key cannot have expressional elements)
   12407             :      */
   12408        1112 :     *attnamelist = NIL;
   12409        2612 :     for (i = 0; i < indexStruct->indnkeyatts; i++)
   12410             :     {
   12411        1500 :         int         pkattno = indexStruct->indkey.values[i];
   12412             : 
   12413        1500 :         attnums[i] = pkattno;
   12414        1500 :         atttypids[i] = attnumTypeId(pkrel, pkattno);
   12415        1500 :         attcollids[i] = attnumCollationId(pkrel, pkattno);
   12416        1500 :         opclasses[i] = indclass->values[i];
   12417        1500 :         *attnamelist = lappend(*attnamelist,
   12418        1500 :                                makeString(pstrdup(NameStr(*attnumAttName(pkrel, pkattno)))));
   12419             :     }
   12420             : 
   12421        1112 :     *pk_has_without_overlaps = indexStruct->indisexclusion;
   12422             : 
   12423        1112 :     ReleaseSysCache(indexTuple);
   12424             : 
   12425        1112 :     return i;
   12426             : }
   12427             : 
   12428             : /*
   12429             :  * transformFkeyCheckAttrs -
   12430             :  *
   12431             :  *  Validate that the 'attnums' columns in the 'pkrel' relation are valid to
   12432             :  *  reference as part of a foreign key constraint.
   12433             :  *
   12434             :  *  Returns the OID of the unique index supporting the constraint and
   12435             :  *  populates the caller-provided 'opclasses' array with the opclasses
   12436             :  *  associated with the index columns.  Also sets whether the index
   12437             :  *  uses WITHOUT OVERLAPS.
   12438             :  *
   12439             :  *  Raises an ERROR on validation failure.
   12440             :  */
   12441             : static Oid
   12442        1272 : transformFkeyCheckAttrs(Relation pkrel,
   12443             :                         int numattrs, int16 *attnums,
   12444             :                         bool with_period, Oid *opclasses,
   12445             :                         bool *pk_has_without_overlaps)
   12446             : {
   12447        1272 :     Oid         indexoid = InvalidOid;
   12448        1272 :     bool        found = false;
   12449        1272 :     bool        found_deferrable = false;
   12450             :     List       *indexoidlist;
   12451             :     ListCell   *indexoidscan;
   12452             :     int         i,
   12453             :                 j;
   12454             : 
   12455             :     /*
   12456             :      * Reject duplicate appearances of columns in the referenced-columns list.
   12457             :      * Such a case is forbidden by the SQL standard, and even if we thought it
   12458             :      * useful to allow it, there would be ambiguity about how to match the
   12459             :      * list to unique indexes (in particular, it'd be unclear which index
   12460             :      * opclass goes with which FK column).
   12461             :      */
   12462        2980 :     for (i = 0; i < numattrs; i++)
   12463             :     {
   12464        2262 :         for (j = i + 1; j < numattrs; j++)
   12465             :         {
   12466         554 :             if (attnums[i] == attnums[j])
   12467          24 :                 ereport(ERROR,
   12468             :                         (errcode(ERRCODE_INVALID_FOREIGN_KEY),
   12469             :                          errmsg("foreign key referenced-columns list must not contain duplicates")));
   12470             :         }
   12471             :     }
   12472             : 
   12473             :     /*
   12474             :      * Get the list of index OIDs for the table from the relcache, and look up
   12475             :      * each one in the pg_index syscache, and match unique indexes to the list
   12476             :      * of attnums we are given.
   12477             :      */
   12478        1248 :     indexoidlist = RelationGetIndexList(pkrel);
   12479             : 
   12480        1428 :     foreach(indexoidscan, indexoidlist)
   12481             :     {
   12482             :         HeapTuple   indexTuple;
   12483             :         Form_pg_index indexStruct;
   12484             : 
   12485        1416 :         indexoid = lfirst_oid(indexoidscan);
   12486        1416 :         indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexoid));
   12487        1416 :         if (!HeapTupleIsValid(indexTuple))
   12488           0 :             elog(ERROR, "cache lookup failed for index %u", indexoid);
   12489        1416 :         indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
   12490             : 
   12491             :         /*
   12492             :          * Must have the right number of columns; must be unique (or if
   12493             :          * temporal then exclusion instead) and not a partial index; forget it
   12494             :          * if there are any expressions, too. Invalid indexes are out as well.
   12495             :          */
   12496        2724 :         if (indexStruct->indnkeyatts == numattrs &&
   12497        1308 :             (with_period ? indexStruct->indisexclusion : indexStruct->indisunique) &&
   12498        2588 :             indexStruct->indisvalid &&
   12499        2588 :             heap_attisnull(indexTuple, Anum_pg_index_indpred, NULL) &&
   12500        1294 :             heap_attisnull(indexTuple, Anum_pg_index_indexprs, NULL))
   12501             :         {
   12502             :             Datum       indclassDatum;
   12503             :             oidvector  *indclass;
   12504             : 
   12505             :             /* Must get indclass the hard way */
   12506        1294 :             indclassDatum = SysCacheGetAttrNotNull(INDEXRELID, indexTuple,
   12507             :                                                    Anum_pg_index_indclass);
   12508        1294 :             indclass = (oidvector *) DatumGetPointer(indclassDatum);
   12509             : 
   12510             :             /*
   12511             :              * The given attnum list may match the index columns in any order.
   12512             :              * Check for a match, and extract the appropriate opclasses while
   12513             :              * we're at it.
   12514             :              *
   12515             :              * We know that attnums[] is duplicate-free per the test at the
   12516             :              * start of this function, and we checked above that the number of
   12517             :              * index columns agrees, so if we find a match for each attnums[]
   12518             :              * entry then we must have a one-to-one match in some order.
   12519             :              */
   12520        2990 :             for (i = 0; i < numattrs; i++)
   12521             :             {
   12522        1754 :                 found = false;
   12523        2342 :                 for (j = 0; j < numattrs; j++)
   12524             :                 {
   12525        2284 :                     if (attnums[i] == indexStruct->indkey.values[j])
   12526             :                     {
   12527        1696 :                         opclasses[i] = indclass->values[j];
   12528        1696 :                         found = true;
   12529        1696 :                         break;
   12530             :                     }
   12531             :                 }
   12532        1754 :                 if (!found)
   12533          58 :                     break;
   12534             :             }
   12535             :             /* The last attribute in the index must be the PERIOD FK part */
   12536        1294 :             if (found && with_period)
   12537             :             {
   12538         130 :                 int16       periodattnum = attnums[numattrs - 1];
   12539             : 
   12540         130 :                 found = (periodattnum == indexStruct->indkey.values[numattrs - 1]);
   12541             :             }
   12542             : 
   12543             :             /*
   12544             :              * Refuse to use a deferrable unique/primary key.  This is per SQL
   12545             :              * spec, and there would be a lot of interesting semantic problems
   12546             :              * if we tried to allow it.
   12547             :              */
   12548        1294 :             if (found && !indexStruct->indimmediate)
   12549             :             {
   12550             :                 /*
   12551             :                  * Remember that we found an otherwise matching index, so that
   12552             :                  * we can generate a more appropriate error message.
   12553             :                  */
   12554           0 :                 found_deferrable = true;
   12555           0 :                 found = false;
   12556             :             }
   12557             : 
   12558             :             /* We need to know whether the index has WITHOUT OVERLAPS */
   12559        1294 :             if (found)
   12560        1236 :                 *pk_has_without_overlaps = indexStruct->indisexclusion;
   12561             :         }
   12562        1416 :         ReleaseSysCache(indexTuple);
   12563        1416 :         if (found)
   12564        1236 :             break;
   12565             :     }
   12566             : 
   12567        1248 :     if (!found)
   12568             :     {
   12569          12 :         if (found_deferrable)
   12570           0 :             ereport(ERROR,
   12571             :                     (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
   12572             :                      errmsg("cannot use a deferrable unique constraint for referenced table \"%s\"",
   12573             :                             RelationGetRelationName(pkrel))));
   12574             :         else
   12575          12 :             ereport(ERROR,
   12576             :                     (errcode(ERRCODE_INVALID_FOREIGN_KEY),
   12577             :                      errmsg("there is no unique constraint matching given keys for referenced table \"%s\"",
   12578             :                             RelationGetRelationName(pkrel))));
   12579             :     }
   12580             : 
   12581        1236 :     list_free(indexoidlist);
   12582             : 
   12583        1236 :     return indexoid;
   12584             : }
   12585             : 
   12586             : /*
   12587             :  * findFkeyCast -
   12588             :  *
   12589             :  *  Wrapper around find_coercion_pathway() for ATAddForeignKeyConstraint().
   12590             :  *  Caller has equal regard for binary coercibility and for an exact match.
   12591             : */
   12592             : static CoercionPathType
   12593          12 : findFkeyCast(Oid targetTypeId, Oid sourceTypeId, Oid *funcid)
   12594             : {
   12595             :     CoercionPathType ret;
   12596             : 
   12597          12 :     if (targetTypeId == sourceTypeId)
   12598             :     {
   12599          12 :         ret = COERCION_PATH_RELABELTYPE;
   12600          12 :         *funcid = InvalidOid;
   12601             :     }
   12602             :     else
   12603             :     {
   12604           0 :         ret = find_coercion_pathway(targetTypeId, sourceTypeId,
   12605             :                                     COERCION_IMPLICIT, funcid);
   12606           0 :         if (ret == COERCION_PATH_NONE)
   12607             :             /* A previously-relied-upon cast is now gone. */
   12608           0 :             elog(ERROR, "could not find cast from %u to %u",
   12609             :                  sourceTypeId, targetTypeId);
   12610             :     }
   12611             : 
   12612          12 :     return ret;
   12613             : }
   12614             : 
   12615             : /*
   12616             :  * Permissions checks on the referenced table for ADD FOREIGN KEY
   12617             :  *
   12618             :  * Note: we have already checked that the user owns the referencing table,
   12619             :  * else we'd have failed much earlier; no additional checks are needed for it.
   12620             :  */
   12621             : static void
   12622        2312 : checkFkeyPermissions(Relation rel, int16 *attnums, int natts)
   12623             : {
   12624        2312 :     Oid         roleid = GetUserId();
   12625             :     AclResult   aclresult;
   12626             :     int         i;
   12627             : 
   12628             :     /* Okay if we have relation-level REFERENCES permission */
   12629        2312 :     aclresult = pg_class_aclcheck(RelationGetRelid(rel), roleid,
   12630             :                                   ACL_REFERENCES);
   12631        2312 :     if (aclresult == ACLCHECK_OK)
   12632        2312 :         return;
   12633             :     /* Else we must have REFERENCES on each column */
   12634           0 :     for (i = 0; i < natts; i++)
   12635             :     {
   12636           0 :         aclresult = pg_attribute_aclcheck(RelationGetRelid(rel), attnums[i],
   12637             :                                           roleid, ACL_REFERENCES);
   12638           0 :         if (aclresult != ACLCHECK_OK)
   12639           0 :             aclcheck_error(aclresult, get_relkind_objtype(rel->rd_rel->relkind),
   12640           0 :                            RelationGetRelationName(rel));
   12641             :     }
   12642             : }
   12643             : 
   12644             : /*
   12645             :  * Scan the existing rows in a table to verify they meet a proposed FK
   12646             :  * constraint.
   12647             :  *
   12648             :  * Caller must have opened and locked both relations appropriately.
   12649             :  */
   12650             : static void
   12651        1108 : validateForeignKeyConstraint(char *conname,
   12652             :                              Relation rel,
   12653             :                              Relation pkrel,
   12654             :                              Oid pkindOid,
   12655             :                              Oid constraintOid,
   12656             :                              bool hasperiod)
   12657             : {
   12658             :     TupleTableSlot *slot;
   12659             :     TableScanDesc scan;
   12660        1108 :     Trigger     trig = {0};
   12661             :     Snapshot    snapshot;
   12662             :     MemoryContext oldcxt;
   12663             :     MemoryContext perTupCxt;
   12664             : 
   12665        1108 :     ereport(DEBUG1,
   12666             :             (errmsg_internal("validating foreign key constraint \"%s\"", conname)));
   12667             : 
   12668             :     /*
   12669             :      * Build a trigger call structure; we'll need it either way.
   12670             :      */
   12671        1108 :     trig.tgoid = InvalidOid;
   12672        1108 :     trig.tgname = conname;
   12673        1108 :     trig.tgenabled = TRIGGER_FIRES_ON_ORIGIN;
   12674        1108 :     trig.tgisinternal = true;
   12675        1108 :     trig.tgconstrrelid = RelationGetRelid(pkrel);
   12676        1108 :     trig.tgconstrindid = pkindOid;
   12677        1108 :     trig.tgconstraint = constraintOid;
   12678        1108 :     trig.tgdeferrable = false;
   12679        1108 :     trig.tginitdeferred = false;
   12680             :     /* we needn't fill in remaining fields */
   12681             : 
   12682             :     /*
   12683             :      * See if we can do it with a single LEFT JOIN query.  A false result
   12684             :      * indicates we must proceed with the fire-the-trigger method. We can't do
   12685             :      * a LEFT JOIN for temporal FKs yet, but we can once we support temporal
   12686             :      * left joins.
   12687             :      */
   12688        1108 :     if (!hasperiod && RI_Initial_Check(&trig, rel, pkrel))
   12689         888 :         return;
   12690             : 
   12691             :     /*
   12692             :      * Scan through each tuple, calling RI_FKey_check_ins (insert trigger) as
   12693             :      * if that tuple had just been inserted.  If any of those fail, it should
   12694             :      * ereport(ERROR) and that's that.
   12695             :      */
   12696         158 :     snapshot = RegisterSnapshot(GetLatestSnapshot());
   12697         158 :     slot = table_slot_create(rel, NULL);
   12698         158 :     scan = table_beginscan(rel, snapshot, 0, NULL);
   12699             : 
   12700         158 :     perTupCxt = AllocSetContextCreate(CurrentMemoryContext,
   12701             :                                       "validateForeignKeyConstraint",
   12702             :                                       ALLOCSET_SMALL_SIZES);
   12703         158 :     oldcxt = MemoryContextSwitchTo(perTupCxt);
   12704             : 
   12705         242 :     while (table_scan_getnextslot(scan, ForwardScanDirection, slot))
   12706             :     {
   12707         102 :         LOCAL_FCINFO(fcinfo, 0);
   12708         102 :         TriggerData trigdata = {0};
   12709             : 
   12710         102 :         CHECK_FOR_INTERRUPTS();
   12711             : 
   12712             :         /*
   12713             :          * Make a call to the trigger function
   12714             :          *
   12715             :          * No parameters are passed, but we do set a context
   12716             :          */
   12717         510 :         MemSet(fcinfo, 0, SizeForFunctionCallInfo(0));
   12718             : 
   12719             :         /*
   12720             :          * We assume RI_FKey_check_ins won't look at flinfo...
   12721             :          */
   12722         102 :         trigdata.type = T_TriggerData;
   12723         102 :         trigdata.tg_event = TRIGGER_EVENT_INSERT | TRIGGER_EVENT_ROW;
   12724         102 :         trigdata.tg_relation = rel;
   12725         102 :         trigdata.tg_trigtuple = ExecFetchSlotHeapTuple(slot, false, NULL);
   12726         102 :         trigdata.tg_trigslot = slot;
   12727         102 :         trigdata.tg_trigger = &trig;
   12728             : 
   12729         102 :         fcinfo->context = (Node *) &trigdata;
   12730             : 
   12731         102 :         RI_FKey_check_ins(fcinfo);
   12732             : 
   12733          84 :         MemoryContextReset(perTupCxt);
   12734             :     }
   12735             : 
   12736         140 :     MemoryContextSwitchTo(oldcxt);
   12737         140 :     MemoryContextDelete(perTupCxt);
   12738         140 :     table_endscan(scan);
   12739         140 :     UnregisterSnapshot(snapshot);
   12740         140 :     ExecDropSingleTupleTableSlot(slot);
   12741             : }
   12742             : 
   12743             : /*
   12744             :  * CreateFKCheckTrigger
   12745             :  *      Creates the insert (on_insert=true) or update "check" trigger that
   12746             :  *      implements a given foreign key
   12747             :  *
   12748             :  * Returns the OID of the so created trigger.
   12749             :  */
   12750             : static Oid
   12751        5576 : CreateFKCheckTrigger(Oid myRelOid, Oid refRelOid, Constraint *fkconstraint,
   12752             :                      Oid constraintOid, Oid indexOid, Oid parentTrigOid,
   12753             :                      bool on_insert)
   12754             : {
   12755             :     ObjectAddress trigAddress;
   12756             :     CreateTrigStmt *fk_trigger;
   12757             : 
   12758             :     /*
   12759             :      * Note: for a self-referential FK (referencing and referenced tables are
   12760             :      * the same), it is important that the ON UPDATE action fires before the
   12761             :      * CHECK action, since both triggers will fire on the same row during an
   12762             :      * UPDATE event; otherwise the CHECK trigger will be checking a non-final
   12763             :      * state of the row.  Triggers fire in name order, so we ensure this by
   12764             :      * using names like "RI_ConstraintTrigger_a_NNNN" for the action triggers
   12765             :      * and "RI_ConstraintTrigger_c_NNNN" for the check triggers.
   12766             :      */
   12767        5576 :     fk_trigger = makeNode(CreateTrigStmt);
   12768        5576 :     fk_trigger->replace = false;
   12769        5576 :     fk_trigger->isconstraint = true;
   12770        5576 :     fk_trigger->trigname = "RI_ConstraintTrigger_c";
   12771        5576 :     fk_trigger->relation = NULL;
   12772             : 
   12773             :     /* Either ON INSERT or ON UPDATE */
   12774        5576 :     if (on_insert)
   12775             :     {
   12776        2788 :         fk_trigger->funcname = SystemFuncName("RI_FKey_check_ins");
   12777        2788 :         fk_trigger->events = TRIGGER_TYPE_INSERT;
   12778             :     }
   12779             :     else
   12780             :     {
   12781        2788 :         fk_trigger->funcname = SystemFuncName("RI_FKey_check_upd");
   12782        2788 :         fk_trigger->events = TRIGGER_TYPE_UPDATE;
   12783             :     }
   12784             : 
   12785        5576 :     fk_trigger->args = NIL;
   12786        5576 :     fk_trigger->row = true;
   12787        5576 :     fk_trigger->timing = TRIGGER_TYPE_AFTER;
   12788        5576 :     fk_trigger->columns = NIL;
   12789        5576 :     fk_trigger->whenClause = NULL;
   12790        5576 :     fk_trigger->transitionRels = NIL;
   12791        5576 :     fk_trigger->deferrable = fkconstraint->deferrable;
   12792        5576 :     fk_trigger->initdeferred = fkconstraint->initdeferred;
   12793        5576 :     fk_trigger->constrrel = NULL;
   12794             : 
   12795        5576 :     trigAddress = CreateTrigger(fk_trigger, NULL, myRelOid, refRelOid,
   12796             :                                 constraintOid, indexOid, InvalidOid,
   12797             :                                 parentTrigOid, NULL, true, false);
   12798             : 
   12799             :     /* Make changes-so-far visible */
   12800        5576 :     CommandCounterIncrement();
   12801             : 
   12802        5576 :     return trigAddress.objectId;
   12803             : }
   12804             : 
   12805             : /*
   12806             :  * createForeignKeyActionTriggers
   12807             :  *      Create the referenced-side "action" triggers that implement a foreign
   12808             :  *      key.
   12809             :  *
   12810             :  * Returns the OIDs of the so created triggers in *deleteTrigOid and
   12811             :  * *updateTrigOid.
   12812             :  */
   12813             : static void
   12814        2948 : createForeignKeyActionTriggers(Relation rel, Oid refRelOid, Constraint *fkconstraint,
   12815             :                                Oid constraintOid, Oid indexOid,
   12816             :                                Oid parentDelTrigger, Oid parentUpdTrigger,
   12817             :                                Oid *deleteTrigOid, Oid *updateTrigOid)
   12818             : {
   12819             :     CreateTrigStmt *fk_trigger;
   12820             :     ObjectAddress trigAddress;
   12821             : 
   12822             :     /*
   12823             :      * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
   12824             :      * DELETE action on the referenced table.
   12825             :      */
   12826        2948 :     fk_trigger = makeNode(CreateTrigStmt);
   12827        2948 :     fk_trigger->replace = false;
   12828        2948 :     fk_trigger->isconstraint = true;
   12829        2948 :     fk_trigger->trigname = "RI_ConstraintTrigger_a";
   12830        2948 :     fk_trigger->relation = NULL;
   12831        2948 :     fk_trigger->args = NIL;
   12832        2948 :     fk_trigger->row = true;
   12833        2948 :     fk_trigger->timing = TRIGGER_TYPE_AFTER;
   12834        2948 :     fk_trigger->events = TRIGGER_TYPE_DELETE;
   12835        2948 :     fk_trigger->columns = NIL;
   12836        2948 :     fk_trigger->whenClause = NULL;
   12837        2948 :     fk_trigger->transitionRels = NIL;
   12838        2948 :     fk_trigger->constrrel = NULL;
   12839             : 
   12840        2948 :     switch (fkconstraint->fk_del_action)
   12841             :     {
   12842        2280 :         case FKCONSTR_ACTION_NOACTION:
   12843        2280 :             fk_trigger->deferrable = fkconstraint->deferrable;
   12844        2280 :             fk_trigger->initdeferred = fkconstraint->initdeferred;
   12845        2280 :             fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_del");
   12846        2280 :             break;
   12847          82 :         case FKCONSTR_ACTION_RESTRICT:
   12848          82 :             fk_trigger->deferrable = false;
   12849          82 :             fk_trigger->initdeferred = false;
   12850          82 :             fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_del");
   12851          82 :             break;
   12852         428 :         case FKCONSTR_ACTION_CASCADE:
   12853         428 :             fk_trigger->deferrable = false;
   12854         428 :             fk_trigger->initdeferred = false;
   12855         428 :             fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_del");
   12856         428 :             break;
   12857          98 :         case FKCONSTR_ACTION_SETNULL:
   12858          98 :             fk_trigger->deferrable = false;
   12859          98 :             fk_trigger->initdeferred = false;
   12860          98 :             fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_del");
   12861          98 :             break;
   12862          60 :         case FKCONSTR_ACTION_SETDEFAULT:
   12863          60 :             fk_trigger->deferrable = false;
   12864          60 :             fk_trigger->initdeferred = false;
   12865          60 :             fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_del");
   12866          60 :             break;
   12867           0 :         default:
   12868           0 :             elog(ERROR, "unrecognized FK action type: %d",
   12869             :                  (int) fkconstraint->fk_del_action);
   12870             :             break;
   12871             :     }
   12872             : 
   12873        2948 :     trigAddress = CreateTrigger(fk_trigger, NULL, refRelOid,
   12874             :                                 RelationGetRelid(rel),
   12875             :                                 constraintOid, indexOid, InvalidOid,
   12876             :                                 parentDelTrigger, NULL, true, false);
   12877        2948 :     if (deleteTrigOid)
   12878        2948 :         *deleteTrigOid = trigAddress.objectId;
   12879             : 
   12880             :     /* Make changes-so-far visible */
   12881        2948 :     CommandCounterIncrement();
   12882             : 
   12883             :     /*
   12884             :      * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
   12885             :      * UPDATE action on the referenced table.
   12886             :      */
   12887        2948 :     fk_trigger = makeNode(CreateTrigStmt);
   12888        2948 :     fk_trigger->replace = false;
   12889        2948 :     fk_trigger->isconstraint = true;
   12890        2948 :     fk_trigger->trigname = "RI_ConstraintTrigger_a";
   12891        2948 :     fk_trigger->relation = NULL;
   12892        2948 :     fk_trigger->args = NIL;
   12893        2948 :     fk_trigger->row = true;
   12894        2948 :     fk_trigger->timing = TRIGGER_TYPE_AFTER;
   12895        2948 :     fk_trigger->events = TRIGGER_TYPE_UPDATE;
   12896        2948 :     fk_trigger->columns = NIL;
   12897        2948 :     fk_trigger->whenClause = NULL;
   12898        2948 :     fk_trigger->transitionRels = NIL;
   12899        2948 :     fk_trigger->constrrel = NULL;
   12900             : 
   12901        2948 :     switch (fkconstraint->fk_upd_action)
   12902             :     {
   12903        2514 :         case FKCONSTR_ACTION_NOACTION:
   12904        2514 :             fk_trigger->deferrable = fkconstraint->deferrable;
   12905        2514 :             fk_trigger->initdeferred = fkconstraint->initdeferred;
   12906        2514 :             fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_upd");
   12907        2514 :             break;
   12908          48 :         case FKCONSTR_ACTION_RESTRICT:
   12909          48 :             fk_trigger->deferrable = false;
   12910          48 :             fk_trigger->initdeferred = false;
   12911          48 :             fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_upd");
   12912          48 :             break;
   12913         282 :         case FKCONSTR_ACTION_CASCADE:
   12914         282 :             fk_trigger->deferrable = false;
   12915         282 :             fk_trigger->initdeferred = false;
   12916         282 :             fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_upd");
   12917         282 :             break;
   12918          62 :         case FKCONSTR_ACTION_SETNULL:
   12919          62 :             fk_trigger->deferrable = false;
   12920          62 :             fk_trigger->initdeferred = false;
   12921          62 :             fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_upd");
   12922          62 :             break;
   12923          42 :         case FKCONSTR_ACTION_SETDEFAULT:
   12924          42 :             fk_trigger->deferrable = false;
   12925          42 :             fk_trigger->initdeferred = false;
   12926          42 :             fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_upd");
   12927          42 :             break;
   12928           0 :         default:
   12929           0 :             elog(ERROR, "unrecognized FK action type: %d",
   12930             :                  (int) fkconstraint->fk_upd_action);
   12931             :             break;
   12932             :     }
   12933             : 
   12934        2948 :     trigAddress = CreateTrigger(fk_trigger, NULL, refRelOid,
   12935             :                                 RelationGetRelid(rel),
   12936             :                                 constraintOid, indexOid, InvalidOid,
   12937             :                                 parentUpdTrigger, NULL, true, false);
   12938        2948 :     if (updateTrigOid)
   12939        2948 :         *updateTrigOid = trigAddress.objectId;
   12940        2948 : }
   12941             : 
   12942             : /*
   12943             :  * createForeignKeyCheckTriggers
   12944             :  *      Create the referencing-side "check" triggers that implement a foreign
   12945             :  *      key.
   12946             :  *
   12947             :  * Returns the OIDs of the so created triggers in *insertTrigOid and
   12948             :  * *updateTrigOid.
   12949             :  */
   12950             : static void
   12951        2788 : createForeignKeyCheckTriggers(Oid myRelOid, Oid refRelOid,
   12952             :                               Constraint *fkconstraint, Oid constraintOid,
   12953             :                               Oid indexOid,
   12954             :                               Oid parentInsTrigger, Oid parentUpdTrigger,
   12955             :                               Oid *insertTrigOid, Oid *updateTrigOid)
   12956             : {
   12957        2788 :     *insertTrigOid = CreateFKCheckTrigger(myRelOid, refRelOid, fkconstraint,
   12958             :                                           constraintOid, indexOid,
   12959             :                                           parentInsTrigger, true);
   12960        2788 :     *updateTrigOid = CreateFKCheckTrigger(myRelOid, refRelOid, fkconstraint,
   12961             :                                           constraintOid, indexOid,
   12962             :                                           parentUpdTrigger, false);
   12963        2788 : }
   12964             : 
   12965             : /*
   12966             :  * ALTER TABLE DROP CONSTRAINT
   12967             :  *
   12968             :  * Like DROP COLUMN, we can't use the normal ALTER TABLE recursion mechanism.
   12969             :  */
   12970             : static void
   12971         816 : ATExecDropConstraint(Relation rel, const char *constrName,
   12972             :                      DropBehavior behavior, bool recurse,
   12973             :                      bool missing_ok, LOCKMODE lockmode)
   12974             : {
   12975             :     Relation    conrel;
   12976             :     SysScanDesc scan;
   12977             :     ScanKeyData skey[3];
   12978             :     HeapTuple   tuple;
   12979         816 :     bool        found = false;
   12980             : 
   12981         816 :     conrel = table_open(ConstraintRelationId, RowExclusiveLock);
   12982             : 
   12983             :     /*
   12984             :      * Find and drop the target constraint
   12985             :      */
   12986         816 :     ScanKeyInit(&skey[0],
   12987             :                 Anum_pg_constraint_conrelid,
   12988             :                 BTEqualStrategyNumber, F_OIDEQ,
   12989             :                 ObjectIdGetDatum(RelationGetRelid(rel)));
   12990         816 :     ScanKeyInit(&skey[1],
   12991             :                 Anum_pg_constraint_contypid,
   12992             :                 BTEqualStrategyNumber, F_OIDEQ,
   12993             :                 ObjectIdGetDatum(InvalidOid));
   12994         816 :     ScanKeyInit(&skey[2],
   12995             :                 Anum_pg_constraint_conname,
   12996             :                 BTEqualStrategyNumber, F_NAMEEQ,
   12997             :                 CStringGetDatum(constrName));
   12998         816 :     scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
   12999             :                               true, NULL, 3, skey);
   13000             : 
   13001             :     /* There can be at most one matching row */
   13002         816 :     if (HeapTupleIsValid(tuple = systable_getnext(scan)))
   13003             :     {
   13004         780 :         dropconstraint_internal(rel, tuple, behavior, recurse, false,
   13005             :                                 missing_ok, lockmode);
   13006         594 :         found = true;
   13007             :     }
   13008             : 
   13009         630 :     systable_endscan(scan);
   13010             : 
   13011         630 :     if (!found)
   13012             :     {
   13013          36 :         if (!missing_ok)
   13014          24 :             ereport(ERROR,
   13015             :                     errcode(ERRCODE_UNDEFINED_OBJECT),
   13016             :                     errmsg("constraint \"%s\" of relation \"%s\" does not exist",
   13017             :                            constrName, RelationGetRelationName(rel)));
   13018             :         else
   13019          12 :             ereport(NOTICE,
   13020             :                     errmsg("constraint \"%s\" of relation \"%s\" does not exist, skipping",
   13021             :                            constrName, RelationGetRelationName(rel)));
   13022             :     }
   13023             : 
   13024         606 :     table_close(conrel, RowExclusiveLock);
   13025         606 : }
   13026             : 
   13027             : /*
   13028             :  * Remove a constraint, using its pg_constraint tuple
   13029             :  *
   13030             :  * Implementation for ALTER TABLE DROP CONSTRAINT and ALTER TABLE ALTER COLUMN
   13031             :  * DROP NOT NULL.
   13032             :  *
   13033             :  * Returns the address of the constraint being removed.
   13034             :  */
   13035             : static ObjectAddress
   13036        1198 : dropconstraint_internal(Relation rel, HeapTuple constraintTup, DropBehavior behavior,
   13037             :                         bool recurse, bool recursing, bool missing_ok,
   13038             :                         LOCKMODE lockmode)
   13039             : {
   13040             :     Relation    conrel;
   13041             :     Form_pg_constraint con;
   13042             :     ObjectAddress conobj;
   13043             :     List       *children;
   13044        1198 :     bool        is_no_inherit_constraint = false;
   13045             :     char       *constrName;
   13046        1198 :     char       *colname = NULL;
   13047             : 
   13048             :     /* Guard against stack overflow due to overly deep inheritance tree. */
   13049        1198 :     check_stack_depth();
   13050             : 
   13051             :     /* At top level, permission check was done in ATPrepCmd, else do it */
   13052        1198 :     if (recursing)
   13053         210 :         ATSimplePermissions(AT_DropConstraint, rel,
   13054             :                             ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
   13055             : 
   13056        1192 :     conrel = table_open(ConstraintRelationId, RowExclusiveLock);
   13057             : 
   13058        1192 :     con = (Form_pg_constraint) GETSTRUCT(constraintTup);
   13059        1192 :     constrName = NameStr(con->conname);
   13060             : 
   13061             :     /* Don't allow drop of inherited constraints */
   13062        1192 :     if (con->coninhcount > 0 && !recursing)
   13063         156 :         ereport(ERROR,
   13064             :                 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
   13065             :                  errmsg("cannot drop inherited constraint \"%s\" of relation \"%s\"",
   13066             :                         constrName, RelationGetRelationName(rel))));
   13067             : 
   13068             :     /*
   13069             :      * Reset pg_constraint.attnotnull, if this is a not-null constraint.
   13070             :      *
   13071             :      * While doing that, we're in a good position to disallow dropping a not-
   13072             :      * null constraint underneath a primary key, a replica identity index, or
   13073             :      * a generated identity column.
   13074             :      */
   13075        1036 :     if (con->contype == CONSTRAINT_NOTNULL)
   13076             :     {
   13077         278 :         Relation    attrel = table_open(AttributeRelationId, RowExclusiveLock);
   13078         278 :         AttrNumber  attnum = extractNotNullColumn(constraintTup);
   13079             :         Bitmapset  *pkattrs;
   13080             :         Bitmapset  *irattrs;
   13081             :         HeapTuple   atttup;
   13082             :         Form_pg_attribute attForm;
   13083             : 
   13084             :         /* save column name for recursion step */
   13085         278 :         colname = get_attname(RelationGetRelid(rel), attnum, false);
   13086             : 
   13087             :         /*
   13088             :          * Disallow if it's in the primary key.  For partitioned tables we
   13089             :          * cannot rely solely on RelationGetIndexAttrBitmap, because it'll
   13090             :          * return NULL if the primary key is invalid; but we still need to
   13091             :          * protect not-null constraints under such a constraint, so check the
   13092             :          * slow way.
   13093             :          */
   13094         278 :         pkattrs = RelationGetIndexAttrBitmap(rel, INDEX_ATTR_BITMAP_PRIMARY_KEY);
   13095             : 
   13096         278 :         if (pkattrs == NULL &&
   13097         242 :             rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
   13098             :         {
   13099          18 :             Oid         pkindex = RelationGetPrimaryKeyIndex(rel, true);
   13100             : 
   13101          18 :             if (OidIsValid(pkindex))
   13102             :             {
   13103           0 :                 Relation    pk = relation_open(pkindex, AccessShareLock);
   13104             : 
   13105           0 :                 pkattrs = NULL;
   13106           0 :                 for (int i = 0; i < pk->rd_index->indnkeyatts; i++)
   13107           0 :                     pkattrs = bms_add_member(pkattrs, pk->rd_index->indkey.values[i]);
   13108             : 
   13109           0 :                 relation_close(pk, AccessShareLock);
   13110             :             }
   13111             :         }
   13112             : 
   13113         314 :         if (pkattrs &&
   13114          36 :             bms_is_member(attnum - FirstLowInvalidHeapAttributeNumber, pkattrs))
   13115          24 :             ereport(ERROR,
   13116             :                     errcode(ERRCODE_INVALID_TABLE_DEFINITION),
   13117             :                     errmsg("column \"%s\" is in a primary key",
   13118             :                            get_attname(RelationGetRelid(rel), attnum, false)));
   13119             : 
   13120             :         /* Disallow if it's in the replica identity */
   13121         254 :         irattrs = RelationGetIndexAttrBitmap(rel, INDEX_ATTR_BITMAP_IDENTITY_KEY);
   13122         254 :         if (bms_is_member(attnum - FirstLowInvalidHeapAttributeNumber, irattrs))
   13123          12 :             ereport(ERROR,
   13124             :                     errcode(ERRCODE_INVALID_TABLE_DEFINITION),
   13125             :                     errmsg("column \"%s\" is in index used as replica identity",
   13126             :                            get_attname(RelationGetRelid(rel), attnum, false)));
   13127             : 
   13128             :         /* Disallow if it's a GENERATED AS IDENTITY column */
   13129         242 :         atttup = SearchSysCacheCopyAttNum(RelationGetRelid(rel), attnum);
   13130         242 :         if (!HeapTupleIsValid(atttup))
   13131           0 :             elog(ERROR, "cache lookup failed for attribute %d of relation %u",
   13132             :                  attnum, RelationGetRelid(rel));
   13133         242 :         attForm = (Form_pg_attribute) GETSTRUCT(atttup);
   13134         242 :         if (attForm->attidentity != '\0')
   13135           0 :             ereport(ERROR,
   13136             :                     errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
   13137             :                     errmsg("column \"%s\" of relation \"%s\" is an identity column",
   13138             :                            get_attname(RelationGetRelid(rel), attnum,
   13139             :                                        false),
   13140             :                            RelationGetRelationName(rel)));
   13141             : 
   13142             :         /* All good -- reset attnotnull if needed */
   13143         242 :         if (attForm->attnotnull)
   13144             :         {
   13145         242 :             attForm->attnotnull = false;
   13146         242 :             CatalogTupleUpdate(attrel, &atttup->t_self, atttup);
   13147             :         }
   13148             : 
   13149         242 :         table_close(attrel, RowExclusiveLock);
   13150             :     }
   13151             : 
   13152        1000 :     is_no_inherit_constraint = con->connoinherit;
   13153             : 
   13154             :     /*
   13155             :      * If it's a foreign-key constraint, we'd better lock the referenced table
   13156             :      * and check that that's not in use, just as we've already done for the
   13157             :      * constrained table (else we might, eg, be dropping a trigger that has
   13158             :      * unfired events).  But we can/must skip that in the self-referential
   13159             :      * case.
   13160             :      */
   13161        1000 :     if (con->contype == CONSTRAINT_FOREIGN &&
   13162         216 :         con->confrelid != RelationGetRelid(rel))
   13163             :     {
   13164             :         Relation    frel;
   13165             : 
   13166             :         /* Must match lock taken by RemoveTriggerById: */
   13167         216 :         frel = table_open(con->confrelid, AccessExclusiveLock);
   13168         216 :         CheckAlterTableIsSafe(frel);
   13169         210 :         table_close(frel, NoLock);
   13170             :     }
   13171             : 
   13172             :     /*
   13173             :      * Perform the actual constraint deletion
   13174             :      */
   13175         994 :     ObjectAddressSet(conobj, ConstraintRelationId, con->oid);
   13176         994 :     performDeletion(&conobj, behavior, 0);
   13177             : 
   13178             :     /*
   13179             :      * For partitioned tables, non-CHECK, non-NOT-NULL inherited constraints
   13180             :      * are dropped via the dependency mechanism, so we're done here.
   13181             :      */
   13182         958 :     if (con->contype != CONSTRAINT_CHECK &&
   13183         640 :         con->contype != CONSTRAINT_NOTNULL &&
   13184         398 :         rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
   13185             :     {
   13186          90 :         table_close(conrel, RowExclusiveLock);
   13187          90 :         return conobj;
   13188             :     }
   13189             : 
   13190             :     /*
   13191             :      * Propagate to children as appropriate.  Unlike most other ALTER
   13192             :      * routines, we have to do this one level of recursion at a time; we can't
   13193             :      * use find_all_inheritors to do it in one pass.
   13194             :      */
   13195         868 :     if (!is_no_inherit_constraint)
   13196         554 :         children = find_inheritance_children(RelationGetRelid(rel), lockmode);
   13197             :     else
   13198         314 :         children = NIL;
   13199             : 
   13200        2102 :     foreach_oid(childrelid, children)
   13201             :     {
   13202             :         Relation    childrel;
   13203             :         HeapTuple   tuple;
   13204             :         Form_pg_constraint childcon;
   13205             : 
   13206             :         /* find_inheritance_children already got lock */
   13207         378 :         childrel = table_open(childrelid, NoLock);
   13208         378 :         CheckAlterTableIsSafe(childrel);
   13209             : 
   13210             :         /*
   13211             :          * We search for not-null constraints by column name, and others by
   13212             :          * constraint name.
   13213             :          */
   13214         378 :         if (con->contype == CONSTRAINT_NOTNULL)
   13215             :         {
   13216         142 :             tuple = findNotNullConstraint(childrelid, colname);
   13217         142 :             if (!HeapTupleIsValid(tuple))
   13218           0 :                 elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation %u",
   13219             :                      colname, RelationGetRelid(childrel));
   13220             :         }
   13221             :         else
   13222             :         {
   13223             :             SysScanDesc scan;
   13224             :             ScanKeyData skey[3];
   13225             : 
   13226         236 :             ScanKeyInit(&skey[0],
   13227             :                         Anum_pg_constraint_conrelid,
   13228             :                         BTEqualStrategyNumber, F_OIDEQ,
   13229             :                         ObjectIdGetDatum(childrelid));
   13230         236 :             ScanKeyInit(&skey[1],
   13231             :                         Anum_pg_constraint_contypid,
   13232             :                         BTEqualStrategyNumber, F_OIDEQ,
   13233             :                         ObjectIdGetDatum(InvalidOid));
   13234         236 :             ScanKeyInit(&skey[2],
   13235             :                         Anum_pg_constraint_conname,
   13236             :                         BTEqualStrategyNumber, F_NAMEEQ,
   13237             :                         CStringGetDatum(constrName));
   13238         236 :             scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
   13239             :                                       true, NULL, 3, skey);
   13240             :             /* There can only be one, so no need to loop */
   13241         236 :             tuple = systable_getnext(scan);
   13242         236 :             if (!HeapTupleIsValid(tuple))
   13243           0 :                 ereport(ERROR,
   13244             :                         (errcode(ERRCODE_UNDEFINED_OBJECT),
   13245             :                          errmsg("constraint \"%s\" of relation \"%s\" does not exist",
   13246             :                                 constrName,
   13247             :                                 RelationGetRelationName(childrel))));
   13248         236 :             tuple = heap_copytuple(tuple);
   13249         236 :             systable_endscan(scan);
   13250             :         }
   13251             : 
   13252         378 :         childcon = (Form_pg_constraint) GETSTRUCT(tuple);
   13253             : 
   13254             :         /* Right now only CHECK and not-null constraints can be inherited */
   13255         378 :         if (childcon->contype != CONSTRAINT_CHECK &&
   13256         142 :             childcon->contype != CONSTRAINT_NOTNULL)
   13257           0 :             elog(ERROR, "inherited constraint is not a CHECK or not-null constraint");
   13258             : 
   13259         378 :         if (childcon->coninhcount <= 0) /* shouldn't happen */
   13260           0 :             elog(ERROR, "relation %u has non-inherited constraint \"%s\"",
   13261             :                  childrelid, NameStr(childcon->conname));
   13262             : 
   13263         378 :         if (recurse)
   13264             :         {
   13265             :             /*
   13266             :              * If the child constraint has other definition sources, just
   13267             :              * decrement its inheritance count; if not, recurse to delete it.
   13268             :              */
   13269         276 :             if (childcon->coninhcount == 1 && !childcon->conislocal)
   13270             :             {
   13271             :                 /* Time to delete this child constraint, too */
   13272         210 :                 dropconstraint_internal(childrel, tuple, behavior,
   13273             :                                         recurse, true, missing_ok,
   13274             :                                         lockmode);
   13275             :             }
   13276             :             else
   13277             :             {
   13278             :                 /* Child constraint must survive my deletion */
   13279          66 :                 childcon->coninhcount--;
   13280          66 :                 CatalogTupleUpdate(conrel, &tuple->t_self, tuple);
   13281             : 
   13282             :                 /* Make update visible */
   13283          66 :                 CommandCounterIncrement();
   13284             :             }
   13285             :         }
   13286             :         else
   13287             :         {
   13288             :             /*
   13289             :              * If we were told to drop ONLY in this table (no recursion) and
   13290             :              * there are no further parents for this constraint, we need to
   13291             :              * mark the inheritors' constraints as locally defined rather than
   13292             :              * inherited.
   13293             :              */
   13294         102 :             childcon->coninhcount--;
   13295         102 :             if (childcon->coninhcount == 0)
   13296         102 :                 childcon->conislocal = true;
   13297             : 
   13298         102 :             CatalogTupleUpdate(conrel, &tuple->t_self, tuple);
   13299             : 
   13300             :             /* Make update visible */
   13301         102 :             CommandCounterIncrement();
   13302             :         }
   13303             : 
   13304         372 :         heap_freetuple(tuple);
   13305             : 
   13306         372 :         table_close(childrel, NoLock);
   13307             :     }
   13308             : 
   13309         862 :     table_close(conrel, RowExclusiveLock);
   13310             : 
   13311         862 :     return conobj;
   13312             : }
   13313             : 
   13314             : /*
   13315             :  * ALTER COLUMN TYPE
   13316             :  *
   13317             :  * Unlike other subcommand types, we do parse transformation for ALTER COLUMN
   13318             :  * TYPE during phase 1 --- the AlterTableCmd passed in here is already
   13319             :  * transformed (and must be, because we rely on some transformed fields).
   13320             :  *
   13321             :  * The point of this is that the execution of all ALTER COLUMN TYPEs for a
   13322             :  * table will be done "in parallel" during phase 3, so all the USING
   13323             :  * expressions should be parsed assuming the original column types.  Also,
   13324             :  * this allows a USING expression to refer to a field that will be dropped.
   13325             :  *
   13326             :  * To make this work safely, AT_PASS_DROP then AT_PASS_ALTER_TYPE must be
   13327             :  * the first two execution steps in phase 2; they must not see the effects
   13328             :  * of any other subcommand types, since the USING expressions are parsed
   13329             :  * against the unmodified table's state.
   13330             :  */
   13331             : static void
   13332        1168 : ATPrepAlterColumnType(List **wqueue,
   13333             :                       AlteredTableInfo *tab, Relation rel,
   13334             :                       bool recurse, bool recursing,
   13335             :                       AlterTableCmd *cmd, LOCKMODE lockmode,
   13336             :                       AlterTableUtilityContext *context)
   13337             : {
   13338        1168 :     char       *colName = cmd->name;
   13339        1168 :     ColumnDef  *def = (ColumnDef *) cmd->def;
   13340        1168 :     TypeName   *typeName = def->typeName;
   13341        1168 :     Node       *transform = def->cooked_default;
   13342             :     HeapTuple   tuple;
   13343             :     Form_pg_attribute attTup;
   13344             :     AttrNumber  attnum;
   13345             :     Oid         targettype;
   13346             :     int32       targettypmod;
   13347             :     Oid         targetcollid;
   13348             :     NewColumnValue *newval;
   13349        1168 :     ParseState *pstate = make_parsestate(NULL);
   13350             :     AclResult   aclresult;
   13351             :     bool        is_expr;
   13352             : 
   13353        1168 :     if (rel->rd_rel->reloftype && !recursing)
   13354           6 :         ereport(ERROR,
   13355             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   13356             :                  errmsg("cannot alter column type of typed table")));
   13357             : 
   13358             :     /* lookup the attribute so we can check inheritance status */
   13359        1162 :     tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
   13360        1162 :     if (!HeapTupleIsValid(tuple))
   13361           0 :         ereport(ERROR,
   13362             :                 (errcode(ERRCODE_UNDEFINED_COLUMN),
   13363             :                  errmsg("column \"%s\" of relation \"%s\" does not exist",
   13364             :                         colName, RelationGetRelationName(rel))));
   13365        1162 :     attTup = (Form_pg_attribute) GETSTRUCT(tuple);
   13366        1162 :     attnum = attTup->attnum;
   13367             : 
   13368             :     /* Can't alter a system attribute */
   13369        1162 :     if (attnum <= 0)
   13370           6 :         ereport(ERROR,
   13371             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   13372             :                  errmsg("cannot alter system column \"%s\"",
   13373             :                         colName)));
   13374             : 
   13375             :     /*
   13376             :      * Cannot specify USING when altering type of a generated column, because
   13377             :      * that would violate the generation expression.
   13378             :      */
   13379        1156 :     if (attTup->attgenerated && def->cooked_default)
   13380           6 :         ereport(ERROR,
   13381             :                 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
   13382             :                  errmsg("cannot specify USING when altering type of generated column"),
   13383             :                  errdetail("Column \"%s\" is a generated column.", colName)));
   13384             : 
   13385             :     /*
   13386             :      * Don't alter inherited columns.  At outer level, there had better not be
   13387             :      * any inherited definition; when recursing, we assume this was checked at
   13388             :      * the parent level (see below).
   13389             :      */
   13390        1150 :     if (attTup->attinhcount > 0 && !recursing)
   13391           6 :         ereport(ERROR,
   13392             :                 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
   13393             :                  errmsg("cannot alter inherited column \"%s\"",
   13394             :                         colName)));
   13395             : 
   13396             :     /* Don't alter columns used in the partition key */
   13397        1144 :     if (has_partition_attrs(rel,
   13398             :                             bms_make_singleton(attnum - FirstLowInvalidHeapAttributeNumber),
   13399             :                             &is_expr))
   13400          18 :         ereport(ERROR,
   13401             :                 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
   13402             :                  errmsg("cannot alter column \"%s\" because it is part of the partition key of relation \"%s\"",
   13403             :                         colName, RelationGetRelationName(rel))));
   13404             : 
   13405             :     /* Look up the target type */
   13406        1126 :     typenameTypeIdAndMod(NULL, typeName, &targettype, &targettypmod);
   13407             : 
   13408        1120 :     aclresult = object_aclcheck(TypeRelationId, targettype, GetUserId(), ACL_USAGE);
   13409        1120 :     if (aclresult != ACLCHECK_OK)
   13410          12 :         aclcheck_error_type(aclresult, targettype);
   13411             : 
   13412             :     /* And the collation */
   13413        1108 :     targetcollid = GetColumnDefCollation(NULL, def, targettype);
   13414             : 
   13415             :     /* make sure datatype is legal for a column */
   13416        1102 :     CheckAttributeType(colName, targettype, targetcollid,
   13417        1102 :                        list_make1_oid(rel->rd_rel->reltype),
   13418             :                        0);
   13419             : 
   13420        1096 :     if (tab->relkind == RELKIND_RELATION ||
   13421         196 :         tab->relkind == RELKIND_PARTITIONED_TABLE)
   13422             :     {
   13423             :         /*
   13424             :          * Set up an expression to transform the old data value to the new
   13425             :          * type. If a USING option was given, use the expression as
   13426             :          * transformed by transformAlterTableStmt, else just take the old
   13427             :          * value and try to coerce it.  We do this first so that type
   13428             :          * incompatibility can be detected before we waste effort, and because
   13429             :          * we need the expression to be parsed against the original table row
   13430             :          * type.
   13431             :          */
   13432         960 :         if (!transform)
   13433             :         {
   13434         738 :             transform = (Node *) makeVar(1, attnum,
   13435             :                                          attTup->atttypid, attTup->atttypmod,
   13436             :                                          attTup->attcollation,
   13437             :                                          0);
   13438             :         }
   13439             : 
   13440         960 :         transform = coerce_to_target_type(pstate,
   13441             :                                           transform, exprType(transform),
   13442             :                                           targettype, targettypmod,
   13443             :                                           COERCION_ASSIGNMENT,
   13444             :                                           COERCE_IMPLICIT_CAST,
   13445             :                                           -1);
   13446         960 :         if (transform == NULL)
   13447             :         {
   13448             :             /* error text depends on whether USING was specified or not */
   13449          24 :             if (def->cooked_default != NULL)
   13450           6 :                 ereport(ERROR,
   13451             :                         (errcode(ERRCODE_DATATYPE_MISMATCH),
   13452             :                          errmsg("result of USING clause for column \"%s\""
   13453             :                                 " cannot be cast automatically to type %s",
   13454             :                                 colName, format_type_be(targettype)),
   13455             :                          errhint("You might need to add an explicit cast.")));
   13456             :             else
   13457          18 :                 ereport(ERROR,
   13458             :                         (errcode(ERRCODE_DATATYPE_MISMATCH),
   13459             :                          errmsg("column \"%s\" cannot be cast automatically to type %s",
   13460             :                                 colName, format_type_be(targettype)),
   13461             :                          !attTup->attgenerated ?
   13462             :                 /* translator: USING is SQL, don't translate it */
   13463             :                          errhint("You might need to specify \"USING %s::%s\".",
   13464             :                                  quote_identifier(colName),
   13465             :                                  format_type_with_typemod(targettype,
   13466             :                                                           targettypmod)) : 0));
   13467             :         }
   13468             : 
   13469             :         /* Fix collations after all else */
   13470         936 :         assign_expr_collations(pstate, transform);
   13471             : 
   13472             :         /* Plan the expr now so we can accurately assess the need to rewrite. */
   13473         936 :         transform = (Node *) expression_planner((Expr *) transform);
   13474             : 
   13475             :         /*
   13476             :          * Add a work queue item to make ATRewriteTable update the column
   13477             :          * contents.
   13478             :          */
   13479         936 :         newval = (NewColumnValue *) palloc0(sizeof(NewColumnValue));
   13480         936 :         newval->attnum = attnum;
   13481         936 :         newval->expr = (Expr *) transform;
   13482         936 :         newval->is_generated = false;
   13483             : 
   13484         936 :         tab->newvals = lappend(tab->newvals, newval);
   13485         936 :         if (ATColumnChangeRequiresRewrite(transform, attnum))
   13486         752 :             tab->rewrite |= AT_REWRITE_COLUMN_REWRITE;
   13487             :     }
   13488         136 :     else if (transform)
   13489          12 :         ereport(ERROR,
   13490             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   13491             :                  errmsg("\"%s\" is not a table",
   13492             :                         RelationGetRelationName(rel))));
   13493             : 
   13494        1060 :     if (!RELKIND_HAS_STORAGE(tab->relkind))
   13495             :     {
   13496             :         /*
   13497             :          * For relations without storage, do this check now.  Regular tables
   13498             :          * will check it later when the table is being rewritten.
   13499             :          */
   13500         184 :         find_composite_type_dependencies(rel->rd_rel->reltype, rel, NULL);
   13501             :     }
   13502             : 
   13503        1030 :     ReleaseSysCache(tuple);
   13504             : 
   13505             :     /*
   13506             :      * Recurse manually by queueing a new command for each child, if
   13507             :      * necessary. We cannot apply ATSimpleRecursion here because we need to
   13508             :      * remap attribute numbers in the USING expression, if any.
   13509             :      *
   13510             :      * If we are told not to recurse, there had better not be any child
   13511             :      * tables; else the alter would put them out of step.
   13512             :      */
   13513        1030 :     if (recurse)
   13514             :     {
   13515         772 :         Oid         relid = RelationGetRelid(rel);
   13516             :         List       *child_oids,
   13517             :                    *child_numparents;
   13518             :         ListCell   *lo,
   13519             :                    *li;
   13520             : 
   13521         772 :         child_oids = find_all_inheritors(relid, lockmode,
   13522             :                                          &child_numparents);
   13523             : 
   13524             :         /*
   13525             :          * find_all_inheritors does the recursive search of the inheritance
   13526             :          * hierarchy, so all we have to do is process all of the relids in the
   13527             :          * list that it returns.
   13528             :          */
   13529        1752 :         forboth(lo, child_oids, li, child_numparents)
   13530             :         {
   13531        1004 :             Oid         childrelid = lfirst_oid(lo);
   13532        1004 :             int         numparents = lfirst_int(li);
   13533             :             Relation    childrel;
   13534             :             HeapTuple   childtuple;
   13535             :             Form_pg_attribute childattTup;
   13536             : 
   13537        1004 :             if (childrelid == relid)
   13538         772 :                 continue;
   13539             : 
   13540             :             /* find_all_inheritors already got lock */
   13541         232 :             childrel = relation_open(childrelid, NoLock);
   13542         232 :             CheckAlterTableIsSafe(childrel);
   13543             : 
   13544             :             /*
   13545             :              * Verify that the child doesn't have any inherited definitions of
   13546             :              * this column that came from outside this inheritance hierarchy.
   13547             :              * (renameatt makes a similar test, though in a different way
   13548             :              * because of its different recursion mechanism.)
   13549             :              */
   13550         232 :             childtuple = SearchSysCacheAttName(RelationGetRelid(childrel),
   13551             :                                                colName);
   13552         232 :             if (!HeapTupleIsValid(childtuple))
   13553           0 :                 ereport(ERROR,
   13554             :                         (errcode(ERRCODE_UNDEFINED_COLUMN),
   13555             :                          errmsg("column \"%s\" of relation \"%s\" does not exist",
   13556             :                                 colName, RelationGetRelationName(childrel))));
   13557         232 :             childattTup = (Form_pg_attribute) GETSTRUCT(childtuple);
   13558             : 
   13559         232 :             if (childattTup->attinhcount > numparents)
   13560           6 :                 ereport(ERROR,
   13561             :                         (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
   13562             :                          errmsg("cannot alter inherited column \"%s\" of relation \"%s\"",
   13563             :                                 colName, RelationGetRelationName(childrel))));
   13564             : 
   13565         226 :             ReleaseSysCache(childtuple);
   13566             : 
   13567             :             /*
   13568             :              * Remap the attribute numbers.  If no USING expression was
   13569             :              * specified, there is no need for this step.
   13570             :              */
   13571         226 :             if (def->cooked_default)
   13572             :             {
   13573             :                 AttrMap    *attmap;
   13574             :                 bool        found_whole_row;
   13575             : 
   13576             :                 /* create a copy to scribble on */
   13577          78 :                 cmd = copyObject(cmd);
   13578             : 
   13579          78 :                 attmap = build_attrmap_by_name(RelationGetDescr(childrel),
   13580             :                                                RelationGetDescr(rel),
   13581             :                                                false);
   13582         156 :                 ((ColumnDef *) cmd->def)->cooked_default =
   13583          78 :                     map_variable_attnos(def->cooked_default,
   13584             :                                         1, 0,
   13585             :                                         attmap,
   13586             :                                         InvalidOid, &found_whole_row);
   13587          78 :                 if (found_whole_row)
   13588           6 :                     ereport(ERROR,
   13589             :                             (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   13590             :                              errmsg("cannot convert whole-row table reference"),
   13591             :                              errdetail("USING expression contains a whole-row table reference.")));
   13592          72 :                 pfree(attmap);
   13593             :             }
   13594         220 :             ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode, context);
   13595         208 :             relation_close(childrel, NoLock);
   13596             :         }
   13597             :     }
   13598         308 :     else if (!recursing &&
   13599          50 :              find_inheritance_children(RelationGetRelid(rel), NoLock) != NIL)
   13600           0 :         ereport(ERROR,
   13601             :                 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
   13602             :                  errmsg("type of inherited column \"%s\" must be changed in child tables too",
   13603             :                         colName)));
   13604             : 
   13605        1006 :     if (tab->relkind == RELKIND_COMPOSITE_TYPE)
   13606          50 :         ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
   13607        1000 : }
   13608             : 
   13609             : /*
   13610             :  * When the data type of a column is changed, a rewrite might not be required
   13611             :  * if the new type is sufficiently identical to the old one, and the USING
   13612             :  * clause isn't trying to insert some other value.  It's safe to skip the
   13613             :  * rewrite in these cases:
   13614             :  *
   13615             :  * - the old type is binary coercible to the new type
   13616             :  * - the new type is an unconstrained domain over the old type
   13617             :  * - {NEW,OLD} or {OLD,NEW} is {timestamptz,timestamp} and the timezone is UTC
   13618             :  *
   13619             :  * In the case of a constrained domain, we could get by with scanning the
   13620             :  * table and checking the constraint rather than actually rewriting it, but we
   13621             :  * don't currently try to do that.
   13622             :  */
   13623             : static bool
   13624        1042 : ATColumnChangeRequiresRewrite(Node *expr, AttrNumber varattno)
   13625             : {
   13626             :     Assert(expr != NULL);
   13627             : 
   13628             :     for (;;)
   13629             :     {
   13630             :         /* only one varno, so no need to check that */
   13631        1042 :         if (IsA(expr, Var) && ((Var *) expr)->varattno == varattno)
   13632         184 :             return false;
   13633         858 :         else if (IsA(expr, RelabelType))
   13634          94 :             expr = (Node *) ((RelabelType *) expr)->arg;
   13635         764 :         else if (IsA(expr, CoerceToDomain))
   13636             :         {
   13637           0 :             CoerceToDomain *d = (CoerceToDomain *) expr;
   13638             : 
   13639           0 :             if (DomainHasConstraints(d->resulttype))
   13640           0 :                 return true;
   13641           0 :             expr = (Node *) d->arg;
   13642             :         }
   13643         764 :         else if (IsA(expr, FuncExpr))
   13644             :         {
   13645         564 :             FuncExpr   *f = (FuncExpr *) expr;
   13646             : 
   13647         564 :             switch (f->funcid)
   13648             :             {
   13649          18 :                 case F_TIMESTAMPTZ_TIMESTAMP:
   13650             :                 case F_TIMESTAMP_TIMESTAMPTZ:
   13651          18 :                     if (TimestampTimestampTzRequiresRewrite())
   13652           6 :                         return true;
   13653             :                     else
   13654          12 :                         expr = linitial(f->args);
   13655          12 :                     break;
   13656         546 :                 default:
   13657         546 :                     return true;
   13658             :             }
   13659             :         }
   13660             :         else
   13661         200 :             return true;
   13662             :     }
   13663             : }
   13664             : 
   13665             : /*
   13666             :  * ALTER COLUMN .. SET DATA TYPE
   13667             :  *
   13668             :  * Return the address of the modified column.
   13669             :  */
   13670             : static ObjectAddress
   13671         970 : ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
   13672             :                       AlterTableCmd *cmd, LOCKMODE lockmode)
   13673             : {
   13674         970 :     char       *colName = cmd->name;
   13675         970 :     ColumnDef  *def = (ColumnDef *) cmd->def;
   13676         970 :     TypeName   *typeName = def->typeName;
   13677             :     HeapTuple   heapTup;
   13678             :     Form_pg_attribute attTup,
   13679             :                 attOldTup;
   13680             :     AttrNumber  attnum;
   13681             :     HeapTuple   typeTuple;
   13682             :     Form_pg_type tform;
   13683             :     Oid         targettype;
   13684             :     int32       targettypmod;
   13685             :     Oid         targetcollid;
   13686             :     Node       *defaultexpr;
   13687             :     Relation    attrelation;
   13688             :     Relation    depRel;
   13689             :     ScanKeyData key[3];
   13690             :     SysScanDesc scan;
   13691             :     HeapTuple   depTup;
   13692             :     ObjectAddress address;
   13693             : 
   13694             :     /*
   13695             :      * Clear all the missing values if we're rewriting the table, since this
   13696             :      * renders them pointless.
   13697             :      */
   13698         970 :     if (tab->rewrite)
   13699             :     {
   13700             :         Relation    newrel;
   13701             : 
   13702         698 :         newrel = table_open(RelationGetRelid(rel), NoLock);
   13703         698 :         RelationClearMissing(newrel);
   13704         698 :         relation_close(newrel, NoLock);
   13705             :         /* make sure we don't conflict with later attribute modifications */
   13706         698 :         CommandCounterIncrement();
   13707             :     }
   13708             : 
   13709         970 :     attrelation = table_open(AttributeRelationId, RowExclusiveLock);
   13710             : 
   13711             :     /* Look up the target column */
   13712         970 :     heapTup = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
   13713         970 :     if (!HeapTupleIsValid(heapTup)) /* shouldn't happen */
   13714           0 :         ereport(ERROR,
   13715             :                 (errcode(ERRCODE_UNDEFINED_COLUMN),
   13716             :                  errmsg("column \"%s\" of relation \"%s\" does not exist",
   13717             :                         colName, RelationGetRelationName(rel))));
   13718         970 :     attTup = (Form_pg_attribute) GETSTRUCT(heapTup);
   13719         970 :     attnum = attTup->attnum;
   13720         970 :     attOldTup = TupleDescAttr(tab->oldDesc, attnum - 1);
   13721             : 
   13722             :     /* Check for multiple ALTER TYPE on same column --- can't cope */
   13723         970 :     if (attTup->atttypid != attOldTup->atttypid ||
   13724         970 :         attTup->atttypmod != attOldTup->atttypmod)
   13725           0 :         ereport(ERROR,
   13726             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   13727             :                  errmsg("cannot alter type of column \"%s\" twice",
   13728             :                         colName)));
   13729             : 
   13730             :     /* Look up the target type (should not fail, since prep found it) */
   13731         970 :     typeTuple = typenameType(NULL, typeName, &targettypmod);
   13732         970 :     tform = (Form_pg_type) GETSTRUCT(typeTuple);
   13733         970 :     targettype = tform->oid;
   13734             :     /* And the collation */
   13735         970 :     targetcollid = GetColumnDefCollation(NULL, def, targettype);
   13736             : 
   13737             :     /*
   13738             :      * If there is a default expression for the column, get it and ensure we
   13739             :      * can coerce it to the new datatype.  (We must do this before changing
   13740             :      * the column type, because build_column_default itself will try to
   13741             :      * coerce, and will not issue the error message we want if it fails.)
   13742             :      *
   13743             :      * We remove any implicit coercion steps at the top level of the old
   13744             :      * default expression; this has been agreed to satisfy the principle of
   13745             :      * least surprise.  (The conversion to the new column type should act like
   13746             :      * it started from what the user sees as the stored expression, and the
   13747             :      * implicit coercions aren't going to be shown.)
   13748             :      */
   13749         970 :     if (attTup->atthasdef)
   13750             :     {
   13751          56 :         defaultexpr = build_column_default(rel, attnum);
   13752             :         Assert(defaultexpr);
   13753          56 :         defaultexpr = strip_implicit_coercions(defaultexpr);
   13754          56 :         defaultexpr = coerce_to_target_type(NULL,   /* no UNKNOWN params */
   13755             :                                             defaultexpr, exprType(defaultexpr),
   13756             :                                             targettype, targettypmod,
   13757             :                                             COERCION_ASSIGNMENT,
   13758             :                                             COERCE_IMPLICIT_CAST,
   13759             :                                             -1);
   13760          56 :         if (defaultexpr == NULL)
   13761             :         {
   13762           6 :             if (attTup->attgenerated)
   13763           0 :                 ereport(ERROR,
   13764             :                         (errcode(ERRCODE_DATATYPE_MISMATCH),
   13765             :                          errmsg("generation expression for column \"%s\" cannot be cast automatically to type %s",
   13766             :                                 colName, format_type_be(targettype))));
   13767             :             else
   13768           6 :                 ereport(ERROR,
   13769             :                         (errcode(ERRCODE_DATATYPE_MISMATCH),
   13770             :                          errmsg("default for column \"%s\" cannot be cast automatically to type %s",
   13771             :                                 colName, format_type_be(targettype))));
   13772             :         }
   13773             :     }
   13774             :     else
   13775         914 :         defaultexpr = NULL;
   13776             : 
   13777             :     /*
   13778             :      * Find everything that depends on the column (constraints, indexes, etc),
   13779             :      * and record enough information to let us recreate the objects.
   13780             :      *
   13781             :      * The actual recreation does not happen here, but only after we have
   13782             :      * performed all the individual ALTER TYPE operations.  We have to save
   13783             :      * the info before executing ALTER TYPE, though, else the deparser will
   13784             :      * get confused.
   13785             :      */
   13786         964 :     RememberAllDependentForRebuilding(tab, AT_AlterColumnType, rel, attnum, colName);
   13787             : 
   13788             :     /*
   13789             :      * Now scan for dependencies of this column on other things.  The only
   13790             :      * things we should find are the dependency on the column datatype and
   13791             :      * possibly a collation dependency.  Those can be removed.
   13792             :      */
   13793         940 :     depRel = table_open(DependRelationId, RowExclusiveLock);
   13794             : 
   13795         940 :     ScanKeyInit(&key[0],
   13796             :                 Anum_pg_depend_classid,
   13797             :                 BTEqualStrategyNumber, F_OIDEQ,
   13798             :                 ObjectIdGetDatum(RelationRelationId));
   13799         940 :     ScanKeyInit(&key[1],
   13800             :                 Anum_pg_depend_objid,
   13801             :                 BTEqualStrategyNumber, F_OIDEQ,
   13802             :                 ObjectIdGetDatum(RelationGetRelid(rel)));
   13803         940 :     ScanKeyInit(&key[2],
   13804             :                 Anum_pg_depend_objsubid,
   13805             :                 BTEqualStrategyNumber, F_INT4EQ,
   13806             :                 Int32GetDatum((int32) attnum));
   13807             : 
   13808         940 :     scan = systable_beginscan(depRel, DependDependerIndexId, true,
   13809             :                               NULL, 3, key);
   13810             : 
   13811         944 :     while (HeapTupleIsValid(depTup = systable_getnext(scan)))
   13812             :     {
   13813           4 :         Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(depTup);
   13814             :         ObjectAddress foundObject;
   13815             : 
   13816           4 :         foundObject.classId = foundDep->refclassid;
   13817           4 :         foundObject.objectId = foundDep->refobjid;
   13818           4 :         foundObject.objectSubId = foundDep->refobjsubid;
   13819             : 
   13820           4 :         if (foundDep->deptype != DEPENDENCY_NORMAL)
   13821           0 :             elog(ERROR, "found unexpected dependency type '%c'",
   13822             :                  foundDep->deptype);
   13823           4 :         if (!(foundDep->refclassid == TypeRelationId &&
   13824           4 :               foundDep->refobjid == attTup->atttypid) &&
   13825           0 :             !(foundDep->refclassid == CollationRelationId &&
   13826           0 :               foundDep->refobjid == attTup->attcollation))
   13827           0 :             elog(ERROR, "found unexpected dependency for column: %s",
   13828             :                  getObjectDescription(&foundObject, false));
   13829             : 
   13830           4 :         CatalogTupleDelete(depRel, &depTup->t_self);
   13831             :     }
   13832             : 
   13833         940 :     systable_endscan(scan);
   13834             : 
   13835         940 :     table_close(depRel, RowExclusiveLock);
   13836             : 
   13837             :     /*
   13838             :      * Here we go --- change the recorded column type and collation.  (Note
   13839             :      * heapTup is a copy of the syscache entry, so okay to scribble on.) First
   13840             :      * fix up the missing value if any.
   13841             :      */
   13842         940 :     if (attTup->atthasmissing)
   13843             :     {
   13844             :         Datum       missingval;
   13845             :         bool        missingNull;
   13846             : 
   13847             :         /* if rewrite is true the missing value should already be cleared */
   13848             :         Assert(tab->rewrite == 0);
   13849             : 
   13850             :         /* Get the missing value datum */
   13851           6 :         missingval = heap_getattr(heapTup,
   13852             :                                   Anum_pg_attribute_attmissingval,
   13853             :                                   attrelation->rd_att,
   13854             :                                   &missingNull);
   13855             : 
   13856             :         /* if it's a null array there is nothing to do */
   13857             : 
   13858           6 :         if (!missingNull)
   13859             :         {
   13860             :             /*
   13861             :              * Get the datum out of the array and repack it in a new array
   13862             :              * built with the new type data. We assume that since the table
   13863             :              * doesn't need rewriting, the actual Datum doesn't need to be
   13864             :              * changed, only the array metadata.
   13865             :              */
   13866             : 
   13867           6 :             int         one = 1;
   13868             :             bool        isNull;
   13869           6 :             Datum       valuesAtt[Natts_pg_attribute] = {0};
   13870           6 :             bool        nullsAtt[Natts_pg_attribute] = {0};
   13871           6 :             bool        replacesAtt[Natts_pg_attribute] = {0};
   13872             :             HeapTuple   newTup;
   13873             : 
   13874          12 :             missingval = array_get_element(missingval,
   13875             :                                            1,
   13876             :                                            &one,
   13877             :                                            0,
   13878           6 :                                            attTup->attlen,
   13879           6 :                                            attTup->attbyval,
   13880           6 :                                            attTup->attalign,
   13881             :                                            &isNull);
   13882           6 :             missingval = PointerGetDatum(construct_array(&missingval,
   13883             :                                                          1,
   13884             :                                                          targettype,
   13885           6 :                                                          tform->typlen,
   13886           6 :                                                          tform->typbyval,
   13887           6 :                                                          tform->typalign));
   13888             : 
   13889           6 :             valuesAtt[Anum_pg_attribute_attmissingval - 1] = missingval;
   13890           6 :             replacesAtt[Anum_pg_attribute_attmissingval - 1] = true;
   13891           6 :             nullsAtt[Anum_pg_attribute_attmissingval - 1] = false;
   13892             : 
   13893           6 :             newTup = heap_modify_tuple(heapTup, RelationGetDescr(attrelation),
   13894             :                                        valuesAtt, nullsAtt, replacesAtt);
   13895           6 :             heap_freetuple(heapTup);
   13896           6 :             heapTup = newTup;
   13897           6 :             attTup = (Form_pg_attribute) GETSTRUCT(heapTup);
   13898             :         }
   13899             :     }
   13900             : 
   13901         940 :     attTup->atttypid = targettype;
   13902         940 :     attTup->atttypmod = targettypmod;
   13903         940 :     attTup->attcollation = targetcollid;
   13904         940 :     if (list_length(typeName->arrayBounds) > PG_INT16_MAX)
   13905           0 :         ereport(ERROR,
   13906             :                 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
   13907             :                 errmsg("too many array dimensions"));
   13908         940 :     attTup->attndims = list_length(typeName->arrayBounds);
   13909         940 :     attTup->attlen = tform->typlen;
   13910         940 :     attTup->attbyval = tform->typbyval;
   13911         940 :     attTup->attalign = tform->typalign;
   13912         940 :     attTup->attstorage = tform->typstorage;
   13913         940 :     attTup->attcompression = InvalidCompressionMethod;
   13914             : 
   13915         940 :     ReleaseSysCache(typeTuple);
   13916             : 
   13917         940 :     CatalogTupleUpdate(attrelation, &heapTup->t_self, heapTup);
   13918             : 
   13919         940 :     table_close(attrelation, RowExclusiveLock);
   13920             : 
   13921             :     /* Install dependencies on new datatype and collation */
   13922         940 :     add_column_datatype_dependency(RelationGetRelid(rel), attnum, targettype);
   13923         940 :     add_column_collation_dependency(RelationGetRelid(rel), attnum, targetcollid);
   13924             : 
   13925             :     /*
   13926             :      * Drop any pg_statistic entry for the column, since it's now wrong type
   13927             :      */
   13928         940 :     RemoveStatistics(RelationGetRelid(rel), attnum);
   13929             : 
   13930         940 :     InvokeObjectPostAlterHook(RelationRelationId,
   13931             :                               RelationGetRelid(rel), attnum);
   13932             : 
   13933             :     /*
   13934             :      * Update the default, if present, by brute force --- remove and re-add
   13935             :      * the default.  Probably unsafe to take shortcuts, since the new version
   13936             :      * may well have additional dependencies.  (It's okay to do this now,
   13937             :      * rather than after other ALTER TYPE commands, since the default won't
   13938             :      * depend on other column types.)
   13939             :      */
   13940         940 :     if (defaultexpr)
   13941             :     {
   13942             :         /*
   13943             :          * If it's a GENERATED default, drop its dependency records, in
   13944             :          * particular its INTERNAL dependency on the column, which would
   13945             :          * otherwise cause dependency.c to refuse to perform the deletion.
   13946             :          */
   13947          50 :         if (attTup->attgenerated)
   13948             :         {
   13949           6 :             Oid         attrdefoid = GetAttrDefaultOid(RelationGetRelid(rel), attnum);
   13950             : 
   13951           6 :             if (!OidIsValid(attrdefoid))
   13952           0 :                 elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
   13953             :                      RelationGetRelid(rel), attnum);
   13954           6 :             (void) deleteDependencyRecordsFor(AttrDefaultRelationId, attrdefoid, false);
   13955             :         }
   13956             : 
   13957             :         /*
   13958             :          * Make updates-so-far visible, particularly the new pg_attribute row
   13959             :          * which will be updated again.
   13960             :          */
   13961          50 :         CommandCounterIncrement();
   13962             : 
   13963             :         /*
   13964             :          * We use RESTRICT here for safety, but at present we do not expect
   13965             :          * anything to depend on the default.
   13966             :          */
   13967          50 :         RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT, true,
   13968             :                           true);
   13969             : 
   13970          50 :         StoreAttrDefault(rel, attnum, defaultexpr, true, false);
   13971             :     }
   13972             : 
   13973         940 :     ObjectAddressSubSet(address, RelationRelationId,
   13974             :                         RelationGetRelid(rel), attnum);
   13975             : 
   13976             :     /* Cleanup */
   13977         940 :     heap_freetuple(heapTup);
   13978             : 
   13979         940 :     return address;
   13980             : }
   13981             : 
   13982             : /*
   13983             :  * Subroutine for ATExecAlterColumnType and ATExecSetExpression: Find everything
   13984             :  * that depends on the column (constraints, indexes, etc), and record enough
   13985             :  * information to let us recreate the objects.
   13986             :  */
   13987             : static void
   13988        1042 : RememberAllDependentForRebuilding(AlteredTableInfo *tab, AlterTableType subtype,
   13989             :                                   Relation rel, AttrNumber attnum, const char *colName)
   13990             : {
   13991             :     Relation    depRel;
   13992             :     ScanKeyData key[3];
   13993             :     SysScanDesc scan;
   13994             :     HeapTuple   depTup;
   13995             : 
   13996             :     Assert(subtype == AT_AlterColumnType || subtype == AT_SetExpression);
   13997             : 
   13998        1042 :     depRel = table_open(DependRelationId, RowExclusiveLock);
   13999             : 
   14000        1042 :     ScanKeyInit(&key[0],
   14001             :                 Anum_pg_depend_refclassid,
   14002             :                 BTEqualStrategyNumber, F_OIDEQ,
   14003             :                 ObjectIdGetDatum(RelationRelationId));
   14004        1042 :     ScanKeyInit(&key[1],
   14005             :                 Anum_pg_depend_refobjid,
   14006             :                 BTEqualStrategyNumber, F_OIDEQ,
   14007             :                 ObjectIdGetDatum(RelationGetRelid(rel)));
   14008        1042 :     ScanKeyInit(&key[2],
   14009             :                 Anum_pg_depend_refobjsubid,
   14010             :                 BTEqualStrategyNumber, F_INT4EQ,
   14011             :                 Int32GetDatum((int32) attnum));
   14012             : 
   14013        1042 :     scan = systable_beginscan(depRel, DependReferenceIndexId, true,
   14014             :                               NULL, 3, key);
   14015             : 
   14016        2132 :     while (HeapTupleIsValid(depTup = systable_getnext(scan)))
   14017             :     {
   14018        1114 :         Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(depTup);
   14019             :         ObjectAddress foundObject;
   14020             : 
   14021        1114 :         foundObject.classId = foundDep->classid;
   14022        1114 :         foundObject.objectId = foundDep->objid;
   14023        1114 :         foundObject.objectSubId = foundDep->objsubid;
   14024             : 
   14025        1114 :         switch (foundObject.classId)
   14026             :         {
   14027         274 :             case RelationRelationId:
   14028             :                 {
   14029         274 :                     char        relKind = get_rel_relkind(foundObject.objectId);
   14030             : 
   14031         274 :                     if (relKind == RELKIND_INDEX ||
   14032             :                         relKind == RELKIND_PARTITIONED_INDEX)
   14033             :                     {
   14034             :                         Assert(foundObject.objectSubId == 0);
   14035         236 :                         RememberIndexForRebuilding(foundObject.objectId, tab);
   14036             :                     }
   14037          38 :                     else if (relKind == RELKIND_SEQUENCE)
   14038             :                     {
   14039             :                         /*
   14040             :                          * This must be a SERIAL column's sequence.  We need
   14041             :                          * not do anything to it.
   14042             :                          */
   14043             :                         Assert(foundObject.objectSubId == 0);
   14044             :                     }
   14045             :                     else
   14046             :                     {
   14047             :                         /* Not expecting any other direct dependencies... */
   14048           0 :                         elog(ERROR, "unexpected object depending on column: %s",
   14049             :                              getObjectDescription(&foundObject, false));
   14050             :                     }
   14051         274 :                     break;
   14052             :                 }
   14053             : 
   14054         674 :             case ConstraintRelationId:
   14055             :                 Assert(foundObject.objectSubId == 0);
   14056         674 :                 RememberConstraintForRebuilding(foundObject.objectId, tab);
   14057         674 :                 break;
   14058             : 
   14059           0 :             case ProcedureRelationId:
   14060             : 
   14061             :                 /*
   14062             :                  * A new-style SQL function can depend on a column, if that
   14063             :                  * column is referenced in the parsed function body.  Ideally
   14064             :                  * we'd automatically update the function by deparsing and
   14065             :                  * reparsing it, but that's risky and might well fail anyhow.
   14066             :                  * FIXME someday.
   14067             :                  *
   14068             :                  * This is only a problem for AT_AlterColumnType, not
   14069             :                  * AT_SetExpression.
   14070             :                  */
   14071           0 :                 if (subtype == AT_AlterColumnType)
   14072           0 :                     ereport(ERROR,
   14073             :                             (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   14074             :                              errmsg("cannot alter type of a column used by a function or procedure"),
   14075             :                              errdetail("%s depends on column \"%s\"",
   14076             :                                        getObjectDescription(&foundObject, false),
   14077             :                                        colName)));
   14078           0 :                 break;
   14079             : 
   14080          12 :             case RewriteRelationId:
   14081             : 
   14082             :                 /*
   14083             :                  * View/rule bodies have pretty much the same issues as
   14084             :                  * function bodies.  FIXME someday.
   14085             :                  */
   14086          12 :                 if (subtype == AT_AlterColumnType)
   14087          12 :                     ereport(ERROR,
   14088             :                             (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   14089             :                              errmsg("cannot alter type of a column used by a view or rule"),
   14090             :                              errdetail("%s depends on column \"%s\"",
   14091             :                                        getObjectDescription(&foundObject, false),
   14092             :                                        colName)));
   14093           0 :                 break;
   14094             : 
   14095           0 :             case TriggerRelationId:
   14096             : 
   14097             :                 /*
   14098             :                  * A trigger can depend on a column because the column is
   14099             :                  * specified as an update target, or because the column is
   14100             :                  * used in the trigger's WHEN condition.  The first case would
   14101             :                  * not require any extra work, but the second case would
   14102             :                  * require updating the WHEN expression, which has the same
   14103             :                  * issues as above.  Since we can't easily tell which case
   14104             :                  * applies, we punt for both.  FIXME someday.
   14105             :                  */
   14106           0 :                 if (subtype == AT_AlterColumnType)
   14107           0 :                     ereport(ERROR,
   14108             :                             (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   14109             :                              errmsg("cannot alter type of a column used in a trigger definition"),
   14110             :                              errdetail("%s depends on column \"%s\"",
   14111             :                                        getObjectDescription(&foundObject, false),
   14112             :                                        colName)));
   14113           0 :                 break;
   14114             : 
   14115           0 :             case PolicyRelationId:
   14116             : 
   14117             :                 /*
   14118             :                  * A policy can depend on a column because the column is
   14119             :                  * specified in the policy's USING or WITH CHECK qual
   14120             :                  * expressions.  It might be possible to rewrite and recheck
   14121             :                  * the policy expression, but punt for now.  It's certainly
   14122             :                  * easy enough to remove and recreate the policy; still, FIXME
   14123             :                  * someday.
   14124             :                  */
   14125           0 :                 if (subtype == AT_AlterColumnType)
   14126           0 :                     ereport(ERROR,
   14127             :                             (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   14128             :                              errmsg("cannot alter type of a column used in a policy definition"),
   14129             :                              errdetail("%s depends on column \"%s\"",
   14130             :                                        getObjectDescription(&foundObject, false),
   14131             :                                        colName)));
   14132           0 :                 break;
   14133             : 
   14134         140 :             case AttrDefaultRelationId:
   14135             :                 {
   14136         140 :                     ObjectAddress col = GetAttrDefaultColumnAddress(foundObject.objectId);
   14137             : 
   14138         140 :                     if (col.objectId == RelationGetRelid(rel) &&
   14139         140 :                         col.objectSubId == attnum)
   14140             :                     {
   14141             :                         /*
   14142             :                          * Ignore the column's own default expression.  The
   14143             :                          * caller deals with it.
   14144             :                          */
   14145             :                     }
   14146             :                     else
   14147             :                     {
   14148             :                         /*
   14149             :                          * This must be a reference from the expression of a
   14150             :                          * generated column elsewhere in the same table.
   14151             :                          * Changing the type/generated expression of a column
   14152             :                          * that is used by a generated column is not allowed
   14153             :                          * by SQL standard, so just punt for now.  It might be
   14154             :                          * doable with some thinking and effort.
   14155             :                          */
   14156          12 :                         if (subtype == AT_AlterColumnType)
   14157          12 :                             ereport(ERROR,
   14158             :                                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   14159             :                                      errmsg("cannot alter type of a column used by a generated column"),
   14160             :                                      errdetail("Column \"%s\" is used by generated column \"%s\".",
   14161             :                                                colName,
   14162             :                                                get_attname(col.objectId,
   14163             :                                                            col.objectSubId,
   14164             :                                                            false))));
   14165             :                     }
   14166         128 :                     break;
   14167             :                 }
   14168             : 
   14169          14 :             case StatisticExtRelationId:
   14170             : 
   14171             :                 /*
   14172             :                  * Give the extended-stats machinery a chance to fix anything
   14173             :                  * that this column type change would break.
   14174             :                  */
   14175          14 :                 RememberStatisticsForRebuilding(foundObject.objectId, tab);
   14176          14 :                 break;
   14177             : 
   14178           0 :             case PublicationRelRelationId:
   14179             : 
   14180             :                 /*
   14181             :                  * Column reference in a PUBLICATION ... FOR TABLE ... WHERE
   14182             :                  * clause.  Same issues as above.  FIXME someday.
   14183             :                  */
   14184           0 :                 if (subtype == AT_AlterColumnType)
   14185           0 :                     ereport(ERROR,
   14186             :                             (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   14187             :                              errmsg("cannot alter type of a column used by a publication WHERE clause"),
   14188             :                              errdetail("%s depends on column \"%s\"",
   14189             :                                        getObjectDescription(&foundObject, false),
   14190             :                                        colName)));
   14191           0 :                 break;
   14192             : 
   14193           0 :             default:
   14194             : 
   14195             :                 /*
   14196             :                  * We don't expect any other sorts of objects to depend on a
   14197             :                  * column.
   14198             :                  */
   14199           0 :                 elog(ERROR, "unexpected object depending on column: %s",
   14200             :                      getObjectDescription(&foundObject, false));
   14201             :                 break;
   14202             :         }
   14203             :     }
   14204             : 
   14205        1018 :     systable_endscan(scan);
   14206        1018 :     table_close(depRel, NoLock);
   14207        1018 : }
   14208             : 
   14209             : /*
   14210             :  * Subroutine for ATExecAlterColumnType: remember that a replica identity
   14211             :  * needs to be reset.
   14212             :  */
   14213             : static void
   14214         444 : RememberReplicaIdentityForRebuilding(Oid indoid, AlteredTableInfo *tab)
   14215             : {
   14216         444 :     if (!get_index_isreplident(indoid))
   14217         426 :         return;
   14218             : 
   14219          18 :     if (tab->replicaIdentityIndex)
   14220           0 :         elog(ERROR, "relation %u has multiple indexes marked as replica identity", tab->relid);
   14221             : 
   14222          18 :     tab->replicaIdentityIndex = get_rel_name(indoid);
   14223             : }
   14224             : 
   14225             : /*
   14226             :  * Subroutine for ATExecAlterColumnType: remember any clustered index.
   14227             :  */
   14228             : static void
   14229         444 : RememberClusterOnForRebuilding(Oid indoid, AlteredTableInfo *tab)
   14230             : {
   14231         444 :     if (!get_index_isclustered(indoid))
   14232         426 :         return;
   14233             : 
   14234          18 :     if (tab->clusterOnIndex)
   14235           0 :         elog(ERROR, "relation %u has multiple clustered indexes", tab->relid);
   14236             : 
   14237          18 :     tab->clusterOnIndex = get_rel_name(indoid);
   14238             : }
   14239             : 
   14240             : /*
   14241             :  * Subroutine for ATExecAlterColumnType: remember that a constraint needs
   14242             :  * to be rebuilt (which we might already know).
   14243             :  */
   14244             : static void
   14245         686 : RememberConstraintForRebuilding(Oid conoid, AlteredTableInfo *tab)
   14246             : {
   14247             :     /*
   14248             :      * This de-duplication check is critical for two independent reasons: we
   14249             :      * mustn't try to recreate the same constraint twice, and if a constraint
   14250             :      * depends on more than one column whose type is to be altered, we must
   14251             :      * capture its definition string before applying any of the column type
   14252             :      * changes.  ruleutils.c will get confused if we ask again later.
   14253             :      */
   14254         686 :     if (!list_member_oid(tab->changedConstraintOids, conoid))
   14255             :     {
   14256             :         /* OK, capture the constraint's existing definition string */
   14257         596 :         char       *defstring = pg_get_constraintdef_command(conoid);
   14258             :         Oid         indoid;
   14259             : 
   14260             :         /*
   14261             :          * It is critical to create not-null constraints ahead of primary key
   14262             :          * indexes; otherwise, the not-null constraint would be created by the
   14263             :          * primary key, and the constraint name would be wrong.
   14264             :          */
   14265         596 :         if (get_constraint_type(conoid) == CONSTRAINT_NOTNULL)
   14266             :         {
   14267         198 :             tab->changedConstraintOids = lcons_oid(conoid,
   14268             :                                                    tab->changedConstraintOids);
   14269         198 :             tab->changedConstraintDefs = lcons(defstring,
   14270             :                                                tab->changedConstraintDefs);
   14271             :         }
   14272             :         else
   14273             :         {
   14274             : 
   14275         398 :             tab->changedConstraintOids = lappend_oid(tab->changedConstraintOids,
   14276             :                                                      conoid);
   14277         398 :             tab->changedConstraintDefs = lappend(tab->changedConstraintDefs,
   14278             :                                                  defstring);
   14279             :         }
   14280             : 
   14281             :         /*
   14282             :          * For the index of a constraint, if any, remember if it is used for
   14283             :          * the table's replica identity or if it is a clustered index, so that
   14284             :          * ATPostAlterTypeCleanup() can queue up commands necessary to restore
   14285             :          * those properties.
   14286             :          */
   14287         596 :         indoid = get_constraint_index(conoid);
   14288         596 :         if (OidIsValid(indoid))
   14289             :         {
   14290         228 :             RememberReplicaIdentityForRebuilding(indoid, tab);
   14291         228 :             RememberClusterOnForRebuilding(indoid, tab);
   14292             :         }
   14293             :     }
   14294         686 : }
   14295             : 
   14296             : /*
   14297             :  * Subroutine for ATExecAlterColumnType: remember that an index needs
   14298             :  * to be rebuilt (which we might already know).
   14299             :  */
   14300             : static void
   14301         236 : RememberIndexForRebuilding(Oid indoid, AlteredTableInfo *tab)
   14302             : {
   14303             :     /*
   14304             :      * This de-duplication check is critical for two independent reasons: we
   14305             :      * mustn't try to recreate the same index twice, and if an index depends
   14306             :      * on more than one column whose type is to be altered, we must capture
   14307             :      * its definition string before applying any of the column type changes.
   14308             :      * ruleutils.c will get confused if we ask again later.
   14309             :      */
   14310         236 :     if (!list_member_oid(tab->changedIndexOids, indoid))
   14311             :     {
   14312             :         /*
   14313             :          * Before adding it as an index-to-rebuild, we'd better see if it
   14314             :          * belongs to a constraint, and if so rebuild the constraint instead.
   14315             :          * Typically this check fails, because constraint indexes normally
   14316             :          * have only dependencies on their constraint.  But it's possible for
   14317             :          * such an index to also have direct dependencies on table columns,
   14318             :          * for example with a partial exclusion constraint.
   14319             :          */
   14320         228 :         Oid         conoid = get_index_constraint(indoid);
   14321             : 
   14322         228 :         if (OidIsValid(conoid))
   14323             :         {
   14324          12 :             RememberConstraintForRebuilding(conoid, tab);
   14325             :         }
   14326             :         else
   14327             :         {
   14328             :             /* OK, capture the index's existing definition string */
   14329         216 :             char       *defstring = pg_get_indexdef_string(indoid);
   14330             : 
   14331         216 :             tab->changedIndexOids = lappend_oid(tab->changedIndexOids,
   14332             :                                                 indoid);
   14333         216 :             tab->changedIndexDefs = lappend(tab->changedIndexDefs,
   14334             :                                             defstring);
   14335             : 
   14336             :             /*
   14337             :              * Remember if this index is used for the table's replica identity
   14338             :              * or if it is a clustered index, so that ATPostAlterTypeCleanup()
   14339             :              * can queue up commands necessary to restore those properties.
   14340             :              */
   14341         216 :             RememberReplicaIdentityForRebuilding(indoid, tab);
   14342         216 :             RememberClusterOnForRebuilding(indoid, tab);
   14343             :         }
   14344             :     }
   14345         236 : }
   14346             : 
   14347             : /*
   14348             :  * Subroutine for ATExecAlterColumnType: remember that a statistics object
   14349             :  * needs to be rebuilt (which we might already know).
   14350             :  */
   14351             : static void
   14352          14 : RememberStatisticsForRebuilding(Oid stxoid, AlteredTableInfo *tab)
   14353             : {
   14354             :     /*
   14355             :      * This de-duplication check is critical for two independent reasons: we
   14356             :      * mustn't try to recreate the same statistics object twice, and if the
   14357             :      * statistics object depends on more than one column whose type is to be
   14358             :      * altered, we must capture its definition string before applying any of
   14359             :      * the type changes. ruleutils.c will get confused if we ask again later.
   14360             :      */
   14361          14 :     if (!list_member_oid(tab->changedStatisticsOids, stxoid))
   14362             :     {
   14363             :         /* OK, capture the statistics object's existing definition string */
   14364          14 :         char       *defstring = pg_get_statisticsobjdef_string(stxoid);
   14365             : 
   14366          14 :         tab->changedStatisticsOids = lappend_oid(tab->changedStatisticsOids,
   14367             :                                                  stxoid);
   14368          14 :         tab->changedStatisticsDefs = lappend(tab->changedStatisticsDefs,
   14369             :                                              defstring);
   14370             :     }
   14371          14 : }
   14372             : 
   14373             : /*
   14374             :  * Cleanup after we've finished all the ALTER TYPE or SET EXPRESSION
   14375             :  * operations for a particular relation.  We have to drop and recreate all the
   14376             :  * indexes and constraints that depend on the altered columns.  We do the
   14377             :  * actual dropping here, but re-creation is managed by adding work queue
   14378             :  * entries to do those steps later.
   14379             :  */
   14380             : static void
   14381         988 : ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab, LOCKMODE lockmode)
   14382             : {
   14383             :     ObjectAddress obj;
   14384             :     ObjectAddresses *objects;
   14385             :     ListCell   *def_item;
   14386             :     ListCell   *oid_item;
   14387             : 
   14388             :     /*
   14389             :      * Collect all the constraints and indexes to drop so we can process them
   14390             :      * in a single call.  That way we don't have to worry about dependencies
   14391             :      * among them.
   14392             :      */
   14393         988 :     objects = new_object_addresses();
   14394             : 
   14395             :     /*
   14396             :      * Re-parse the index and constraint definitions, and attach them to the
   14397             :      * appropriate work queue entries.  We do this before dropping because in
   14398             :      * the case of a FOREIGN KEY constraint, we might not yet have exclusive
   14399             :      * lock on the table the constraint is attached to, and we need to get
   14400             :      * that before reparsing/dropping.
   14401             :      *
   14402             :      * We can't rely on the output of deparsing to tell us which relation to
   14403             :      * operate on, because concurrent activity might have made the name
   14404             :      * resolve differently.  Instead, we've got to use the OID of the
   14405             :      * constraint or index we're processing to figure out which relation to
   14406             :      * operate on.
   14407             :      */
   14408        1584 :     forboth(oid_item, tab->changedConstraintOids,
   14409             :             def_item, tab->changedConstraintDefs)
   14410             :     {
   14411         596 :         Oid         oldId = lfirst_oid(oid_item);
   14412             :         HeapTuple   tup;
   14413             :         Form_pg_constraint con;
   14414             :         Oid         relid;
   14415             :         Oid         confrelid;
   14416             :         char        contype;
   14417             :         bool        conislocal;
   14418             : 
   14419         596 :         tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(oldId));
   14420         596 :         if (!HeapTupleIsValid(tup)) /* should not happen */
   14421           0 :             elog(ERROR, "cache lookup failed for constraint %u", oldId);
   14422         596 :         con = (Form_pg_constraint) GETSTRUCT(tup);
   14423         596 :         if (OidIsValid(con->conrelid))
   14424         582 :             relid = con->conrelid;
   14425             :         else
   14426             :         {
   14427             :             /* must be a domain constraint */
   14428          14 :             relid = get_typ_typrelid(getBaseType(con->contypid));
   14429          14 :             if (!OidIsValid(relid))
   14430           0 :                 elog(ERROR, "could not identify relation associated with constraint %u", oldId);
   14431             :         }
   14432         596 :         confrelid = con->confrelid;
   14433         596 :         contype = con->contype;
   14434         596 :         conislocal = con->conislocal;
   14435         596 :         ReleaseSysCache(tup);
   14436             : 
   14437         596 :         ObjectAddressSet(obj, ConstraintRelationId, oldId);
   14438         596 :         add_exact_object_address(&obj, objects);
   14439             : 
   14440             :         /*
   14441             :          * If the constraint is inherited (only), we don't want to inject a
   14442             :          * new definition here; it'll get recreated when
   14443             :          * ATAddCheckNNConstraint recurses from adding the parent table's
   14444             :          * constraint.  But we had to carry the info this far so that we can
   14445             :          * drop the constraint below.
   14446             :          */
   14447         596 :         if (!conislocal)
   14448          28 :             continue;
   14449             : 
   14450             :         /*
   14451             :          * When rebuilding an FK constraint that references the table we're
   14452             :          * modifying, we might not yet have any lock on the FK's table, so get
   14453             :          * one now.  We'll need AccessExclusiveLock for the DROP CONSTRAINT
   14454             :          * step, so there's no value in asking for anything weaker.
   14455             :          */
   14456         568 :         if (relid != tab->relid && contype == CONSTRAINT_FOREIGN)
   14457          36 :             LockRelationOid(relid, AccessExclusiveLock);
   14458             : 
   14459         568 :         ATPostAlterTypeParse(oldId, relid, confrelid,
   14460         568 :                              (char *) lfirst(def_item),
   14461         568 :                              wqueue, lockmode, tab->rewrite);
   14462             :     }
   14463        1204 :     forboth(oid_item, tab->changedIndexOids,
   14464             :             def_item, tab->changedIndexDefs)
   14465             :     {
   14466         216 :         Oid         oldId = lfirst_oid(oid_item);
   14467             :         Oid         relid;
   14468             : 
   14469         216 :         relid = IndexGetRelation(oldId, false);
   14470         216 :         ATPostAlterTypeParse(oldId, relid, InvalidOid,
   14471         216 :                              (char *) lfirst(def_item),
   14472         216 :                              wqueue, lockmode, tab->rewrite);
   14473             : 
   14474         216 :         ObjectAddressSet(obj, RelationRelationId, oldId);
   14475         216 :         add_exact_object_address(&obj, objects);
   14476             :     }
   14477             : 
   14478             :     /* add dependencies for new statistics */
   14479        1002 :     forboth(oid_item, tab->changedStatisticsOids,
   14480             :             def_item, tab->changedStatisticsDefs)
   14481             :     {
   14482          14 :         Oid         oldId = lfirst_oid(oid_item);
   14483             :         Oid         relid;
   14484             : 
   14485          14 :         relid = StatisticsGetRelation(oldId, false);
   14486          14 :         ATPostAlterTypeParse(oldId, relid, InvalidOid,
   14487          14 :                              (char *) lfirst(def_item),
   14488          14 :                              wqueue, lockmode, tab->rewrite);
   14489             : 
   14490          14 :         ObjectAddressSet(obj, StatisticExtRelationId, oldId);
   14491          14 :         add_exact_object_address(&obj, objects);
   14492             :     }
   14493             : 
   14494             :     /*
   14495             :      * Queue up command to restore replica identity index marking
   14496             :      */
   14497         988 :     if (tab->replicaIdentityIndex)
   14498             :     {
   14499          18 :         AlterTableCmd *cmd = makeNode(AlterTableCmd);
   14500          18 :         ReplicaIdentityStmt *subcmd = makeNode(ReplicaIdentityStmt);
   14501             : 
   14502          18 :         subcmd->identity_type = REPLICA_IDENTITY_INDEX;
   14503          18 :         subcmd->name = tab->replicaIdentityIndex;
   14504          18 :         cmd->subtype = AT_ReplicaIdentity;
   14505          18 :         cmd->def = (Node *) subcmd;
   14506             : 
   14507             :         /* do it after indexes and constraints */
   14508          18 :         tab->subcmds[AT_PASS_OLD_CONSTR] =
   14509          18 :             lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
   14510             :     }
   14511             : 
   14512             :     /*
   14513             :      * Queue up command to restore marking of index used for cluster.
   14514             :      */
   14515         988 :     if (tab->clusterOnIndex)
   14516             :     {
   14517          18 :         AlterTableCmd *cmd = makeNode(AlterTableCmd);
   14518             : 
   14519          18 :         cmd->subtype = AT_ClusterOn;
   14520          18 :         cmd->name = tab->clusterOnIndex;
   14521             : 
   14522             :         /* do it after indexes and constraints */
   14523          18 :         tab->subcmds[AT_PASS_OLD_CONSTR] =
   14524          18 :             lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
   14525             :     }
   14526             : 
   14527             :     /*
   14528             :      * It should be okay to use DROP_RESTRICT here, since nothing else should
   14529             :      * be depending on these objects.
   14530             :      */
   14531         988 :     performMultipleDeletions(objects, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
   14532             : 
   14533         988 :     free_object_addresses(objects);
   14534             : 
   14535             :     /*
   14536             :      * The objects will get recreated during subsequent passes over the work
   14537             :      * queue.
   14538             :      */
   14539         988 : }
   14540             : 
   14541             : /*
   14542             :  * Parse the previously-saved definition string for a constraint, index or
   14543             :  * statistics object against the newly-established column data type(s), and
   14544             :  * queue up the resulting command parsetrees for execution.
   14545             :  *
   14546             :  * This might fail if, for example, you have a WHERE clause that uses an
   14547             :  * operator that's not available for the new column type.
   14548             :  */
   14549             : static void
   14550         798 : ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
   14551             :                      List **wqueue, LOCKMODE lockmode, bool rewrite)
   14552             : {
   14553             :     List       *raw_parsetree_list;
   14554             :     List       *querytree_list;
   14555             :     ListCell   *list_item;
   14556             :     Relation    rel;
   14557             : 
   14558             :     /*
   14559             :      * We expect that we will get only ALTER TABLE and CREATE INDEX
   14560             :      * statements. Hence, there is no need to pass them through
   14561             :      * parse_analyze_*() or the rewriter, but instead we need to pass them
   14562             :      * through parse_utilcmd.c to make them ready for execution.
   14563             :      */
   14564         798 :     raw_parsetree_list = raw_parser(cmd, RAW_PARSE_DEFAULT);
   14565         798 :     querytree_list = NIL;
   14566        1596 :     foreach(list_item, raw_parsetree_list)
   14567             :     {
   14568         798 :         RawStmt    *rs = lfirst_node(RawStmt, list_item);
   14569         798 :         Node       *stmt = rs->stmt;
   14570             : 
   14571         798 :         if (IsA(stmt, IndexStmt))
   14572         216 :             querytree_list = lappend(querytree_list,
   14573         216 :                                      transformIndexStmt(oldRelId,
   14574             :                                                         (IndexStmt *) stmt,
   14575             :                                                         cmd));
   14576         582 :         else if (IsA(stmt, AlterTableStmt))
   14577             :         {
   14578             :             List       *beforeStmts;
   14579             :             List       *afterStmts;
   14580             : 
   14581         554 :             stmt = (Node *) transformAlterTableStmt(oldRelId,
   14582             :                                                     (AlterTableStmt *) stmt,
   14583             :                                                     cmd,
   14584             :                                                     &beforeStmts,
   14585             :                                                     &afterStmts);
   14586         554 :             querytree_list = list_concat(querytree_list, beforeStmts);
   14587         554 :             querytree_list = lappend(querytree_list, stmt);
   14588         554 :             querytree_list = list_concat(querytree_list, afterStmts);
   14589             :         }
   14590          28 :         else if (IsA(stmt, CreateStatsStmt))
   14591          14 :             querytree_list = lappend(querytree_list,
   14592          14 :                                      transformStatsStmt(oldRelId,
   14593             :                                                         (CreateStatsStmt *) stmt,
   14594             :                                                         cmd));
   14595             :         else
   14596          14 :             querytree_list = lappend(querytree_list, stmt);
   14597             :     }
   14598             : 
   14599             :     /* Caller should already have acquired whatever lock we need. */
   14600         798 :     rel = relation_open(oldRelId, NoLock);
   14601             : 
   14602             :     /*
   14603             :      * Attach each generated command to the proper place in the work queue.
   14604             :      * Note this could result in creation of entirely new work-queue entries.
   14605             :      *
   14606             :      * Also note that we have to tweak the command subtypes, because it turns
   14607             :      * out that re-creation of indexes and constraints has to act a bit
   14608             :      * differently from initial creation.
   14609             :      */
   14610        1596 :     foreach(list_item, querytree_list)
   14611             :     {
   14612         798 :         Node       *stm = (Node *) lfirst(list_item);
   14613             :         AlteredTableInfo *tab;
   14614             : 
   14615         798 :         tab = ATGetQueueEntry(wqueue, rel);
   14616             : 
   14617         798 :         if (IsA(stm, IndexStmt))
   14618             :         {
   14619         216 :             IndexStmt  *stmt = (IndexStmt *) stm;
   14620             :             AlterTableCmd *newcmd;
   14621             : 
   14622         216 :             if (!rewrite)
   14623          56 :                 TryReuseIndex(oldId, stmt);
   14624         216 :             stmt->reset_default_tblspc = true;
   14625             :             /* keep the index's comment */
   14626         216 :             stmt->idxcomment = GetComment(oldId, RelationRelationId, 0);
   14627             : 
   14628         216 :             newcmd = makeNode(AlterTableCmd);
   14629         216 :             newcmd->subtype = AT_ReAddIndex;
   14630         216 :             newcmd->def = (Node *) stmt;
   14631         216 :             tab->subcmds[AT_PASS_OLD_INDEX] =
   14632         216 :                 lappend(tab->subcmds[AT_PASS_OLD_INDEX], newcmd);
   14633             :         }
   14634         582 :         else if (IsA(stm, AlterTableStmt))
   14635             :         {
   14636         554 :             AlterTableStmt *stmt = (AlterTableStmt *) stm;
   14637             :             ListCell   *lcmd;
   14638             : 
   14639        1108 :             foreach(lcmd, stmt->cmds)
   14640             :             {
   14641         554 :                 AlterTableCmd *cmd = lfirst_node(AlterTableCmd, lcmd);
   14642             : 
   14643         554 :                 if (cmd->subtype == AT_AddIndex)
   14644             :                 {
   14645             :                     IndexStmt  *indstmt;
   14646             :                     Oid         indoid;
   14647             : 
   14648         228 :                     indstmt = castNode(IndexStmt, cmd->def);
   14649         228 :                     indoid = get_constraint_index(oldId);
   14650             : 
   14651         228 :                     if (!rewrite)
   14652          48 :                         TryReuseIndex(indoid, indstmt);
   14653             :                     /* keep any comment on the index */
   14654         228 :                     indstmt->idxcomment = GetComment(indoid,
   14655             :                                                      RelationRelationId, 0);
   14656         228 :                     indstmt->reset_default_tblspc = true;
   14657             : 
   14658         228 :                     cmd->subtype = AT_ReAddIndex;
   14659         228 :                     tab->subcmds[AT_PASS_OLD_INDEX] =
   14660         228 :                         lappend(tab->subcmds[AT_PASS_OLD_INDEX], cmd);
   14661             : 
   14662             :                     /* recreate any comment on the constraint */
   14663         228 :                     RebuildConstraintComment(tab,
   14664             :                                              AT_PASS_OLD_INDEX,
   14665             :                                              oldId,
   14666             :                                              rel,
   14667             :                                              NIL,
   14668         228 :                                              indstmt->idxname);
   14669             :                 }
   14670         326 :                 else if (cmd->subtype == AT_AddConstraint)
   14671             :                 {
   14672         326 :                     Constraint *con = castNode(Constraint, cmd->def);
   14673             : 
   14674         326 :                     con->old_pktable_oid = refRelId;
   14675             :                     /* rewriting neither side of a FK */
   14676         326 :                     if (con->contype == CONSTR_FOREIGN &&
   14677          72 :                         !rewrite && tab->rewrite == 0)
   14678           6 :                         TryReuseForeignKey(oldId, con);
   14679         326 :                     con->reset_default_tblspc = true;
   14680         326 :                     cmd->subtype = AT_ReAddConstraint;
   14681         326 :                     tab->subcmds[AT_PASS_OLD_CONSTR] =
   14682         326 :                         lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
   14683             : 
   14684             :                     /*
   14685             :                      * Recreate any comment on the constraint.  If we have
   14686             :                      * recreated a primary key, then transformTableConstraint
   14687             :                      * has added an unnamed not-null constraint here; skip
   14688             :                      * this in that case.
   14689             :                      */
   14690         326 :                     if (con->conname)
   14691         326 :                         RebuildConstraintComment(tab,
   14692             :                                                  AT_PASS_OLD_CONSTR,
   14693             :                                                  oldId,
   14694             :                                                  rel,
   14695             :                                                  NIL,
   14696         326 :                                                  con->conname);
   14697             :                     else
   14698             :                         Assert(con->contype == CONSTR_NOTNULL);
   14699             :                 }
   14700             :                 else
   14701           0 :                     elog(ERROR, "unexpected statement subtype: %d",
   14702             :                          (int) cmd->subtype);
   14703             :             }
   14704             :         }
   14705          28 :         else if (IsA(stm, AlterDomainStmt))
   14706             :         {
   14707          14 :             AlterDomainStmt *stmt = (AlterDomainStmt *) stm;
   14708             : 
   14709          14 :             if (stmt->subtype == 'C')    /* ADD CONSTRAINT */
   14710             :             {
   14711          14 :                 Constraint *con = castNode(Constraint, stmt->def);
   14712          14 :                 AlterTableCmd *cmd = makeNode(AlterTableCmd);
   14713             : 
   14714          14 :                 cmd->subtype = AT_ReAddDomainConstraint;
   14715          14 :                 cmd->def = (Node *) stmt;
   14716          14 :                 tab->subcmds[AT_PASS_OLD_CONSTR] =
   14717          14 :                     lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
   14718             : 
   14719             :                 /* recreate any comment on the constraint */
   14720          14 :                 RebuildConstraintComment(tab,
   14721             :                                          AT_PASS_OLD_CONSTR,
   14722             :                                          oldId,
   14723             :                                          NULL,
   14724             :                                          stmt->typeName,
   14725          14 :                                          con->conname);
   14726             :             }
   14727             :             else
   14728           0 :                 elog(ERROR, "unexpected statement subtype: %d",
   14729             :                      (int) stmt->subtype);
   14730             :         }
   14731          14 :         else if (IsA(stm, CreateStatsStmt))
   14732             :         {
   14733          14 :             CreateStatsStmt *stmt = (CreateStatsStmt *) stm;
   14734             :             AlterTableCmd *newcmd;
   14735             : 
   14736             :             /* keep the statistics object's comment */
   14737          14 :             stmt->stxcomment = GetComment(oldId, StatisticExtRelationId, 0);
   14738             : 
   14739          14 :             newcmd = makeNode(AlterTableCmd);
   14740          14 :             newcmd->subtype = AT_ReAddStatistics;
   14741          14 :             newcmd->def = (Node *) stmt;
   14742          14 :             tab->subcmds[AT_PASS_MISC] =
   14743          14 :                 lappend(tab->subcmds[AT_PASS_MISC], newcmd);
   14744             :         }
   14745             :         else
   14746           0 :             elog(ERROR, "unexpected statement type: %d",
   14747             :                  (int) nodeTag(stm));
   14748             :     }
   14749             : 
   14750         798 :     relation_close(rel, NoLock);
   14751         798 : }
   14752             : 
   14753             : /*
   14754             :  * Subroutine for ATPostAlterTypeParse() to recreate any existing comment
   14755             :  * for a table or domain constraint that is being rebuilt.
   14756             :  *
   14757             :  * objid is the OID of the constraint.
   14758             :  * Pass "rel" for a table constraint, or "domname" (domain's qualified name
   14759             :  * as a string list) for a domain constraint.
   14760             :  * (We could dig that info, as well as the conname, out of the pg_constraint
   14761             :  * entry; but callers already have them so might as well pass them.)
   14762             :  */
   14763             : static void
   14764         568 : RebuildConstraintComment(AlteredTableInfo *tab, AlterTablePass pass, Oid objid,
   14765             :                          Relation rel, List *domname,
   14766             :                          const char *conname)
   14767             : {
   14768             :     CommentStmt *cmd;
   14769             :     char       *comment_str;
   14770             :     AlterTableCmd *newcmd;
   14771             : 
   14772             :     /* Look for comment for object wanted, and leave if none */
   14773         568 :     comment_str = GetComment(objid, ConstraintRelationId, 0);
   14774         568 :     if (comment_str == NULL)
   14775         478 :         return;
   14776             : 
   14777             :     /* Build CommentStmt node, copying all input data for safety */
   14778          90 :     cmd = makeNode(CommentStmt);
   14779          90 :     if (rel)
   14780             :     {
   14781          78 :         cmd->objtype = OBJECT_TABCONSTRAINT;
   14782          78 :         cmd->object = (Node *)
   14783          78 :             list_make3(makeString(get_namespace_name(RelationGetNamespace(rel))),
   14784             :                        makeString(pstrdup(RelationGetRelationName(rel))),
   14785             :                        makeString(pstrdup(conname)));
   14786             :     }
   14787             :     else
   14788             :     {
   14789          12 :         cmd->objtype = OBJECT_DOMCONSTRAINT;
   14790          12 :         cmd->object = (Node *)
   14791          12 :             list_make2(makeTypeNameFromNameList(copyObject(domname)),
   14792             :                        makeString(pstrdup(conname)));
   14793             :     }
   14794          90 :     cmd->comment = comment_str;
   14795             : 
   14796             :     /* Append it to list of commands */
   14797          90 :     newcmd = makeNode(AlterTableCmd);
   14798          90 :     newcmd->subtype = AT_ReAddComment;
   14799          90 :     newcmd->def = (Node *) cmd;
   14800          90 :     tab->subcmds[pass] = lappend(tab->subcmds[pass], newcmd);
   14801             : }
   14802             : 
   14803             : /*
   14804             :  * Subroutine for ATPostAlterTypeParse().  Calls out to CheckIndexCompatible()
   14805             :  * for the real analysis, then mutates the IndexStmt based on that verdict.
   14806             :  */
   14807             : static void
   14808         104 : TryReuseIndex(Oid oldId, IndexStmt *stmt)
   14809             : {
   14810         104 :     if (CheckIndexCompatible(oldId,
   14811         104 :                              stmt->accessMethod,
   14812         104 :                              stmt->indexParams,
   14813         104 :                              stmt->excludeOpNames,
   14814         104 :                              stmt->iswithoutoverlaps))
   14815             :     {
   14816         104 :         Relation    irel = index_open(oldId, NoLock);
   14817             : 
   14818             :         /* If it's a partitioned index, there is no storage to share. */
   14819         104 :         if (irel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
   14820             :         {
   14821          74 :             stmt->oldNumber = irel->rd_locator.relNumber;
   14822          74 :             stmt->oldCreateSubid = irel->rd_createSubid;
   14823          74 :             stmt->oldFirstRelfilelocatorSubid = irel->rd_firstRelfilelocatorSubid;
   14824             :         }
   14825         104 :         index_close(irel, NoLock);
   14826             :     }
   14827         104 : }
   14828             : 
   14829             : /*
   14830             :  * Subroutine for ATPostAlterTypeParse().
   14831             :  *
   14832             :  * Stash the old P-F equality operator into the Constraint node, for possible
   14833             :  * use by ATAddForeignKeyConstraint() in determining whether revalidation of
   14834             :  * this constraint can be skipped.
   14835             :  */
   14836             : static void
   14837           6 : TryReuseForeignKey(Oid oldId, Constraint *con)
   14838             : {
   14839             :     HeapTuple   tup;
   14840             :     Datum       adatum;
   14841             :     ArrayType  *arr;
   14842             :     Oid        *rawarr;
   14843             :     int         numkeys;
   14844             :     int         i;
   14845             : 
   14846             :     Assert(con->contype == CONSTR_FOREIGN);
   14847             :     Assert(con->old_conpfeqop == NIL);   /* already prepared this node */
   14848             : 
   14849           6 :     tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(oldId));
   14850           6 :     if (!HeapTupleIsValid(tup)) /* should not happen */
   14851           0 :         elog(ERROR, "cache lookup failed for constraint %u", oldId);
   14852             : 
   14853           6 :     adatum = SysCacheGetAttrNotNull(CONSTROID, tup,
   14854             :                                     Anum_pg_constraint_conpfeqop);
   14855           6 :     arr = DatumGetArrayTypeP(adatum);   /* ensure not toasted */
   14856           6 :     numkeys = ARR_DIMS(arr)[0];
   14857             :     /* test follows the one in ri_FetchConstraintInfo() */
   14858           6 :     if (ARR_NDIM(arr) != 1 ||
   14859           6 :         ARR_HASNULL(arr) ||
   14860           6 :         ARR_ELEMTYPE(arr) != OIDOID)
   14861           0 :         elog(ERROR, "conpfeqop is not a 1-D Oid array");
   14862           6 :     rawarr = (Oid *) ARR_DATA_PTR(arr);
   14863             : 
   14864             :     /* stash a List of the operator Oids in our Constraint node */
   14865          12 :     for (i = 0; i < numkeys; i++)
   14866           6 :         con->old_conpfeqop = lappend_oid(con->old_conpfeqop, rawarr[i]);
   14867             : 
   14868           6 :     ReleaseSysCache(tup);
   14869           6 : }
   14870             : 
   14871             : /*
   14872             :  * ALTER COLUMN .. OPTIONS ( ... )
   14873             :  *
   14874             :  * Returns the address of the modified column
   14875             :  */
   14876             : static ObjectAddress
   14877         172 : ATExecAlterColumnGenericOptions(Relation rel,
   14878             :                                 const char *colName,
   14879             :                                 List *options,
   14880             :                                 LOCKMODE lockmode)
   14881             : {
   14882             :     Relation    ftrel;
   14883             :     Relation    attrel;
   14884             :     ForeignServer *server;
   14885             :     ForeignDataWrapper *fdw;
   14886             :     HeapTuple   tuple;
   14887             :     HeapTuple   newtuple;
   14888             :     bool        isnull;
   14889             :     Datum       repl_val[Natts_pg_attribute];
   14890             :     bool        repl_null[Natts_pg_attribute];
   14891             :     bool        repl_repl[Natts_pg_attribute];
   14892             :     Datum       datum;
   14893             :     Form_pg_foreign_table fttableform;
   14894             :     Form_pg_attribute atttableform;
   14895             :     AttrNumber  attnum;
   14896             :     ObjectAddress address;
   14897             : 
   14898         172 :     if (options == NIL)
   14899           0 :         return InvalidObjectAddress;
   14900             : 
   14901             :     /* First, determine FDW validator associated to the foreign table. */
   14902         172 :     ftrel = table_open(ForeignTableRelationId, AccessShareLock);
   14903         172 :     tuple = SearchSysCache1(FOREIGNTABLEREL, ObjectIdGetDatum(rel->rd_id));
   14904         172 :     if (!HeapTupleIsValid(tuple))
   14905           0 :         ereport(ERROR,
   14906             :                 (errcode(ERRCODE_UNDEFINED_OBJECT),
   14907             :                  errmsg("foreign table \"%s\" does not exist",
   14908             :                         RelationGetRelationName(rel))));
   14909         172 :     fttableform = (Form_pg_foreign_table) GETSTRUCT(tuple);
   14910         172 :     server = GetForeignServer(fttableform->ftserver);
   14911         172 :     fdw = GetForeignDataWrapper(server->fdwid);
   14912             : 
   14913         172 :     table_close(ftrel, AccessShareLock);
   14914         172 :     ReleaseSysCache(tuple);
   14915             : 
   14916         172 :     attrel = table_open(AttributeRelationId, RowExclusiveLock);
   14917         172 :     tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
   14918         172 :     if (!HeapTupleIsValid(tuple))
   14919           0 :         ereport(ERROR,
   14920             :                 (errcode(ERRCODE_UNDEFINED_COLUMN),
   14921             :                  errmsg("column \"%s\" of relation \"%s\" does not exist",
   14922             :                         colName, RelationGetRelationName(rel))));
   14923             : 
   14924             :     /* Prevent them from altering a system attribute */
   14925         172 :     atttableform = (Form_pg_attribute) GETSTRUCT(tuple);
   14926         172 :     attnum = atttableform->attnum;
   14927         172 :     if (attnum <= 0)
   14928           6 :         ereport(ERROR,
   14929             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   14930             :                  errmsg("cannot alter system column \"%s\"", colName)));
   14931             : 
   14932             : 
   14933             :     /* Initialize buffers for new tuple values */
   14934         166 :     memset(repl_val, 0, sizeof(repl_val));
   14935         166 :     memset(repl_null, false, sizeof(repl_null));
   14936         166 :     memset(repl_repl, false, sizeof(repl_repl));
   14937             : 
   14938             :     /* Extract the current options */
   14939         166 :     datum = SysCacheGetAttr(ATTNAME,
   14940             :                             tuple,
   14941             :                             Anum_pg_attribute_attfdwoptions,
   14942             :                             &isnull);
   14943         166 :     if (isnull)
   14944         156 :         datum = PointerGetDatum(NULL);
   14945             : 
   14946             :     /* Transform the options */
   14947         166 :     datum = transformGenericOptions(AttributeRelationId,
   14948             :                                     datum,
   14949             :                                     options,
   14950             :                                     fdw->fdwvalidator);
   14951             : 
   14952         166 :     if (PointerIsValid(DatumGetPointer(datum)))
   14953         166 :         repl_val[Anum_pg_attribute_attfdwoptions - 1] = datum;
   14954             :     else
   14955           0 :         repl_null[Anum_pg_attribute_attfdwoptions - 1] = true;
   14956             : 
   14957         166 :     repl_repl[Anum_pg_attribute_attfdwoptions - 1] = true;
   14958             : 
   14959             :     /* Everything looks good - update the tuple */
   14960             : 
   14961         166 :     newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrel),
   14962             :                                  repl_val, repl_null, repl_repl);
   14963             : 
   14964         166 :     CatalogTupleUpdate(attrel, &newtuple->t_self, newtuple);
   14965             : 
   14966         166 :     InvokeObjectPostAlterHook(RelationRelationId,
   14967             :                               RelationGetRelid(rel),
   14968             :                               atttableform->attnum);
   14969         166 :     ObjectAddressSubSet(address, RelationRelationId,
   14970             :                         RelationGetRelid(rel), attnum);
   14971             : 
   14972         166 :     ReleaseSysCache(tuple);
   14973             : 
   14974         166 :     table_close(attrel, RowExclusiveLock);
   14975             : 
   14976         166 :     heap_freetuple(newtuple);
   14977             : 
   14978         166 :     return address;
   14979             : }
   14980             : 
   14981             : /*
   14982             :  * ALTER TABLE OWNER
   14983             :  *
   14984             :  * recursing is true if we are recursing from a table to its indexes,
   14985             :  * sequences, or toast table.  We don't allow the ownership of those things to
   14986             :  * be changed separately from the parent table.  Also, we can skip permission
   14987             :  * checks (this is necessary not just an optimization, else we'd fail to
   14988             :  * handle toast tables properly).
   14989             :  *
   14990             :  * recursing is also true if ALTER TYPE OWNER is calling us to fix up a
   14991             :  * free-standing composite type.
   14992             :  */
   14993             : void
   14994        2022 : ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing, LOCKMODE lockmode)
   14995             : {
   14996             :     Relation    target_rel;
   14997             :     Relation    class_rel;
   14998             :     HeapTuple   tuple;
   14999             :     Form_pg_class tuple_class;
   15000             : 
   15001             :     /*
   15002             :      * Get exclusive lock till end of transaction on the target table. Use
   15003             :      * relation_open so that we can work on indexes and sequences.
   15004             :      */
   15005        2022 :     target_rel = relation_open(relationOid, lockmode);
   15006             : 
   15007             :     /* Get its pg_class tuple, too */
   15008        2022 :     class_rel = table_open(RelationRelationId, RowExclusiveLock);
   15009             : 
   15010        2022 :     tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relationOid));
   15011        2022 :     if (!HeapTupleIsValid(tuple))
   15012           0 :         elog(ERROR, "cache lookup failed for relation %u", relationOid);
   15013        2022 :     tuple_class = (Form_pg_class) GETSTRUCT(tuple);
   15014             : 
   15015             :     /* Can we change the ownership of this tuple? */
   15016        2022 :     switch (tuple_class->relkind)
   15017             :     {
   15018        1756 :         case RELKIND_RELATION:
   15019             :         case RELKIND_VIEW:
   15020             :         case RELKIND_MATVIEW:
   15021             :         case RELKIND_FOREIGN_TABLE:
   15022             :         case RELKIND_PARTITIONED_TABLE:
   15023             :             /* ok to change owner */
   15024        1756 :             break;
   15025          96 :         case RELKIND_INDEX:
   15026          96 :             if (!recursing)
   15027             :             {
   15028             :                 /*
   15029             :                  * Because ALTER INDEX OWNER used to be allowed, and in fact
   15030             :                  * is generated by old versions of pg_dump, we give a warning
   15031             :                  * and do nothing rather than erroring out.  Also, to avoid
   15032             :                  * unnecessary chatter while restoring those old dumps, say
   15033             :                  * nothing at all if the command would be a no-op anyway.
   15034             :                  */
   15035           0 :                 if (tuple_class->relowner != newOwnerId)
   15036           0 :                     ereport(WARNING,
   15037             :                             (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   15038             :                              errmsg("cannot change owner of index \"%s\"",
   15039             :                                     NameStr(tuple_class->relname)),
   15040             :                              errhint("Change the ownership of the index's table instead.")));
   15041             :                 /* quick hack to exit via the no-op path */
   15042           0 :                 newOwnerId = tuple_class->relowner;
   15043             :             }
   15044          96 :             break;
   15045          20 :         case RELKIND_PARTITIONED_INDEX:
   15046          20 :             if (recursing)
   15047          20 :                 break;
   15048           0 :             ereport(ERROR,
   15049             :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   15050             :                      errmsg("cannot change owner of index \"%s\"",
   15051             :                             NameStr(tuple_class->relname)),
   15052             :                      errhint("Change the ownership of the index's table instead.")));
   15053             :             break;
   15054         100 :         case RELKIND_SEQUENCE:
   15055         100 :             if (!recursing &&
   15056          52 :                 tuple_class->relowner != newOwnerId)
   15057             :             {
   15058             :                 /* if it's an owned sequence, disallow changing it by itself */
   15059             :                 Oid         tableId;
   15060             :                 int32       colId;
   15061             : 
   15062           0 :                 if (sequenceIsOwned(relationOid, DEPENDENCY_AUTO, &tableId, &colId) ||
   15063           0 :                     sequenceIsOwned(relationOid, DEPENDENCY_INTERNAL, &tableId, &colId))
   15064           0 :                     ereport(ERROR,
   15065             :                             (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   15066             :                              errmsg("cannot change owner of sequence \"%s\"",
   15067             :                                     NameStr(tuple_class->relname)),
   15068             :                              errdetail("Sequence \"%s\" is linked to table \"%s\".",
   15069             :                                        NameStr(tuple_class->relname),
   15070             :                                        get_rel_name(tableId))));
   15071             :             }
   15072         100 :             break;
   15073           8 :         case RELKIND_COMPOSITE_TYPE:
   15074           8 :             if (recursing)
   15075           8 :                 break;
   15076           0 :             ereport(ERROR,
   15077             :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   15078             :                      errmsg("\"%s\" is a composite type",
   15079             :                             NameStr(tuple_class->relname)),
   15080             :             /* translator: %s is an SQL ALTER command */
   15081             :                      errhint("Use %s instead.",
   15082             :                              "ALTER TYPE")));
   15083             :             break;
   15084          42 :         case RELKIND_TOASTVALUE:
   15085          42 :             if (recursing)
   15086          42 :                 break;
   15087             :             /* FALL THRU */
   15088             :         default:
   15089           0 :             ereport(ERROR,
   15090             :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   15091             :                      errmsg("cannot change owner of relation \"%s\"",
   15092             :                             NameStr(tuple_class->relname)),
   15093             :                      errdetail_relkind_not_supported(tuple_class->relkind)));
   15094             :     }
   15095             : 
   15096             :     /*
   15097             :      * If the new owner is the same as the existing owner, consider the
   15098             :      * command to have succeeded.  This is for dump restoration purposes.
   15099             :      */
   15100        2022 :     if (tuple_class->relowner != newOwnerId)
   15101             :     {
   15102             :         Datum       repl_val[Natts_pg_class];
   15103             :         bool        repl_null[Natts_pg_class];
   15104             :         bool        repl_repl[Natts_pg_class];
   15105             :         Acl        *newAcl;
   15106             :         Datum       aclDatum;
   15107             :         bool        isNull;
   15108             :         HeapTuple   newtuple;
   15109             : 
   15110             :         /* skip permission checks when recursing to index or toast table */
   15111         498 :         if (!recursing)
   15112             :         {
   15113             :             /* Superusers can always do it */
   15114         280 :             if (!superuser())
   15115             :             {
   15116          42 :                 Oid         namespaceOid = tuple_class->relnamespace;
   15117             :                 AclResult   aclresult;
   15118             : 
   15119             :                 /* Otherwise, must be owner of the existing object */
   15120          42 :                 if (!object_ownercheck(RelationRelationId, relationOid, GetUserId()))
   15121           0 :                     aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relationOid)),
   15122           0 :                                    RelationGetRelationName(target_rel));
   15123             : 
   15124             :                 /* Must be able to become new owner */
   15125          42 :                 check_can_set_role(GetUserId(), newOwnerId);
   15126             : 
   15127             :                 /* New owner must have CREATE privilege on namespace */
   15128          30 :                 aclresult = object_aclcheck(NamespaceRelationId, namespaceOid, newOwnerId,
   15129             :                                             ACL_CREATE);
   15130          30 :                 if (aclresult != ACLCHECK_OK)
   15131           0 :                     aclcheck_error(aclresult, OBJECT_SCHEMA,
   15132           0 :                                    get_namespace_name(namespaceOid));
   15133             :             }
   15134             :         }
   15135             : 
   15136         486 :         memset(repl_null, false, sizeof(repl_null));
   15137         486 :         memset(repl_repl, false, sizeof(repl_repl));
   15138             : 
   15139         486 :         repl_repl[Anum_pg_class_relowner - 1] = true;
   15140         486 :         repl_val[Anum_pg_class_relowner - 1] = ObjectIdGetDatum(newOwnerId);
   15141             : 
   15142             :         /*
   15143             :          * Determine the modified ACL for the new owner.  This is only
   15144             :          * necessary when the ACL is non-null.
   15145             :          */
   15146         486 :         aclDatum = SysCacheGetAttr(RELOID, tuple,
   15147             :                                    Anum_pg_class_relacl,
   15148             :                                    &isNull);
   15149         486 :         if (!isNull)
   15150             :         {
   15151          46 :             newAcl = aclnewowner(DatumGetAclP(aclDatum),
   15152             :                                  tuple_class->relowner, newOwnerId);
   15153          46 :             repl_repl[Anum_pg_class_relacl - 1] = true;
   15154          46 :             repl_val[Anum_pg_class_relacl - 1] = PointerGetDatum(newAcl);
   15155             :         }
   15156             : 
   15157         486 :         newtuple = heap_modify_tuple(tuple, RelationGetDescr(class_rel), repl_val, repl_null, repl_repl);
   15158             : 
   15159         486 :         CatalogTupleUpdate(class_rel, &newtuple->t_self, newtuple);
   15160             : 
   15161         486 :         heap_freetuple(newtuple);
   15162             : 
   15163             :         /*
   15164             :          * We must similarly update any per-column ACLs to reflect the new
   15165             :          * owner; for neatness reasons that's split out as a subroutine.
   15166             :          */
   15167         486 :         change_owner_fix_column_acls(relationOid,
   15168             :                                      tuple_class->relowner,
   15169             :                                      newOwnerId);
   15170             : 
   15171             :         /*
   15172             :          * Update owner dependency reference, if any.  A composite type has
   15173             :          * none, because it's tracked for the pg_type entry instead of here;
   15174             :          * indexes and TOAST tables don't have their own entries either.
   15175             :          */
   15176         486 :         if (tuple_class->relkind != RELKIND_COMPOSITE_TYPE &&
   15177         478 :             tuple_class->relkind != RELKIND_INDEX &&
   15178         382 :             tuple_class->relkind != RELKIND_PARTITIONED_INDEX &&
   15179         362 :             tuple_class->relkind != RELKIND_TOASTVALUE)
   15180         320 :             changeDependencyOnOwner(RelationRelationId, relationOid,
   15181             :                                     newOwnerId);
   15182             : 
   15183             :         /*
   15184             :          * Also change the ownership of the table's row type, if it has one
   15185             :          */
   15186         486 :         if (OidIsValid(tuple_class->reltype))
   15187         294 :             AlterTypeOwnerInternal(tuple_class->reltype, newOwnerId);
   15188             : 
   15189             :         /*
   15190             :          * If we are operating on a table or materialized view, also change
   15191             :          * the ownership of any indexes and sequences that belong to the
   15192             :          * relation, as well as its toast table (if it has one).
   15193             :          */
   15194         486 :         if (tuple_class->relkind == RELKIND_RELATION ||
   15195         262 :             tuple_class->relkind == RELKIND_PARTITIONED_TABLE ||
   15196         224 :             tuple_class->relkind == RELKIND_MATVIEW ||
   15197         224 :             tuple_class->relkind == RELKIND_TOASTVALUE)
   15198             :         {
   15199             :             List       *index_oid_list;
   15200             :             ListCell   *i;
   15201             : 
   15202             :             /* Find all the indexes belonging to this relation */
   15203         304 :             index_oid_list = RelationGetIndexList(target_rel);
   15204             : 
   15205             :             /* For each index, recursively change its ownership */
   15206         420 :             foreach(i, index_oid_list)
   15207         116 :                 ATExecChangeOwner(lfirst_oid(i), newOwnerId, true, lockmode);
   15208             : 
   15209         304 :             list_free(index_oid_list);
   15210             :         }
   15211             : 
   15212             :         /* If it has a toast table, recurse to change its ownership */
   15213         486 :         if (tuple_class->reltoastrelid != InvalidOid)
   15214          42 :             ATExecChangeOwner(tuple_class->reltoastrelid, newOwnerId,
   15215             :                               true, lockmode);
   15216             : 
   15217             :         /* If it has dependent sequences, recurse to change them too */
   15218         486 :         change_owner_recurse_to_sequences(relationOid, newOwnerId, lockmode);
   15219             :     }
   15220             : 
   15221        2010 :     InvokeObjectPostAlterHook(RelationRelationId, relationOid, 0);
   15222             : 
   15223        2010 :     ReleaseSysCache(tuple);
   15224        2010 :     table_close(class_rel, RowExclusiveLock);
   15225        2010 :     relation_close(target_rel, NoLock);
   15226        2010 : }
   15227             : 
   15228             : /*
   15229             :  * change_owner_fix_column_acls
   15230             :  *
   15231             :  * Helper function for ATExecChangeOwner.  Scan the columns of the table
   15232             :  * and fix any non-null column ACLs to reflect the new owner.
   15233             :  */
   15234             : static void
   15235         486 : change_owner_fix_column_acls(Oid relationOid, Oid oldOwnerId, Oid newOwnerId)
   15236             : {
   15237             :     Relation    attRelation;
   15238             :     SysScanDesc scan;
   15239             :     ScanKeyData key[1];
   15240             :     HeapTuple   attributeTuple;
   15241             : 
   15242         486 :     attRelation = table_open(AttributeRelationId, RowExclusiveLock);
   15243         486 :     ScanKeyInit(&key[0],
   15244             :                 Anum_pg_attribute_attrelid,
   15245             :                 BTEqualStrategyNumber, F_OIDEQ,
   15246             :                 ObjectIdGetDatum(relationOid));
   15247         486 :     scan = systable_beginscan(attRelation, AttributeRelidNumIndexId,
   15248             :                               true, NULL, 1, key);
   15249        3372 :     while (HeapTupleIsValid(attributeTuple = systable_getnext(scan)))
   15250             :     {
   15251        2886 :         Form_pg_attribute att = (Form_pg_attribute) GETSTRUCT(attributeTuple);
   15252             :         Datum       repl_val[Natts_pg_attribute];
   15253             :         bool        repl_null[Natts_pg_attribute];
   15254             :         bool        repl_repl[Natts_pg_attribute];
   15255             :         Acl        *newAcl;
   15256             :         Datum       aclDatum;
   15257             :         bool        isNull;
   15258             :         HeapTuple   newtuple;
   15259             : 
   15260             :         /* Ignore dropped columns */
   15261        2886 :         if (att->attisdropped)
   15262        2884 :             continue;
   15263             : 
   15264        2886 :         aclDatum = heap_getattr(attributeTuple,
   15265             :                                 Anum_pg_attribute_attacl,
   15266             :                                 RelationGetDescr(attRelation),
   15267             :                                 &isNull);
   15268             :         /* Null ACLs do not require changes */
   15269        2886 :         if (isNull)
   15270        2884 :             continue;
   15271             : 
   15272           2 :         memset(repl_null, false, sizeof(repl_null));
   15273           2 :         memset(repl_repl, false, sizeof(repl_repl));
   15274             : 
   15275           2 :         newAcl = aclnewowner(DatumGetAclP(aclDatum),
   15276             :                              oldOwnerId, newOwnerId);
   15277           2 :         repl_repl[Anum_pg_attribute_attacl - 1] = true;
   15278           2 :         repl_val[Anum_pg_attribute_attacl - 1] = PointerGetDatum(newAcl);
   15279             : 
   15280           2 :         newtuple = heap_modify_tuple(attributeTuple,
   15281             :                                      RelationGetDescr(attRelation),
   15282             :                                      repl_val, repl_null, repl_repl);
   15283             : 
   15284           2 :         CatalogTupleUpdate(attRelation, &newtuple->t_self, newtuple);
   15285             : 
   15286           2 :         heap_freetuple(newtuple);
   15287             :     }
   15288         486 :     systable_endscan(scan);
   15289         486 :     table_close(attRelation, RowExclusiveLock);
   15290         486 : }
   15291             : 
   15292             : /*
   15293             :  * change_owner_recurse_to_sequences
   15294             :  *
   15295             :  * Helper function for ATExecChangeOwner.  Examines pg_depend searching
   15296             :  * for sequences that are dependent on serial columns, and changes their
   15297             :  * ownership.
   15298             :  */
   15299             : static void
   15300         486 : change_owner_recurse_to_sequences(Oid relationOid, Oid newOwnerId, LOCKMODE lockmode)
   15301             : {
   15302             :     Relation    depRel;
   15303             :     SysScanDesc scan;
   15304             :     ScanKeyData key[2];
   15305             :     HeapTuple   tup;
   15306             : 
   15307             :     /*
   15308             :      * SERIAL sequences are those having an auto dependency on one of the
   15309             :      * table's columns (we don't care *which* column, exactly).
   15310             :      */
   15311         486 :     depRel = table_open(DependRelationId, AccessShareLock);
   15312             : 
   15313         486 :     ScanKeyInit(&key[0],
   15314             :                 Anum_pg_depend_refclassid,
   15315             :                 BTEqualStrategyNumber, F_OIDEQ,
   15316             :                 ObjectIdGetDatum(RelationRelationId));
   15317         486 :     ScanKeyInit(&key[1],
   15318             :                 Anum_pg_depend_refobjid,
   15319             :                 BTEqualStrategyNumber, F_OIDEQ,
   15320             :                 ObjectIdGetDatum(relationOid));
   15321             :     /* we leave refobjsubid unspecified */
   15322             : 
   15323         486 :     scan = systable_beginscan(depRel, DependReferenceIndexId, true,
   15324             :                               NULL, 2, key);
   15325             : 
   15326        1374 :     while (HeapTupleIsValid(tup = systable_getnext(scan)))
   15327             :     {
   15328         888 :         Form_pg_depend depForm = (Form_pg_depend) GETSTRUCT(tup);
   15329             :         Relation    seqRel;
   15330             : 
   15331             :         /* skip dependencies other than auto dependencies on columns */
   15332         888 :         if (depForm->refobjsubid == 0 ||
   15333         352 :             depForm->classid != RelationRelationId ||
   15334         142 :             depForm->objsubid != 0 ||
   15335         142 :             !(depForm->deptype == DEPENDENCY_AUTO || depForm->deptype == DEPENDENCY_INTERNAL))
   15336         746 :             continue;
   15337             : 
   15338             :         /* Use relation_open just in case it's an index */
   15339         142 :         seqRel = relation_open(depForm->objid, lockmode);
   15340             : 
   15341             :         /* skip non-sequence relations */
   15342         142 :         if (RelationGetForm(seqRel)->relkind != RELKIND_SEQUENCE)
   15343             :         {
   15344             :             /* No need to keep the lock */
   15345         116 :             relation_close(seqRel, lockmode);
   15346         116 :             continue;
   15347             :         }
   15348             : 
   15349             :         /* We don't need to close the sequence while we alter it. */
   15350          26 :         ATExecChangeOwner(depForm->objid, newOwnerId, true, lockmode);
   15351             : 
   15352             :         /* Now we can close it.  Keep the lock till end of transaction. */
   15353          26 :         relation_close(seqRel, NoLock);
   15354             :     }
   15355             : 
   15356         486 :     systable_endscan(scan);
   15357             : 
   15358         486 :     relation_close(depRel, AccessShareLock);
   15359         486 : }
   15360             : 
   15361             : /*
   15362             :  * ALTER TABLE CLUSTER ON
   15363             :  *
   15364             :  * The only thing we have to do is to change the indisclustered bits.
   15365             :  *
   15366             :  * Return the address of the new clustering index.
   15367             :  */
   15368             : static ObjectAddress
   15369          64 : ATExecClusterOn(Relation rel, const char *indexName, LOCKMODE lockmode)
   15370             : {
   15371             :     Oid         indexOid;
   15372             :     ObjectAddress address;
   15373             : 
   15374          64 :     indexOid = get_relname_relid(indexName, rel->rd_rel->relnamespace);
   15375             : 
   15376          64 :     if (!OidIsValid(indexOid))
   15377           0 :         ereport(ERROR,
   15378             :                 (errcode(ERRCODE_UNDEFINED_OBJECT),
   15379             :                  errmsg("index \"%s\" for table \"%s\" does not exist",
   15380             :                         indexName, RelationGetRelationName(rel))));
   15381             : 
   15382             :     /* Check index is valid to cluster on */
   15383          64 :     check_index_is_clusterable(rel, indexOid, lockmode);
   15384             : 
   15385             :     /* And do the work */
   15386          64 :     mark_index_clustered(rel, indexOid, false);
   15387             : 
   15388          58 :     ObjectAddressSet(address,
   15389             :                      RelationRelationId, indexOid);
   15390             : 
   15391          58 :     return address;
   15392             : }
   15393             : 
   15394             : /*
   15395             :  * ALTER TABLE SET WITHOUT CLUSTER
   15396             :  *
   15397             :  * We have to find any indexes on the table that have indisclustered bit
   15398             :  * set and turn it off.
   15399             :  */
   15400             : static void
   15401          18 : ATExecDropCluster(Relation rel, LOCKMODE lockmode)
   15402             : {
   15403          18 :     mark_index_clustered(rel, InvalidOid, false);
   15404          12 : }
   15405             : 
   15406             : /*
   15407             :  * Preparation phase for SET ACCESS METHOD
   15408             :  *
   15409             :  * Check that the access method exists and determine whether a change is
   15410             :  * actually needed.
   15411             :  */
   15412             : static void
   15413         110 : ATPrepSetAccessMethod(AlteredTableInfo *tab, Relation rel, const char *amname)
   15414             : {
   15415             :     Oid         amoid;
   15416             : 
   15417             :     /*
   15418             :      * Look up the access method name and check that it differs from the
   15419             :      * table's current AM.  If DEFAULT was specified for a partitioned table
   15420             :      * (amname is NULL), set it to InvalidOid to reset the catalogued AM.
   15421             :      */
   15422         110 :     if (amname != NULL)
   15423          74 :         amoid = get_table_am_oid(amname, false);
   15424          36 :     else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
   15425          18 :         amoid = InvalidOid;
   15426             :     else
   15427          18 :         amoid = get_table_am_oid(default_table_access_method, false);
   15428             : 
   15429             :     /* if it's a match, phase 3 doesn't need to do anything */
   15430         110 :     if (rel->rd_rel->relam == amoid)
   15431          12 :         return;
   15432             : 
   15433             :     /* Save info for Phase 3 to do the real work */
   15434          98 :     tab->rewrite |= AT_REWRITE_ACCESS_METHOD;
   15435          98 :     tab->newAccessMethod = amoid;
   15436          98 :     tab->chgAccessMethod = true;
   15437             : }
   15438             : 
   15439             : /*
   15440             :  * Special handling of ALTER TABLE SET ACCESS METHOD for relations with no
   15441             :  * storage that have an interest in preserving AM.
   15442             :  *
   15443             :  * Since these have no storage, setting the access method is a catalog only
   15444             :  * operation.
   15445             :  */
   15446             : static void
   15447          44 : ATExecSetAccessMethodNoStorage(Relation rel, Oid newAccessMethodId)
   15448             : {
   15449             :     Relation    pg_class;
   15450             :     Oid         oldAccessMethodId;
   15451             :     HeapTuple   tuple;
   15452             :     Form_pg_class rd_rel;
   15453          44 :     Oid         reloid = RelationGetRelid(rel);
   15454             : 
   15455             :     /*
   15456             :      * Shouldn't be called on relations having storage; these are processed in
   15457             :      * phase 3.
   15458             :      */
   15459             :     Assert(!RELKIND_HAS_STORAGE(rel->rd_rel->relkind));
   15460             : 
   15461             :     /* Get a modifiable copy of the relation's pg_class row. */
   15462          44 :     pg_class = table_open(RelationRelationId, RowExclusiveLock);
   15463             : 
   15464          44 :     tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(reloid));
   15465          44 :     if (!HeapTupleIsValid(tuple))
   15466           0 :         elog(ERROR, "cache lookup failed for relation %u", reloid);
   15467          44 :     rd_rel = (Form_pg_class) GETSTRUCT(tuple);
   15468             : 
   15469             :     /* Update the pg_class row. */
   15470          44 :     oldAccessMethodId = rd_rel->relam;
   15471          44 :     rd_rel->relam = newAccessMethodId;
   15472             : 
   15473             :     /* Leave if no update required */
   15474          44 :     if (rd_rel->relam == oldAccessMethodId)
   15475             :     {
   15476           0 :         heap_freetuple(tuple);
   15477           0 :         table_close(pg_class, RowExclusiveLock);
   15478           0 :         return;
   15479             :     }
   15480             : 
   15481          44 :     CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
   15482             : 
   15483             :     /*
   15484             :      * Update the dependency on the new access method.  No dependency is added
   15485             :      * if the new access method is InvalidOid (default case).  Be very careful
   15486             :      * that this has to compare the previous value stored in pg_class with the
   15487             :      * new one.
   15488             :      */
   15489          44 :     if (!OidIsValid(oldAccessMethodId) && OidIsValid(rd_rel->relam))
   15490          20 :     {
   15491             :         ObjectAddress relobj,
   15492             :                     referenced;
   15493             : 
   15494             :         /*
   15495             :          * New access method is defined and there was no dependency
   15496             :          * previously, so record a new one.
   15497             :          */
   15498          20 :         ObjectAddressSet(relobj, RelationRelationId, reloid);
   15499          20 :         ObjectAddressSet(referenced, AccessMethodRelationId, rd_rel->relam);
   15500          20 :         recordDependencyOn(&relobj, &referenced, DEPENDENCY_NORMAL);
   15501             :     }
   15502          24 :     else if (OidIsValid(oldAccessMethodId) &&
   15503          24 :              !OidIsValid(rd_rel->relam))
   15504             :     {
   15505             :         /*
   15506             :          * There was an access method defined, and no new one, so just remove
   15507             :          * the existing dependency.
   15508             :          */
   15509          12 :         deleteDependencyRecordsForClass(RelationRelationId, reloid,
   15510             :                                         AccessMethodRelationId,
   15511             :                                         DEPENDENCY_NORMAL);
   15512             :     }
   15513             :     else
   15514             :     {
   15515             :         Assert(OidIsValid(oldAccessMethodId) &&
   15516             :                OidIsValid(rd_rel->relam));
   15517             : 
   15518             :         /* Both are valid, so update the dependency */
   15519          12 :         changeDependencyFor(RelationRelationId, reloid,
   15520             :                             AccessMethodRelationId,
   15521             :                             oldAccessMethodId, rd_rel->relam);
   15522             :     }
   15523             : 
   15524             :     /* make the relam and dependency changes visible */
   15525          44 :     CommandCounterIncrement();
   15526             : 
   15527          44 :     InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
   15528             : 
   15529          44 :     heap_freetuple(tuple);
   15530          44 :     table_close(pg_class, RowExclusiveLock);
   15531             : }
   15532             : 
   15533             : /*
   15534             :  * ALTER TABLE SET TABLESPACE
   15535             :  */
   15536             : static void
   15537         158 : ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel, const char *tablespacename, LOCKMODE lockmode)
   15538             : {
   15539             :     Oid         tablespaceId;
   15540             : 
   15541             :     /* Check that the tablespace exists */
   15542         158 :     tablespaceId = get_tablespace_oid(tablespacename, false);
   15543             : 
   15544             :     /* Check permissions except when moving to database's default */
   15545         158 :     if (OidIsValid(tablespaceId) && tablespaceId != MyDatabaseTableSpace)
   15546             :     {
   15547             :         AclResult   aclresult;
   15548             : 
   15549          66 :         aclresult = object_aclcheck(TableSpaceRelationId, tablespaceId, GetUserId(), ACL_CREATE);
   15550          66 :         if (aclresult != ACLCHECK_OK)
   15551           0 :             aclcheck_error(aclresult, OBJECT_TABLESPACE, tablespacename);
   15552             :     }
   15553             : 
   15554             :     /* Save info for Phase 3 to do the real work */
   15555         158 :     if (OidIsValid(tab->newTableSpace))
   15556           0 :         ereport(ERROR,
   15557             :                 (errcode(ERRCODE_SYNTAX_ERROR),
   15558             :                  errmsg("cannot have multiple SET TABLESPACE subcommands")));
   15559             : 
   15560         158 :     tab->newTableSpace = tablespaceId;
   15561         158 : }
   15562             : 
   15563             : /*
   15564             :  * Set, reset, or replace reloptions.
   15565             :  */
   15566             : static void
   15567         946 : ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation,
   15568             :                     LOCKMODE lockmode)
   15569             : {
   15570             :     Oid         relid;
   15571             :     Relation    pgclass;
   15572             :     HeapTuple   tuple;
   15573             :     HeapTuple   newtuple;
   15574             :     Datum       datum;
   15575             :     bool        isnull;
   15576             :     Datum       newOptions;
   15577             :     Datum       repl_val[Natts_pg_class];
   15578             :     bool        repl_null[Natts_pg_class];
   15579             :     bool        repl_repl[Natts_pg_class];
   15580         946 :     const char *const validnsps[] = HEAP_RELOPT_NAMESPACES;
   15581             : 
   15582         946 :     if (defList == NIL && operation != AT_ReplaceRelOptions)
   15583           0 :         return;                 /* nothing to do */
   15584             : 
   15585         946 :     pgclass = table_open(RelationRelationId, RowExclusiveLock);
   15586             : 
   15587             :     /* Fetch heap tuple */
   15588         946 :     relid = RelationGetRelid(rel);
   15589         946 :     tuple = SearchSysCacheLocked1(RELOID, ObjectIdGetDatum(relid));
   15590         946 :     if (!HeapTupleIsValid(tuple))
   15591           0 :         elog(ERROR, "cache lookup failed for relation %u", relid);
   15592             : 
   15593         946 :     if (operation == AT_ReplaceRelOptions)
   15594             :     {
   15595             :         /*
   15596             :          * If we're supposed to replace the reloptions list, we just pretend
   15597             :          * there were none before.
   15598             :          */
   15599         194 :         datum = (Datum) 0;
   15600         194 :         isnull = true;
   15601             :     }
   15602             :     else
   15603             :     {
   15604             :         /* Get the old reloptions */
   15605         752 :         datum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions,
   15606             :                                 &isnull);
   15607             :     }
   15608             : 
   15609             :     /* Generate new proposed reloptions (text array) */
   15610         946 :     newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
   15611             :                                      defList, NULL, validnsps, false,
   15612             :                                      operation == AT_ResetRelOptions);
   15613             : 
   15614             :     /* Validate */
   15615         940 :     switch (rel->rd_rel->relkind)
   15616             :     {
   15617         524 :         case RELKIND_RELATION:
   15618             :         case RELKIND_TOASTVALUE:
   15619             :         case RELKIND_MATVIEW:
   15620         524 :             (void) heap_reloptions(rel->rd_rel->relkind, newOptions, true);
   15621         524 :             break;
   15622           6 :         case RELKIND_PARTITIONED_TABLE:
   15623           6 :             (void) partitioned_table_reloptions(newOptions, true);
   15624           0 :             break;
   15625         296 :         case RELKIND_VIEW:
   15626         296 :             (void) view_reloptions(newOptions, true);
   15627         278 :             break;
   15628         114 :         case RELKIND_INDEX:
   15629             :         case RELKIND_PARTITIONED_INDEX:
   15630         114 :             (void) index_reloptions(rel->rd_indam->amoptions, newOptions, true);
   15631          92 :             break;
   15632           0 :         default:
   15633           0 :             ereport(ERROR,
   15634             :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   15635             :                      errmsg("cannot set options for relation \"%s\"",
   15636             :                             RelationGetRelationName(rel)),
   15637             :                      errdetail_relkind_not_supported(rel->rd_rel->relkind)));
   15638             :             break;
   15639             :     }
   15640             : 
   15641             :     /* Special-case validation of view options */
   15642         894 :     if (rel->rd_rel->relkind == RELKIND_VIEW)
   15643             :     {
   15644         278 :         Query      *view_query = get_view_query(rel);
   15645         278 :         List       *view_options = untransformRelOptions(newOptions);
   15646             :         ListCell   *cell;
   15647         278 :         bool        check_option = false;
   15648             : 
   15649         380 :         foreach(cell, view_options)
   15650             :         {
   15651         102 :             DefElem    *defel = (DefElem *) lfirst(cell);
   15652             : 
   15653         102 :             if (strcmp(defel->defname, "check_option") == 0)
   15654          24 :                 check_option = true;
   15655             :         }
   15656             : 
   15657             :         /*
   15658             :          * If the check option is specified, look to see if the view is
   15659             :          * actually auto-updatable or not.
   15660             :          */
   15661         278 :         if (check_option)
   15662             :         {
   15663             :             const char *view_updatable_error =
   15664          24 :                 view_query_is_auto_updatable(view_query, true);
   15665             : 
   15666          24 :             if (view_updatable_error)
   15667           0 :                 ereport(ERROR,
   15668             :                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   15669             :                          errmsg("WITH CHECK OPTION is supported only on automatically updatable views"),
   15670             :                          errhint("%s", _(view_updatable_error))));
   15671             :         }
   15672             :     }
   15673             : 
   15674             :     /*
   15675             :      * All we need do here is update the pg_class row; the new options will be
   15676             :      * propagated into relcaches during post-commit cache inval.
   15677             :      */
   15678         894 :     memset(repl_val, 0, sizeof(repl_val));
   15679         894 :     memset(repl_null, false, sizeof(repl_null));
   15680         894 :     memset(repl_repl, false, sizeof(repl_repl));
   15681             : 
   15682         894 :     if (newOptions != (Datum) 0)
   15683         600 :         repl_val[Anum_pg_class_reloptions - 1] = newOptions;
   15684             :     else
   15685         294 :         repl_null[Anum_pg_class_reloptions - 1] = true;
   15686             : 
   15687         894 :     repl_repl[Anum_pg_class_reloptions - 1] = true;
   15688             : 
   15689         894 :     newtuple = heap_modify_tuple(tuple, RelationGetDescr(pgclass),
   15690             :                                  repl_val, repl_null, repl_repl);
   15691             : 
   15692         894 :     CatalogTupleUpdate(pgclass, &newtuple->t_self, newtuple);
   15693         894 :     UnlockTuple(pgclass, &tuple->t_self, InplaceUpdateTupleLock);
   15694             : 
   15695         894 :     InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
   15696             : 
   15697         894 :     heap_freetuple(newtuple);
   15698             : 
   15699         894 :     ReleaseSysCache(tuple);
   15700             : 
   15701             :     /* repeat the whole exercise for the toast table, if there's one */
   15702         894 :     if (OidIsValid(rel->rd_rel->reltoastrelid))
   15703             :     {
   15704             :         Relation    toastrel;
   15705         256 :         Oid         toastid = rel->rd_rel->reltoastrelid;
   15706             : 
   15707         256 :         toastrel = table_open(toastid, lockmode);
   15708             : 
   15709             :         /* Fetch heap tuple */
   15710         256 :         tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(toastid));
   15711         256 :         if (!HeapTupleIsValid(tuple))
   15712           0 :             elog(ERROR, "cache lookup failed for relation %u", toastid);
   15713             : 
   15714         256 :         if (operation == AT_ReplaceRelOptions)
   15715             :         {
   15716             :             /*
   15717             :              * If we're supposed to replace the reloptions list, we just
   15718             :              * pretend there were none before.
   15719             :              */
   15720           0 :             datum = (Datum) 0;
   15721           0 :             isnull = true;
   15722             :         }
   15723             :         else
   15724             :         {
   15725             :             /* Get the old reloptions */
   15726         256 :             datum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions,
   15727             :                                     &isnull);
   15728             :         }
   15729             : 
   15730         256 :         newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
   15731             :                                          defList, "toast", validnsps, false,
   15732             :                                          operation == AT_ResetRelOptions);
   15733             : 
   15734         256 :         (void) heap_reloptions(RELKIND_TOASTVALUE, newOptions, true);
   15735             : 
   15736         256 :         memset(repl_val, 0, sizeof(repl_val));
   15737         256 :         memset(repl_null, false, sizeof(repl_null));
   15738         256 :         memset(repl_repl, false, sizeof(repl_repl));
   15739             : 
   15740         256 :         if (newOptions != (Datum) 0)
   15741          42 :             repl_val[Anum_pg_class_reloptions - 1] = newOptions;
   15742             :         else
   15743         214 :             repl_null[Anum_pg_class_reloptions - 1] = true;
   15744             : 
   15745         256 :         repl_repl[Anum_pg_class_reloptions - 1] = true;
   15746             : 
   15747         256 :         newtuple = heap_modify_tuple(tuple, RelationGetDescr(pgclass),
   15748             :                                      repl_val, repl_null, repl_repl);
   15749             : 
   15750         256 :         CatalogTupleUpdate(pgclass, &newtuple->t_self, newtuple);
   15751             : 
   15752         256 :         InvokeObjectPostAlterHookArg(RelationRelationId,
   15753             :                                      RelationGetRelid(toastrel), 0,
   15754             :                                      InvalidOid, true);
   15755             : 
   15756         256 :         heap_freetuple(newtuple);
   15757             : 
   15758         256 :         ReleaseSysCache(tuple);
   15759             : 
   15760         256 :         table_close(toastrel, NoLock);
   15761             :     }
   15762             : 
   15763         894 :     table_close(pgclass, RowExclusiveLock);
   15764             : }
   15765             : 
   15766             : /*
   15767             :  * Execute ALTER TABLE SET TABLESPACE for cases where there is no tuple
   15768             :  * rewriting to be done, so we just want to copy the data as fast as possible.
   15769             :  */
   15770             : static void
   15771         162 : ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode)
   15772             : {
   15773             :     Relation    rel;
   15774             :     Oid         reltoastrelid;
   15775             :     RelFileNumber newrelfilenumber;
   15776             :     RelFileLocator newrlocator;
   15777         162 :     List       *reltoastidxids = NIL;
   15778             :     ListCell   *lc;
   15779             : 
   15780             :     /*
   15781             :      * Need lock here in case we are recursing to toast table or index
   15782             :      */
   15783         162 :     rel = relation_open(tableOid, lockmode);
   15784             : 
   15785             :     /* Check first if relation can be moved to new tablespace */
   15786         162 :     if (!CheckRelationTableSpaceMove(rel, newTableSpace))
   15787             :     {
   15788           2 :         InvokeObjectPostAlterHook(RelationRelationId,
   15789             :                                   RelationGetRelid(rel), 0);
   15790           2 :         relation_close(rel, NoLock);
   15791           2 :         return;
   15792             :     }
   15793             : 
   15794         160 :     reltoastrelid = rel->rd_rel->reltoastrelid;
   15795             :     /* Fetch the list of indexes on toast relation if necessary */
   15796         160 :     if (OidIsValid(reltoastrelid))
   15797             :     {
   15798          20 :         Relation    toastRel = relation_open(reltoastrelid, lockmode);
   15799             : 
   15800          20 :         reltoastidxids = RelationGetIndexList(toastRel);
   15801          20 :         relation_close(toastRel, lockmode);
   15802             :     }
   15803             : 
   15804             :     /*
   15805             :      * Relfilenumbers are not unique in databases across tablespaces, so we
   15806             :      * need to allocate a new one in the new tablespace.
   15807             :      */
   15808         160 :     newrelfilenumber = GetNewRelFileNumber(newTableSpace, NULL,
   15809         160 :                                            rel->rd_rel->relpersistence);
   15810             : 
   15811             :     /* Open old and new relation */
   15812         160 :     newrlocator = rel->rd_locator;
   15813         160 :     newrlocator.relNumber = newrelfilenumber;
   15814         160 :     newrlocator.spcOid = newTableSpace;
   15815             : 
   15816             :     /* hand off to AM to actually create new rel storage and copy the data */
   15817         160 :     if (rel->rd_rel->relkind == RELKIND_INDEX)
   15818             :     {
   15819          62 :         index_copy_data(rel, newrlocator);
   15820             :     }
   15821             :     else
   15822             :     {
   15823             :         Assert(RELKIND_HAS_TABLE_AM(rel->rd_rel->relkind));
   15824          98 :         table_relation_copy_data(rel, &newrlocator);
   15825             :     }
   15826             : 
   15827             :     /*
   15828             :      * Update the pg_class row.
   15829             :      *
   15830             :      * NB: This wouldn't work if ATExecSetTableSpace() were allowed to be
   15831             :      * executed on pg_class or its indexes (the above copy wouldn't contain
   15832             :      * the updated pg_class entry), but that's forbidden with
   15833             :      * CheckRelationTableSpaceMove().
   15834             :      */
   15835         160 :     SetRelationTableSpace(rel, newTableSpace, newrelfilenumber);
   15836             : 
   15837         160 :     InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
   15838             : 
   15839         160 :     RelationAssumeNewRelfilelocator(rel);
   15840             : 
   15841         160 :     relation_close(rel, NoLock);
   15842             : 
   15843             :     /* Make sure the reltablespace change is visible */
   15844         160 :     CommandCounterIncrement();
   15845             : 
   15846             :     /* Move associated toast relation and/or indexes, too */
   15847         160 :     if (OidIsValid(reltoastrelid))
   15848          20 :         ATExecSetTableSpace(reltoastrelid, newTableSpace, lockmode);
   15849         180 :     foreach(lc, reltoastidxids)
   15850          20 :         ATExecSetTableSpace(lfirst_oid(lc), newTableSpace, lockmode);
   15851             : 
   15852             :     /* Clean up */
   15853         160 :     list_free(reltoastidxids);
   15854             : }
   15855             : 
   15856             : /*
   15857             :  * Special handling of ALTER TABLE SET TABLESPACE for relations with no
   15858             :  * storage that have an interest in preserving tablespace.
   15859             :  *
   15860             :  * Since these have no storage the tablespace can be updated with a simple
   15861             :  * metadata only operation to update the tablespace.
   15862             :  */
   15863             : static void
   15864          36 : ATExecSetTableSpaceNoStorage(Relation rel, Oid newTableSpace)
   15865             : {
   15866             :     /*
   15867             :      * Shouldn't be called on relations having storage; these are processed in
   15868             :      * phase 3.
   15869             :      */
   15870             :     Assert(!RELKIND_HAS_STORAGE(rel->rd_rel->relkind));
   15871             : 
   15872             :     /* check if relation can be moved to its new tablespace */
   15873          36 :     if (!CheckRelationTableSpaceMove(rel, newTableSpace))
   15874             :     {
   15875           0 :         InvokeObjectPostAlterHook(RelationRelationId,
   15876             :                                   RelationGetRelid(rel),
   15877             :                                   0);
   15878           0 :         return;
   15879             :     }
   15880             : 
   15881             :     /* Update can be done, so change reltablespace */
   15882          30 :     SetRelationTableSpace(rel, newTableSpace, InvalidOid);
   15883             : 
   15884          30 :     InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
   15885             : 
   15886             :     /* Make sure the reltablespace change is visible */
   15887          30 :     CommandCounterIncrement();
   15888             : }
   15889             : 
   15890             : /*
   15891             :  * Alter Table ALL ... SET TABLESPACE
   15892             :  *
   15893             :  * Allows a user to move all objects of some type in a given tablespace in the
   15894             :  * current database to another tablespace.  Objects can be chosen based on the
   15895             :  * owner of the object also, to allow users to move only their objects.
   15896             :  * The user must have CREATE rights on the new tablespace, as usual.   The main
   15897             :  * permissions handling is done by the lower-level table move function.
   15898             :  *
   15899             :  * All to-be-moved objects are locked first. If NOWAIT is specified and the
   15900             :  * lock can't be acquired then we ereport(ERROR).
   15901             :  */
   15902             : Oid
   15903          30 : AlterTableMoveAll(AlterTableMoveAllStmt *stmt)
   15904             : {
   15905          30 :     List       *relations = NIL;
   15906             :     ListCell   *l;
   15907             :     ScanKeyData key[1];
   15908             :     Relation    rel;
   15909             :     TableScanDesc scan;
   15910             :     HeapTuple   tuple;
   15911             :     Oid         orig_tablespaceoid;
   15912             :     Oid         new_tablespaceoid;
   15913          30 :     List       *role_oids = roleSpecsToIds(stmt->roles);
   15914             : 
   15915             :     /* Ensure we were not asked to move something we can't */
   15916          30 :     if (stmt->objtype != OBJECT_TABLE && stmt->objtype != OBJECT_INDEX &&
   15917          12 :         stmt->objtype != OBJECT_MATVIEW)
   15918           0 :         ereport(ERROR,
   15919             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
   15920             :                  errmsg("only tables, indexes, and materialized views exist in tablespaces")));
   15921             : 
   15922             :     /* Get the orig and new tablespace OIDs */
   15923          30 :     orig_tablespaceoid = get_tablespace_oid(stmt->orig_tablespacename, false);
   15924          30 :     new_tablespaceoid = get_tablespace_oid(stmt->new_tablespacename, false);
   15925             : 
   15926             :     /* Can't move shared relations in to or out of pg_global */
   15927             :     /* This is also checked by ATExecSetTableSpace, but nice to stop earlier */
   15928          30 :     if (orig_tablespaceoid == GLOBALTABLESPACE_OID ||
   15929             :         new_tablespaceoid == GLOBALTABLESPACE_OID)
   15930           0 :         ereport(ERROR,
   15931             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
   15932             :                  errmsg("cannot move relations in to or out of pg_global tablespace")));
   15933             : 
   15934             :     /*
   15935             :      * Must have CREATE rights on the new tablespace, unless it is the
   15936             :      * database default tablespace (which all users implicitly have CREATE
   15937             :      * rights on).
   15938             :      */
   15939          30 :     if (OidIsValid(new_tablespaceoid) && new_tablespaceoid != MyDatabaseTableSpace)
   15940             :     {
   15941             :         AclResult   aclresult;
   15942             : 
   15943           0 :         aclresult = object_aclcheck(TableSpaceRelationId, new_tablespaceoid, GetUserId(),
   15944             :                                     ACL_CREATE);
   15945           0 :         if (aclresult != ACLCHECK_OK)
   15946           0 :             aclcheck_error(aclresult, OBJECT_TABLESPACE,
   15947           0 :                            get_tablespace_name(new_tablespaceoid));
   15948             :     }
   15949             : 
   15950             :     /*
   15951             :      * Now that the checks are done, check if we should set either to
   15952             :      * InvalidOid because it is our database's default tablespace.
   15953             :      */
   15954          30 :     if (orig_tablespaceoid == MyDatabaseTableSpace)
   15955           0 :         orig_tablespaceoid = InvalidOid;
   15956             : 
   15957          30 :     if (new_tablespaceoid == MyDatabaseTableSpace)
   15958          30 :         new_tablespaceoid = InvalidOid;
   15959             : 
   15960             :     /* no-op */
   15961          30 :     if (orig_tablespaceoid == new_tablespaceoid)
   15962           0 :         return new_tablespaceoid;
   15963             : 
   15964             :     /*
   15965             :      * Walk the list of objects in the tablespace and move them. This will
   15966             :      * only find objects in our database, of course.
   15967             :      */
   15968          30 :     ScanKeyInit(&key[0],
   15969             :                 Anum_pg_class_reltablespace,
   15970             :                 BTEqualStrategyNumber, F_OIDEQ,
   15971             :                 ObjectIdGetDatum(orig_tablespaceoid));
   15972             : 
   15973          30 :     rel = table_open(RelationRelationId, AccessShareLock);
   15974          30 :     scan = table_beginscan_catalog(rel, 1, key);
   15975         132 :     while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
   15976             :     {
   15977         102 :         Form_pg_class relForm = (Form_pg_class) GETSTRUCT(tuple);
   15978         102 :         Oid         relOid = relForm->oid;
   15979             : 
   15980             :         /*
   15981             :          * Do not move objects in pg_catalog as part of this, if an admin
   15982             :          * really wishes to do so, they can issue the individual ALTER
   15983             :          * commands directly.
   15984             :          *
   15985             :          * Also, explicitly avoid any shared tables, temp tables, or TOAST
   15986             :          * (TOAST will be moved with the main table).
   15987             :          */
   15988         102 :         if (IsCatalogNamespace(relForm->relnamespace) ||
   15989         204 :             relForm->relisshared ||
   15990         204 :             isAnyTempNamespace(relForm->relnamespace) ||
   15991         102 :             IsToastNamespace(relForm->relnamespace))
   15992           0 :             continue;
   15993             : 
   15994             :         /* Only move the object type requested */
   15995         102 :         if ((stmt->objtype == OBJECT_TABLE &&
   15996          60 :              relForm->relkind != RELKIND_RELATION &&
   15997          36 :              relForm->relkind != RELKIND_PARTITIONED_TABLE) ||
   15998          66 :             (stmt->objtype == OBJECT_INDEX &&
   15999          36 :              relForm->relkind != RELKIND_INDEX &&
   16000           6 :              relForm->relkind != RELKIND_PARTITIONED_INDEX) ||
   16001          60 :             (stmt->objtype == OBJECT_MATVIEW &&
   16002           6 :              relForm->relkind != RELKIND_MATVIEW))
   16003          42 :             continue;
   16004             : 
   16005             :         /* Check if we are only moving objects owned by certain roles */
   16006          60 :         if (role_oids != NIL && !list_member_oid(role_oids, relForm->relowner))
   16007           0 :             continue;
   16008             : 
   16009             :         /*
   16010             :          * Handle permissions-checking here since we are locking the tables
   16011             :          * and also to avoid doing a bunch of work only to fail part-way. Note
   16012             :          * that permissions will also be checked by AlterTableInternal().
   16013             :          *
   16014             :          * Caller must be considered an owner on the table to move it.
   16015             :          */
   16016          60 :         if (!object_ownercheck(RelationRelationId, relOid, GetUserId()))
   16017           0 :             aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relOid)),
   16018           0 :                            NameStr(relForm->relname));
   16019             : 
   16020          60 :         if (stmt->nowait &&
   16021           0 :             !ConditionalLockRelationOid(relOid, AccessExclusiveLock))
   16022           0 :             ereport(ERROR,
   16023             :                     (errcode(ERRCODE_OBJECT_IN_USE),
   16024             :                      errmsg("aborting because lock on relation \"%s.%s\" is not available",
   16025             :                             get_namespace_name(relForm->relnamespace),
   16026             :                             NameStr(relForm->relname))));
   16027             :         else
   16028          60 :             LockRelationOid(relOid, AccessExclusiveLock);
   16029             : 
   16030             :         /* Add to our list of objects to move */
   16031          60 :         relations = lappend_oid(relations, relOid);
   16032             :     }
   16033             : 
   16034          30 :     table_endscan(scan);
   16035          30 :     table_close(rel, AccessShareLock);
   16036             : 
   16037          30 :     if (relations == NIL)
   16038          12 :         ereport(NOTICE,
   16039             :                 (errcode(ERRCODE_NO_DATA_FOUND),
   16040             :                  errmsg("no matching relations in tablespace \"%s\" found",
   16041             :                         orig_tablespaceoid == InvalidOid ? "(database default)" :
   16042             :                         get_tablespace_name(orig_tablespaceoid))));
   16043             : 
   16044             :     /* Everything is locked, loop through and move all of the relations. */
   16045          90 :     foreach(l, relations)
   16046             :     {
   16047          60 :         List       *cmds = NIL;
   16048          60 :         AlterTableCmd *cmd = makeNode(AlterTableCmd);
   16049             : 
   16050          60 :         cmd->subtype = AT_SetTableSpace;
   16051          60 :         cmd->name = stmt->new_tablespacename;
   16052             : 
   16053          60 :         cmds = lappend(cmds, cmd);
   16054             : 
   16055          60 :         EventTriggerAlterTableStart((Node *) stmt);
   16056             :         /* OID is set by AlterTableInternal */
   16057          60 :         AlterTableInternal(lfirst_oid(l), cmds, false);
   16058          60 :         EventTriggerAlterTableEnd();
   16059             :     }
   16060             : 
   16061          30 :     return new_tablespaceoid;
   16062             : }
   16063             : 
   16064             : static void
   16065          62 : index_copy_data(Relation rel, RelFileLocator newrlocator)
   16066             : {
   16067             :     SMgrRelation dstrel;
   16068             : 
   16069             :     /*
   16070             :      * Since we copy the file directly without looking at the shared buffers,
   16071             :      * we'd better first flush out any pages of the source relation that are
   16072             :      * in shared buffers.  We assume no new changes will be made while we are
   16073             :      * holding exclusive lock on the rel.
   16074             :      */
   16075          62 :     FlushRelationBuffers(rel);
   16076             : 
   16077             :     /*
   16078             :      * Create and copy all forks of the relation, and schedule unlinking of
   16079             :      * old physical files.
   16080             :      *
   16081             :      * NOTE: any conflict in relfilenumber value will be caught in
   16082             :      * RelationCreateStorage().
   16083             :      */
   16084          62 :     dstrel = RelationCreateStorage(newrlocator, rel->rd_rel->relpersistence, true);
   16085             : 
   16086             :     /* copy main fork */
   16087          62 :     RelationCopyStorage(RelationGetSmgr(rel), dstrel, MAIN_FORKNUM,
   16088          62 :                         rel->rd_rel->relpersistence);
   16089             : 
   16090             :     /* copy those extra forks that exist */
   16091         248 :     for (ForkNumber forkNum = MAIN_FORKNUM + 1;
   16092         186 :          forkNum <= MAX_FORKNUM; forkNum++)
   16093             :     {
   16094         186 :         if (smgrexists(RelationGetSmgr(rel), forkNum))
   16095             :         {
   16096           0 :             smgrcreate(dstrel, forkNum, false);
   16097             : 
   16098             :             /*
   16099             :              * WAL log creation if the relation is persistent, or this is the
   16100             :              * init fork of an unlogged relation.
   16101             :              */
   16102           0 :             if (RelationIsPermanent(rel) ||
   16103           0 :                 (rel->rd_rel->relpersistence == RELPERSISTENCE_UNLOGGED &&
   16104             :                  forkNum == INIT_FORKNUM))
   16105           0 :                 log_smgrcreate(&newrlocator, forkNum);
   16106           0 :             RelationCopyStorage(RelationGetSmgr(rel), dstrel, forkNum,
   16107           0 :                                 rel->rd_rel->relpersistence);
   16108             :         }
   16109             :     }
   16110             : 
   16111             :     /* drop old relation, and close new one */
   16112          62 :     RelationDropStorage(rel);
   16113          62 :     smgrclose(dstrel);
   16114          62 : }
   16115             : 
   16116             : /*
   16117             :  * ALTER TABLE ENABLE/DISABLE TRIGGER
   16118             :  *
   16119             :  * We just pass this off to trigger.c.
   16120             :  */
   16121             : static void
   16122         340 : ATExecEnableDisableTrigger(Relation rel, const char *trigname,
   16123             :                            char fires_when, bool skip_system, bool recurse,
   16124             :                            LOCKMODE lockmode)
   16125             : {
   16126         340 :     EnableDisableTrigger(rel, trigname, InvalidOid,
   16127             :                          fires_when, skip_system, recurse,
   16128             :                          lockmode);
   16129             : 
   16130         340 :     InvokeObjectPostAlterHook(RelationRelationId,
   16131             :                               RelationGetRelid(rel), 0);
   16132         340 : }
   16133             : 
   16134             : /*
   16135             :  * ALTER TABLE ENABLE/DISABLE RULE
   16136             :  *
   16137             :  * We just pass this off to rewriteDefine.c.
   16138             :  */
   16139             : static void
   16140          46 : ATExecEnableDisableRule(Relation rel, const char *rulename,
   16141             :                         char fires_when, LOCKMODE lockmode)
   16142             : {
   16143          46 :     EnableDisableRule(rel, rulename, fires_when);
   16144             : 
   16145          46 :     InvokeObjectPostAlterHook(RelationRelationId,
   16146             :                               RelationGetRelid(rel), 0);
   16147          46 : }
   16148             : 
   16149             : /*
   16150             :  * ALTER TABLE INHERIT
   16151             :  *
   16152             :  * Add a parent to the child's parents. This verifies that all the columns and
   16153             :  * check constraints of the parent appear in the child and that they have the
   16154             :  * same data types and expressions.
   16155             :  */
   16156             : static void
   16157         346 : ATPrepAddInherit(Relation child_rel)
   16158             : {
   16159         346 :     if (child_rel->rd_rel->reloftype)
   16160           6 :         ereport(ERROR,
   16161             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   16162             :                  errmsg("cannot change inheritance of typed table")));
   16163             : 
   16164         340 :     if (child_rel->rd_rel->relispartition)
   16165           6 :         ereport(ERROR,
   16166             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   16167             :                  errmsg("cannot change inheritance of a partition")));
   16168             : 
   16169         334 :     if (child_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
   16170           6 :         ereport(ERROR,
   16171             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   16172             :                  errmsg("cannot change inheritance of partitioned table")));
   16173         328 : }
   16174             : 
   16175             : /*
   16176             :  * Return the address of the new parent relation.
   16177             :  */
   16178             : static ObjectAddress
   16179         328 : ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode)
   16180             : {
   16181             :     Relation    parent_rel;
   16182             :     List       *children;
   16183             :     ObjectAddress address;
   16184             :     const char *trigger_name;
   16185             : 
   16186             :     /*
   16187             :      * A self-exclusive lock is needed here.  See the similar case in
   16188             :      * MergeAttributes() for a full explanation.
   16189             :      */
   16190         328 :     parent_rel = table_openrv(parent, ShareUpdateExclusiveLock);
   16191             : 
   16192             :     /*
   16193             :      * Must be owner of both parent and child -- child was checked by
   16194             :      * ATSimplePermissions call in ATPrepCmd
   16195             :      */
   16196         328 :     ATSimplePermissions(AT_AddInherit, parent_rel,
   16197             :                         ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
   16198             : 
   16199             :     /* Permanent rels cannot inherit from temporary ones */
   16200         328 :     if (parent_rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
   16201           6 :         child_rel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
   16202           0 :         ereport(ERROR,
   16203             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   16204             :                  errmsg("cannot inherit from temporary relation \"%s\"",
   16205             :                         RelationGetRelationName(parent_rel))));
   16206             : 
   16207             :     /* If parent rel is temp, it must belong to this session */
   16208         328 :     if (parent_rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
   16209           6 :         !parent_rel->rd_islocaltemp)
   16210           0 :         ereport(ERROR,
   16211             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   16212             :                  errmsg("cannot inherit from temporary relation of another session")));
   16213             : 
   16214             :     /* Ditto for the child */
   16215         328 :     if (child_rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
   16216           6 :         !child_rel->rd_islocaltemp)
   16217           0 :         ereport(ERROR,
   16218             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   16219             :                  errmsg("cannot inherit to temporary relation of another session")));
   16220             : 
   16221             :     /* Prevent partitioned tables from becoming inheritance parents */
   16222         328 :     if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
   16223           6 :         ereport(ERROR,
   16224             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   16225             :                  errmsg("cannot inherit from partitioned table \"%s\"",
   16226             :                         parent->relname)));
   16227             : 
   16228             :     /* Likewise for partitions */
   16229         322 :     if (parent_rel->rd_rel->relispartition)
   16230           6 :         ereport(ERROR,
   16231             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   16232             :                  errmsg("cannot inherit from a partition")));
   16233             : 
   16234             :     /*
   16235             :      * Prevent circularity by seeing if proposed parent inherits from child.
   16236             :      * (In particular, this disallows making a rel inherit from itself.)
   16237             :      *
   16238             :      * This is not completely bulletproof because of race conditions: in
   16239             :      * multi-level inheritance trees, someone else could concurrently be
   16240             :      * making another inheritance link that closes the loop but does not join
   16241             :      * either of the rels we have locked.  Preventing that seems to require
   16242             :      * exclusive locks on the entire inheritance tree, which is a cure worse
   16243             :      * than the disease.  find_all_inheritors() will cope with circularity
   16244             :      * anyway, so don't sweat it too much.
   16245             :      *
   16246             :      * We use weakest lock we can on child's children, namely AccessShareLock.
   16247             :      */
   16248         316 :     children = find_all_inheritors(RelationGetRelid(child_rel),
   16249             :                                    AccessShareLock, NULL);
   16250             : 
   16251         316 :     if (list_member_oid(children, RelationGetRelid(parent_rel)))
   16252          12 :         ereport(ERROR,
   16253             :                 (errcode(ERRCODE_DUPLICATE_TABLE),
   16254             :                  errmsg("circular inheritance not allowed"),
   16255             :                  errdetail("\"%s\" is already a child of \"%s\".",
   16256             :                            parent->relname,
   16257             :                            RelationGetRelationName(child_rel))));
   16258             : 
   16259             :     /*
   16260             :      * If child_rel has row-level triggers with transition tables, we
   16261             :      * currently don't allow it to become an inheritance child.  See also
   16262             :      * prohibitions in ATExecAttachPartition() and CreateTrigger().
   16263             :      */
   16264         304 :     trigger_name = FindTriggerIncompatibleWithInheritance(child_rel->trigdesc);
   16265         304 :     if (trigger_name != NULL)
   16266           6 :         ereport(ERROR,
   16267             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   16268             :                  errmsg("trigger \"%s\" prevents table \"%s\" from becoming an inheritance child",
   16269             :                         trigger_name, RelationGetRelationName(child_rel)),
   16270             :                  errdetail("ROW triggers with transition tables are not supported in inheritance hierarchies.")));
   16271             : 
   16272             :     /* OK to create inheritance */
   16273         298 :     CreateInheritance(child_rel, parent_rel, false);
   16274             : 
   16275         232 :     ObjectAddressSet(address, RelationRelationId,
   16276             :                      RelationGetRelid(parent_rel));
   16277             : 
   16278             :     /* keep our lock on the parent relation until commit */
   16279         232 :     table_close(parent_rel, NoLock);
   16280             : 
   16281         232 :     return address;
   16282             : }
   16283             : 
   16284             : /*
   16285             :  * CreateInheritance
   16286             :  *      Catalog manipulation portion of creating inheritance between a child
   16287             :  *      table and a parent table.
   16288             :  *
   16289             :  * Common to ATExecAddInherit() and ATExecAttachPartition().
   16290             :  */
   16291             : static void
   16292        2352 : CreateInheritance(Relation child_rel, Relation parent_rel, bool ispartition)
   16293             : {
   16294             :     Relation    catalogRelation;
   16295             :     SysScanDesc scan;
   16296             :     ScanKeyData key;
   16297             :     HeapTuple   inheritsTuple;
   16298             :     int32       inhseqno;
   16299             : 
   16300             :     /* Note: get RowExclusiveLock because we will write pg_inherits below. */
   16301        2352 :     catalogRelation = table_open(InheritsRelationId, RowExclusiveLock);
   16302             : 
   16303             :     /*
   16304             :      * Check for duplicates in the list of parents, and determine the highest
   16305             :      * inhseqno already present; we'll use the next one for the new parent.
   16306             :      * Also, if proposed child is a partition, it cannot already be
   16307             :      * inheriting.
   16308             :      *
   16309             :      * Note: we do not reject the case where the child already inherits from
   16310             :      * the parent indirectly; CREATE TABLE doesn't reject comparable cases.
   16311             :      */
   16312        2352 :     ScanKeyInit(&key,
   16313             :                 Anum_pg_inherits_inhrelid,
   16314             :                 BTEqualStrategyNumber, F_OIDEQ,
   16315             :                 ObjectIdGetDatum(RelationGetRelid(child_rel)));
   16316        2352 :     scan = systable_beginscan(catalogRelation, InheritsRelidSeqnoIndexId,
   16317             :                               true, NULL, 1, &key);
   16318             : 
   16319             :     /* inhseqno sequences start at 1 */
   16320        2352 :     inhseqno = 0;
   16321        2398 :     while (HeapTupleIsValid(inheritsTuple = systable_getnext(scan)))
   16322             :     {
   16323          52 :         Form_pg_inherits inh = (Form_pg_inherits) GETSTRUCT(inheritsTuple);
   16324             : 
   16325          52 :         if (inh->inhparent == RelationGetRelid(parent_rel))
   16326           6 :             ereport(ERROR,
   16327             :                     (errcode(ERRCODE_DUPLICATE_TABLE),
   16328             :                      errmsg("relation \"%s\" would be inherited from more than once",
   16329             :                             RelationGetRelationName(parent_rel))));
   16330             : 
   16331          46 :         if (inh->inhseqno > inhseqno)
   16332          46 :             inhseqno = inh->inhseqno;
   16333             :     }
   16334        2346 :     systable_endscan(scan);
   16335             : 
   16336             :     /* Match up the columns and bump attinhcount as needed */
   16337        2346 :     MergeAttributesIntoExisting(child_rel, parent_rel, ispartition);
   16338             : 
   16339             :     /* Match up the constraints and bump coninhcount as needed */
   16340        2262 :     MergeConstraintsIntoExisting(child_rel, parent_rel);
   16341             : 
   16342             :     /*
   16343             :      * OK, it looks valid.  Make the catalog entries that show inheritance.
   16344             :      */
   16345        2214 :     StoreCatalogInheritance1(RelationGetRelid(child_rel),
   16346             :                              RelationGetRelid(parent_rel),
   16347             :                              inhseqno + 1,
   16348             :                              catalogRelation,
   16349        2214 :                              parent_rel->rd_rel->relkind ==
   16350             :                              RELKIND_PARTITIONED_TABLE);
   16351             : 
   16352             :     /* Now we're done with pg_inherits */
   16353        2214 :     table_close(catalogRelation, RowExclusiveLock);
   16354        2214 : }
   16355             : 
   16356             : /*
   16357             :  * Obtain the source-text form of the constraint expression for a check
   16358             :  * constraint, given its pg_constraint tuple
   16359             :  */
   16360             : static char *
   16361         184 : decompile_conbin(HeapTuple contup, TupleDesc tupdesc)
   16362             : {
   16363             :     Form_pg_constraint con;
   16364             :     bool        isnull;
   16365             :     Datum       attr;
   16366             :     Datum       expr;
   16367             : 
   16368         184 :     con = (Form_pg_constraint) GETSTRUCT(contup);
   16369         184 :     attr = heap_getattr(contup, Anum_pg_constraint_conbin, tupdesc, &isnull);
   16370         184 :     if (isnull)
   16371           0 :         elog(ERROR, "null conbin for constraint %u", con->oid);
   16372             : 
   16373         184 :     expr = DirectFunctionCall2(pg_get_expr, attr,
   16374             :                                ObjectIdGetDatum(con->conrelid));
   16375         184 :     return TextDatumGetCString(expr);
   16376             : }
   16377             : 
   16378             : /*
   16379             :  * Determine whether two check constraints are functionally equivalent
   16380             :  *
   16381             :  * The test we apply is to see whether they reverse-compile to the same
   16382             :  * source string.  This insulates us from issues like whether attributes
   16383             :  * have the same physical column numbers in parent and child relations.
   16384             :  *
   16385             :  * Note that we ignore enforceability as there are cases where constraints
   16386             :  * with differing enforceability are allowed.
   16387             :  */
   16388             : static bool
   16389          92 : constraints_equivalent(HeapTuple a, HeapTuple b, TupleDesc tupleDesc)
   16390             : {
   16391          92 :     Form_pg_constraint acon = (Form_pg_constraint) GETSTRUCT(a);
   16392          92 :     Form_pg_constraint bcon = (Form_pg_constraint) GETSTRUCT(b);
   16393             : 
   16394          92 :     if (acon->condeferrable != bcon->condeferrable ||
   16395          92 :         acon->condeferred != bcon->condeferred ||
   16396          92 :         strcmp(decompile_conbin(a, tupleDesc),
   16397          92 :                decompile_conbin(b, tupleDesc)) != 0)
   16398           6 :         return false;
   16399             :     else
   16400          86 :         return true;
   16401             : }
   16402             : 
   16403             : /*
   16404             :  * Check columns in child table match up with columns in parent, and increment
   16405             :  * their attinhcount.
   16406             :  *
   16407             :  * Called by CreateInheritance
   16408             :  *
   16409             :  * Currently all parent columns must be found in child. Missing columns are an
   16410             :  * error.  One day we might consider creating new columns like CREATE TABLE
   16411             :  * does.  However, that is widely unpopular --- in the common use case of
   16412             :  * partitioned tables it's a foot-gun.
   16413             :  *
   16414             :  * The data type must match exactly. If the parent column is NOT NULL then
   16415             :  * the child must be as well. Defaults are not compared, however.
   16416             :  */
   16417             : static void
   16418        2346 : MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel, bool ispartition)
   16419             : {
   16420             :     Relation    attrrel;
   16421             :     TupleDesc   parent_desc;
   16422             : 
   16423        2346 :     attrrel = table_open(AttributeRelationId, RowExclusiveLock);
   16424        2346 :     parent_desc = RelationGetDescr(parent_rel);
   16425             : 
   16426        7568 :     for (AttrNumber parent_attno = 1; parent_attno <= parent_desc->natts; parent_attno++)
   16427             :     {
   16428        5306 :         Form_pg_attribute parent_att = TupleDescAttr(parent_desc, parent_attno - 1);
   16429        5306 :         char       *parent_attname = NameStr(parent_att->attname);
   16430             :         HeapTuple   tuple;
   16431             : 
   16432             :         /* Ignore dropped columns in the parent. */
   16433        5306 :         if (parent_att->attisdropped)
   16434         296 :             continue;
   16435             : 
   16436             :         /* Find same column in child (matching on column name). */
   16437        5010 :         tuple = SearchSysCacheCopyAttName(RelationGetRelid(child_rel), parent_attname);
   16438        5010 :         if (HeapTupleIsValid(tuple))
   16439             :         {
   16440        4998 :             Form_pg_attribute child_att = (Form_pg_attribute) GETSTRUCT(tuple);
   16441             : 
   16442        4998 :             if (parent_att->atttypid != child_att->atttypid ||
   16443        4992 :                 parent_att->atttypmod != child_att->atttypmod)
   16444          12 :                 ereport(ERROR,
   16445             :                         (errcode(ERRCODE_DATATYPE_MISMATCH),
   16446             :                          errmsg("child table \"%s\" has different type for column \"%s\"",
   16447             :                                 RelationGetRelationName(child_rel), parent_attname)));
   16448             : 
   16449        4986 :             if (parent_att->attcollation != child_att->attcollation)
   16450           6 :                 ereport(ERROR,
   16451             :                         (errcode(ERRCODE_COLLATION_MISMATCH),
   16452             :                          errmsg("child table \"%s\" has different collation for column \"%s\"",
   16453             :                                 RelationGetRelationName(child_rel), parent_attname)));
   16454             : 
   16455             :             /*
   16456             :              * If the parent has a not-null constraint that's not NO INHERIT,
   16457             :              * make sure the child has one too.
   16458             :              *
   16459             :              * Other constraints are checked elsewhere.
   16460             :              */
   16461        4980 :             if (parent_att->attnotnull && !child_att->attnotnull)
   16462             :             {
   16463             :                 HeapTuple   contup;
   16464             : 
   16465          30 :                 contup = findNotNullConstraintAttnum(RelationGetRelid(parent_rel),
   16466          30 :                                                      parent_att->attnum);
   16467          30 :                 if (HeapTupleIsValid(contup) &&
   16468          30 :                     !((Form_pg_constraint) GETSTRUCT(contup))->connoinherit)
   16469          24 :                     ereport(ERROR,
   16470             :                             errcode(ERRCODE_DATATYPE_MISMATCH),
   16471             :                             errmsg("column \"%s\" in child table \"%s\" must be marked NOT NULL",
   16472             :                                    parent_attname, RelationGetRelationName(child_rel)));
   16473             :             }
   16474             : 
   16475             :             /*
   16476             :              * Child column must be generated if and only if parent column is.
   16477             :              */
   16478        4956 :             if (parent_att->attgenerated && !child_att->attgenerated)
   16479          18 :                 ereport(ERROR,
   16480             :                         (errcode(ERRCODE_DATATYPE_MISMATCH),
   16481             :                          errmsg("column \"%s\" in child table must be a generated column", parent_attname)));
   16482        4938 :             if (child_att->attgenerated && !parent_att->attgenerated)
   16483          12 :                 ereport(ERROR,
   16484             :                         (errcode(ERRCODE_DATATYPE_MISMATCH),
   16485             :                          errmsg("column \"%s\" in child table must not be a generated column", parent_attname)));
   16486             : 
   16487             :             /*
   16488             :              * Regular inheritance children are independent enough not to
   16489             :              * inherit identity columns.  But partitions are integral part of
   16490             :              * a partitioned table and inherit identity column.
   16491             :              */
   16492        4926 :             if (ispartition)
   16493        4386 :                 child_att->attidentity = parent_att->attidentity;
   16494             : 
   16495             :             /*
   16496             :              * OK, bump the child column's inheritance count.  (If we fail
   16497             :              * later on, this change will just roll back.)
   16498             :              */
   16499        4926 :             if (pg_add_s16_overflow(child_att->attinhcount, 1,
   16500             :                                     &child_att->attinhcount))
   16501           0 :                 ereport(ERROR,
   16502             :                         errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
   16503             :                         errmsg("too many inheritance parents"));
   16504             : 
   16505             :             /*
   16506             :              * In case of partitions, we must enforce that value of attislocal
   16507             :              * is same in all partitions. (Note: there are only inherited
   16508             :              * attributes in partitions)
   16509             :              */
   16510        4926 :             if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
   16511             :             {
   16512             :                 Assert(child_att->attinhcount == 1);
   16513        4386 :                 child_att->attislocal = false;
   16514             :             }
   16515             : 
   16516        4926 :             CatalogTupleUpdate(attrrel, &tuple->t_self, tuple);
   16517        4926 :             heap_freetuple(tuple);
   16518             :         }
   16519             :         else
   16520             :         {
   16521          12 :             ereport(ERROR,
   16522             :                     (errcode(ERRCODE_DATATYPE_MISMATCH),
   16523             :                      errmsg("child table is missing column \"%s\"", parent_attname)));
   16524             :         }
   16525             :     }
   16526             : 
   16527        2262 :     table_close(attrrel, RowExclusiveLock);
   16528        2262 : }
   16529             : 
   16530             : /*
   16531             :  * Check constraints in child table match up with constraints in parent,
   16532             :  * and increment their coninhcount.
   16533             :  *
   16534             :  * Constraints that are marked ONLY in the parent are ignored.
   16535             :  *
   16536             :  * Called by CreateInheritance
   16537             :  *
   16538             :  * Currently all constraints in parent must be present in the child. One day we
   16539             :  * may consider adding new constraints like CREATE TABLE does.
   16540             :  *
   16541             :  * XXX This is O(N^2) which may be an issue with tables with hundreds of
   16542             :  * constraints. As long as tables have more like 10 constraints it shouldn't be
   16543             :  * a problem though. Even 100 constraints ought not be the end of the world.
   16544             :  *
   16545             :  * XXX See MergeWithExistingConstraint too if you change this code.
   16546             :  */
   16547             : static void
   16548        2262 : MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel)
   16549             : {
   16550             :     Relation    constraintrel;
   16551             :     SysScanDesc parent_scan;
   16552             :     ScanKeyData parent_key;
   16553             :     HeapTuple   parent_tuple;
   16554        2262 :     Oid         parent_relid = RelationGetRelid(parent_rel);
   16555             :     AttrMap    *attmap;
   16556             : 
   16557        2262 :     constraintrel = table_open(ConstraintRelationId, RowExclusiveLock);
   16558             : 
   16559             :     /* Outer loop scans through the parent's constraint definitions */
   16560        2262 :     ScanKeyInit(&parent_key,
   16561             :                 Anum_pg_constraint_conrelid,
   16562             :                 BTEqualStrategyNumber, F_OIDEQ,
   16563             :                 ObjectIdGetDatum(parent_relid));
   16564        2262 :     parent_scan = systable_beginscan(constraintrel, ConstraintRelidTypidNameIndexId,
   16565             :                                      true, NULL, 1, &parent_key);
   16566             : 
   16567        2262 :     attmap = build_attrmap_by_name(RelationGetDescr(parent_rel),
   16568             :                                    RelationGetDescr(child_rel),
   16569             :                                    true);
   16570             : 
   16571        3928 :     while (HeapTupleIsValid(parent_tuple = systable_getnext(parent_scan)))
   16572             :     {
   16573        1714 :         Form_pg_constraint parent_con = (Form_pg_constraint) GETSTRUCT(parent_tuple);
   16574             :         SysScanDesc child_scan;
   16575             :         ScanKeyData child_key;
   16576             :         HeapTuple   child_tuple;
   16577             :         AttrNumber  parent_attno;
   16578        1714 :         bool        found = false;
   16579             : 
   16580        1714 :         if (parent_con->contype != CONSTRAINT_CHECK &&
   16581        1578 :             parent_con->contype != CONSTRAINT_NOTNULL)
   16582         814 :             continue;
   16583             : 
   16584             :         /* if the parent's constraint is marked NO INHERIT, it's not inherited */
   16585         932 :         if (parent_con->connoinherit)
   16586          32 :             continue;
   16587             : 
   16588         900 :         if (parent_con->contype == CONSTRAINT_NOTNULL)
   16589         784 :             parent_attno = extractNotNullColumn(parent_tuple);
   16590             :         else
   16591         116 :             parent_attno = InvalidAttrNumber;
   16592             : 
   16593             :         /* Search for a child constraint matching this one */
   16594         900 :         ScanKeyInit(&child_key,
   16595             :                     Anum_pg_constraint_conrelid,
   16596             :                     BTEqualStrategyNumber, F_OIDEQ,
   16597             :                     ObjectIdGetDatum(RelationGetRelid(child_rel)));
   16598         900 :         child_scan = systable_beginscan(constraintrel, ConstraintRelidTypidNameIndexId,
   16599             :                                         true, NULL, 1, &child_key);
   16600             : 
   16601        1546 :         while (HeapTupleIsValid(child_tuple = systable_getnext(child_scan)))
   16602             :         {
   16603        1522 :             Form_pg_constraint child_con = (Form_pg_constraint) GETSTRUCT(child_tuple);
   16604             :             HeapTuple   child_copy;
   16605             : 
   16606        1522 :             if (child_con->contype != parent_con->contype)
   16607         352 :                 continue;
   16608             : 
   16609             :             /*
   16610             :              * CHECK constraint are matched by constraint name, NOT NULL ones
   16611             :              * by attribute number.
   16612             :              */
   16613        1170 :             if (child_con->contype == CONSTRAINT_CHECK)
   16614             :             {
   16615         122 :                 if (strcmp(NameStr(parent_con->conname),
   16616         122 :                            NameStr(child_con->conname)) != 0)
   16617          30 :                     continue;
   16618             :             }
   16619        1048 :             else if (child_con->contype == CONSTRAINT_NOTNULL)
   16620             :             {
   16621             :                 Form_pg_attribute parent_attr;
   16622             :                 Form_pg_attribute child_attr;
   16623             :                 AttrNumber  child_attno;
   16624             : 
   16625        1048 :                 parent_attr = TupleDescAttr(parent_rel->rd_att, parent_attno - 1);
   16626        1048 :                 child_attno = extractNotNullColumn(child_tuple);
   16627        1048 :                 if (parent_attno != attmap->attnums[child_attno - 1])
   16628         264 :                     continue;
   16629             : 
   16630         784 :                 child_attr = TupleDescAttr(child_rel->rd_att, child_attno - 1);
   16631             :                 /* there shouldn't be constraints on dropped columns */
   16632         784 :                 if (parent_attr->attisdropped || child_attr->attisdropped)
   16633           0 :                     elog(ERROR, "found not-null constraint on dropped columns");
   16634             :             }
   16635             : 
   16636         876 :             if (child_con->contype == CONSTRAINT_CHECK &&
   16637          92 :                 !constraints_equivalent(parent_tuple, child_tuple, RelationGetDescr(constraintrel)))
   16638           6 :                 ereport(ERROR,
   16639             :                         (errcode(ERRCODE_DATATYPE_MISMATCH),
   16640             :                          errmsg("child table \"%s\" has different definition for check constraint \"%s\"",
   16641             :                                 RelationGetRelationName(child_rel), NameStr(parent_con->conname))));
   16642             : 
   16643             :             /*
   16644             :              * If the child constraint is "no inherit" then cannot merge
   16645             :              */
   16646         870 :             if (child_con->connoinherit)
   16647          12 :                 ereport(ERROR,
   16648             :                         (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
   16649             :                          errmsg("constraint \"%s\" conflicts with non-inherited constraint on child table \"%s\"",
   16650             :                                 NameStr(child_con->conname), RelationGetRelationName(child_rel))));
   16651             : 
   16652             :             /*
   16653             :              * If the child constraint is "not valid" then cannot merge with a
   16654             :              * valid parent constraint
   16655             :              */
   16656         858 :             if (parent_con->convalidated && child_con->conenforced &&
   16657         846 :                 !child_con->convalidated)
   16658           0 :                 ereport(ERROR,
   16659             :                         (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
   16660             :                          errmsg("constraint \"%s\" conflicts with NOT VALID constraint on child table \"%s\"",
   16661             :                                 NameStr(child_con->conname), RelationGetRelationName(child_rel))));
   16662             : 
   16663             :             /*
   16664             :              * A non-enforced child constraint cannot be merged with an
   16665             :              * enforced parent constraint. However, the reverse is allowed,
   16666             :              * where the child constraint is enforced.
   16667             :              */
   16668         858 :             if (parent_con->conenforced && !child_con->conenforced)
   16669           6 :                 ereport(ERROR,
   16670             :                         (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
   16671             :                          errmsg("constraint \"%s\" conflicts with NOT ENFORCED constraint on child table \"%s\"",
   16672             :                                 NameStr(child_con->conname), RelationGetRelationName(child_rel))));
   16673             : 
   16674             :             /*
   16675             :              * OK, bump the child constraint's inheritance count.  (If we fail
   16676             :              * later on, this change will just roll back.)
   16677             :              */
   16678         852 :             child_copy = heap_copytuple(child_tuple);
   16679         852 :             child_con = (Form_pg_constraint) GETSTRUCT(child_copy);
   16680             : 
   16681         852 :             if (pg_add_s16_overflow(child_con->coninhcount, 1,
   16682             :                                     &child_con->coninhcount))
   16683           0 :                 ereport(ERROR,
   16684             :                         errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
   16685             :                         errmsg("too many inheritance parents"));
   16686             : 
   16687             :             /*
   16688             :              * In case of partitions, an inherited constraint must be
   16689             :              * inherited only once since it cannot have multiple parents and
   16690             :              * it is never considered local.
   16691             :              */
   16692         852 :             if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
   16693             :             {
   16694             :                 Assert(child_con->coninhcount == 1);
   16695         740 :                 child_con->conislocal = false;
   16696             :             }
   16697             : 
   16698         852 :             CatalogTupleUpdate(constraintrel, &child_copy->t_self, child_copy);
   16699         852 :             heap_freetuple(child_copy);
   16700             : 
   16701         852 :             found = true;
   16702         852 :             break;
   16703             :         }
   16704             : 
   16705         876 :         systable_endscan(child_scan);
   16706             : 
   16707         876 :         if (!found)
   16708             :         {
   16709          24 :             if (parent_con->contype == CONSTRAINT_NOTNULL)
   16710           0 :                 ereport(ERROR,
   16711             :                         errcode(ERRCODE_DATATYPE_MISMATCH),
   16712             :                         errmsg("column \"%s\" in child table \"%s\" must be marked NOT NULL",
   16713             :                                get_attname(parent_relid,
   16714             :                                            extractNotNullColumn(parent_tuple),
   16715             :                                            false),
   16716             :                                RelationGetRelationName(child_rel)));
   16717             : 
   16718          24 :             ereport(ERROR,
   16719             :                     (errcode(ERRCODE_DATATYPE_MISMATCH),
   16720             :                      errmsg("child table is missing constraint \"%s\"",
   16721             :                             NameStr(parent_con->conname))));
   16722             :         }
   16723             :     }
   16724             : 
   16725        2214 :     systable_endscan(parent_scan);
   16726        2214 :     table_close(constraintrel, RowExclusiveLock);
   16727        2214 : }
   16728             : 
   16729             : /*
   16730             :  * ALTER TABLE NO INHERIT
   16731             :  *
   16732             :  * Return value is the address of the relation that is no longer parent.
   16733             :  */
   16734             : static ObjectAddress
   16735          86 : ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode)
   16736             : {
   16737             :     ObjectAddress address;
   16738             :     Relation    parent_rel;
   16739             : 
   16740          86 :     if (rel->rd_rel->relispartition)
   16741           0 :         ereport(ERROR,
   16742             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   16743             :                  errmsg("cannot change inheritance of a partition")));
   16744             : 
   16745             :     /*
   16746             :      * AccessShareLock on the parent is probably enough, seeing that DROP
   16747             :      * TABLE doesn't lock parent tables at all.  We need some lock since we'll
   16748             :      * be inspecting the parent's schema.
   16749             :      */
   16750          86 :     parent_rel = table_openrv(parent, AccessShareLock);
   16751             : 
   16752             :     /*
   16753             :      * We don't bother to check ownership of the parent table --- ownership of
   16754             :      * the child is presumed enough rights.
   16755             :      */
   16756             : 
   16757             :     /* Off to RemoveInheritance() where most of the work happens */
   16758          86 :     RemoveInheritance(rel, parent_rel, false);
   16759             : 
   16760          80 :     ObjectAddressSet(address, RelationRelationId,
   16761             :                      RelationGetRelid(parent_rel));
   16762             : 
   16763             :     /* keep our lock on the parent relation until commit */
   16764          80 :     table_close(parent_rel, NoLock);
   16765             : 
   16766          80 :     return address;
   16767             : }
   16768             : 
   16769             : /*
   16770             :  * MarkInheritDetached
   16771             :  *
   16772             :  * Set inhdetachpending for a partition, for ATExecDetachPartition
   16773             :  * in concurrent mode.  While at it, verify that no other partition is
   16774             :  * already pending detach.
   16775             :  */
   16776             : static void
   16777         146 : MarkInheritDetached(Relation child_rel, Relation parent_rel)
   16778             : {
   16779             :     Relation    catalogRelation;
   16780             :     SysScanDesc scan;
   16781             :     ScanKeyData key;
   16782             :     HeapTuple   inheritsTuple;
   16783         146 :     bool        found = false;
   16784             : 
   16785             :     Assert(parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
   16786             : 
   16787             :     /*
   16788             :      * Find pg_inherits entries by inhparent.  (We need to scan them all in
   16789             :      * order to verify that no other partition is pending detach.)
   16790             :      */
   16791         146 :     catalogRelation = table_open(InheritsRelationId, RowExclusiveLock);
   16792         146 :     ScanKeyInit(&key,
   16793             :                 Anum_pg_inherits_inhparent,
   16794             :                 BTEqualStrategyNumber, F_OIDEQ,
   16795             :                 ObjectIdGetDatum(RelationGetRelid(parent_rel)));
   16796         146 :     scan = systable_beginscan(catalogRelation, InheritsParentIndexId,
   16797             :                               true, NULL, 1, &key);
   16798             : 
   16799         430 :     while (HeapTupleIsValid(inheritsTuple = systable_getnext(scan)))
   16800             :     {
   16801             :         Form_pg_inherits inhForm;
   16802             : 
   16803         286 :         inhForm = (Form_pg_inherits) GETSTRUCT(inheritsTuple);
   16804         286 :         if (inhForm->inhdetachpending)
   16805           2 :             ereport(ERROR,
   16806             :                     errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
   16807             :                     errmsg("partition \"%s\" already pending detach in partitioned table \"%s.%s\"",
   16808             :                            get_rel_name(inhForm->inhrelid),
   16809             :                            get_namespace_name(parent_rel->rd_rel->relnamespace),
   16810             :                            RelationGetRelationName(parent_rel)),
   16811             :                     errhint("Use ALTER TABLE ... DETACH PARTITION ... FINALIZE to complete the pending detach operation."));
   16812             : 
   16813         284 :         if (inhForm->inhrelid == RelationGetRelid(child_rel))
   16814             :         {
   16815             :             HeapTuple   newtup;
   16816             : 
   16817         144 :             newtup = heap_copytuple(inheritsTuple);
   16818         144 :             ((Form_pg_inherits) GETSTRUCT(newtup))->inhdetachpending = true;
   16819             : 
   16820         144 :             CatalogTupleUpdate(catalogRelation,
   16821             :                                &inheritsTuple->t_self,
   16822             :                                newtup);
   16823         144 :             found = true;
   16824         144 :             heap_freetuple(newtup);
   16825             :             /* keep looking, to ensure we catch others pending detach */
   16826             :         }
   16827             :     }
   16828             : 
   16829             :     /* Done */
   16830         144 :     systable_endscan(scan);
   16831         144 :     table_close(catalogRelation, RowExclusiveLock);
   16832             : 
   16833         144 :     if (!found)
   16834           0 :         ereport(ERROR,
   16835             :                 (errcode(ERRCODE_UNDEFINED_TABLE),
   16836             :                  errmsg("relation \"%s\" is not a partition of relation \"%s\"",
   16837             :                         RelationGetRelationName(child_rel),
   16838             :                         RelationGetRelationName(parent_rel))));
   16839         144 : }
   16840             : 
   16841             : /*
   16842             :  * RemoveInheritance
   16843             :  *
   16844             :  * Drop a parent from the child's parents. This just adjusts the attinhcount
   16845             :  * and attislocal of the columns and removes the pg_inherit and pg_depend
   16846             :  * entries.  expect_detached is passed down to DeleteInheritsTuple, q.v..
   16847             :  *
   16848             :  * If attinhcount goes to 0 then attislocal gets set to true. If it goes back
   16849             :  * up attislocal stays true, which means if a child is ever removed from a
   16850             :  * parent then its columns will never be automatically dropped which may
   16851             :  * surprise. But at least we'll never surprise by dropping columns someone
   16852             :  * isn't expecting to be dropped which would actually mean data loss.
   16853             :  *
   16854             :  * coninhcount and conislocal for inherited constraints are adjusted in
   16855             :  * exactly the same way.
   16856             :  *
   16857             :  * Common to ATExecDropInherit() and ATExecDetachPartition().
   16858             :  */
   16859             : static void
   16860         566 : RemoveInheritance(Relation child_rel, Relation parent_rel, bool expect_detached)
   16861             : {
   16862             :     Relation    catalogRelation;
   16863             :     SysScanDesc scan;
   16864             :     ScanKeyData key[3];
   16865             :     HeapTuple   attributeTuple,
   16866             :                 constraintTuple;
   16867             :     AttrMap    *attmap;
   16868             :     List       *connames;
   16869             :     List       *nncolumns;
   16870             :     bool        found;
   16871             :     bool        is_partitioning;
   16872             : 
   16873         566 :     is_partitioning = (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
   16874             : 
   16875         566 :     found = DeleteInheritsTuple(RelationGetRelid(child_rel),
   16876             :                                 RelationGetRelid(parent_rel),
   16877             :                                 expect_detached,
   16878         566 :                                 RelationGetRelationName(child_rel));
   16879         566 :     if (!found)
   16880             :     {
   16881          24 :         if (is_partitioning)
   16882          18 :             ereport(ERROR,
   16883             :                     (errcode(ERRCODE_UNDEFINED_TABLE),
   16884             :                      errmsg("relation \"%s\" is not a partition of relation \"%s\"",
   16885             :                             RelationGetRelationName(child_rel),
   16886             :                             RelationGetRelationName(parent_rel))));
   16887             :         else
   16888           6 :             ereport(ERROR,
   16889             :                     (errcode(ERRCODE_UNDEFINED_TABLE),
   16890             :                      errmsg("relation \"%s\" is not a parent of relation \"%s\"",
   16891             :                             RelationGetRelationName(parent_rel),
   16892             :                             RelationGetRelationName(child_rel))));
   16893             :     }
   16894             : 
   16895             :     /*
   16896             :      * Search through child columns looking for ones matching parent rel
   16897             :      */
   16898         542 :     catalogRelation = table_open(AttributeRelationId, RowExclusiveLock);
   16899         542 :     ScanKeyInit(&key[0],
   16900             :                 Anum_pg_attribute_attrelid,
   16901             :                 BTEqualStrategyNumber, F_OIDEQ,
   16902             :                 ObjectIdGetDatum(RelationGetRelid(child_rel)));
   16903         542 :     scan = systable_beginscan(catalogRelation, AttributeRelidNumIndexId,
   16904             :                               true, NULL, 1, key);
   16905        4844 :     while (HeapTupleIsValid(attributeTuple = systable_getnext(scan)))
   16906             :     {
   16907        4302 :         Form_pg_attribute att = (Form_pg_attribute) GETSTRUCT(attributeTuple);
   16908             : 
   16909             :         /* Ignore if dropped or not inherited */
   16910        4302 :         if (att->attisdropped)
   16911           6 :             continue;
   16912        4296 :         if (att->attinhcount <= 0)
   16913        3282 :             continue;
   16914             : 
   16915        1014 :         if (SearchSysCacheExistsAttName(RelationGetRelid(parent_rel),
   16916        1014 :                                         NameStr(att->attname)))
   16917             :         {
   16918             :             /* Decrement inhcount and possibly set islocal to true */
   16919         960 :             HeapTuple   copyTuple = heap_copytuple(attributeTuple);
   16920         960 :             Form_pg_attribute copy_att = (Form_pg_attribute) GETSTRUCT(copyTuple);
   16921             : 
   16922         960 :             copy_att->attinhcount--;
   16923         960 :             if (copy_att->attinhcount == 0)
   16924         930 :                 copy_att->attislocal = true;
   16925             : 
   16926         960 :             CatalogTupleUpdate(catalogRelation, &copyTuple->t_self, copyTuple);
   16927         960 :             heap_freetuple(copyTuple);
   16928             :         }
   16929             :     }
   16930         542 :     systable_endscan(scan);
   16931         542 :     table_close(catalogRelation, RowExclusiveLock);
   16932             : 
   16933             :     /*
   16934             :      * Likewise, find inherited check and not-null constraints and disinherit
   16935             :      * them. To do this, we first need a list of the names of the parent's
   16936             :      * check constraints.  (We cheat a bit by only checking for name matches,
   16937             :      * assuming that the expressions will match.)
   16938             :      *
   16939             :      * For NOT NULL columns, we store column numbers to match, mapping them in
   16940             :      * to the child rel's attribute numbers.
   16941             :      */
   16942         542 :     attmap = build_attrmap_by_name(RelationGetDescr(child_rel),
   16943             :                                    RelationGetDescr(parent_rel),
   16944             :                                    false);
   16945             : 
   16946         542 :     catalogRelation = table_open(ConstraintRelationId, RowExclusiveLock);
   16947         542 :     ScanKeyInit(&key[0],
   16948             :                 Anum_pg_constraint_conrelid,
   16949             :                 BTEqualStrategyNumber, F_OIDEQ,
   16950             :                 ObjectIdGetDatum(RelationGetRelid(parent_rel)));
   16951         542 :     scan = systable_beginscan(catalogRelation, ConstraintRelidTypidNameIndexId,
   16952             :                               true, NULL, 1, key);
   16953             : 
   16954         542 :     connames = NIL;
   16955         542 :     nncolumns = NIL;
   16956             : 
   16957        1010 :     while (HeapTupleIsValid(constraintTuple = systable_getnext(scan)))
   16958             :     {
   16959         468 :         Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(constraintTuple);
   16960             : 
   16961         468 :         if (con->connoinherit)
   16962          98 :             continue;
   16963             : 
   16964         370 :         if (con->contype == CONSTRAINT_CHECK)
   16965          12 :             connames = lappend(connames, pstrdup(NameStr(con->conname)));
   16966         370 :         if (con->contype == CONSTRAINT_NOTNULL)
   16967             :         {
   16968         196 :             AttrNumber  parent_attno = extractNotNullColumn(constraintTuple);
   16969             : 
   16970         196 :             nncolumns = lappend_int(nncolumns, attmap->attnums[parent_attno - 1]);
   16971             :         }
   16972             :     }
   16973             : 
   16974         542 :     systable_endscan(scan);
   16975             : 
   16976             :     /* Now scan the child's constraints to find matches */
   16977         542 :     ScanKeyInit(&key[0],
   16978             :                 Anum_pg_constraint_conrelid,
   16979             :                 BTEqualStrategyNumber, F_OIDEQ,
   16980             :                 ObjectIdGetDatum(RelationGetRelid(child_rel)));
   16981         542 :     scan = systable_beginscan(catalogRelation, ConstraintRelidTypidNameIndexId,
   16982             :                               true, NULL, 1, key);
   16983             : 
   16984        1194 :     while (HeapTupleIsValid(constraintTuple = systable_getnext(scan)))
   16985             :     {
   16986         652 :         Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(constraintTuple);
   16987         652 :         bool        match = false;
   16988             : 
   16989             :         /*
   16990             :          * Match CHECK constraints by name, not-null constraints by column
   16991             :          * number, and ignore all others.
   16992             :          */
   16993         652 :         if (con->contype == CONSTRAINT_CHECK)
   16994             :         {
   16995         350 :             foreach_ptr(char, chkname, connames)
   16996             :             {
   16997          18 :                 if (con->contype == CONSTRAINT_CHECK &&
   16998          18 :                     strcmp(NameStr(con->conname), chkname) == 0)
   16999             :                 {
   17000          12 :                     match = true;
   17001          12 :                     connames = foreach_delete_current(connames, chkname);
   17002          12 :                     break;
   17003             :                 }
   17004             :             }
   17005             :         }
   17006         480 :         else if (con->contype == CONSTRAINT_NOTNULL)
   17007             :         {
   17008         256 :             AttrNumber  child_attno = extractNotNullColumn(constraintTuple);
   17009             : 
   17010         518 :             foreach_int(prevattno, nncolumns)
   17011             :             {
   17012         202 :                 if (prevattno == child_attno)
   17013             :                 {
   17014         196 :                     match = true;
   17015         196 :                     nncolumns = foreach_delete_current(nncolumns, prevattno);
   17016         196 :                     break;
   17017             :                 }
   17018             :             }
   17019             :         }
   17020             :         else
   17021         224 :             continue;
   17022             : 
   17023         428 :         if (match)
   17024             :         {
   17025             :             /* Decrement inhcount and possibly set islocal to true */
   17026         208 :             HeapTuple   copyTuple = heap_copytuple(constraintTuple);
   17027         208 :             Form_pg_constraint copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
   17028             : 
   17029         208 :             if (copy_con->coninhcount <= 0) /* shouldn't happen */
   17030           0 :                 elog(ERROR, "relation %u has non-inherited constraint \"%s\"",
   17031             :                      RelationGetRelid(child_rel), NameStr(copy_con->conname));
   17032             : 
   17033         208 :             copy_con->coninhcount--;
   17034         208 :             if (copy_con->coninhcount == 0)
   17035         190 :                 copy_con->conislocal = true;
   17036             : 
   17037         208 :             CatalogTupleUpdate(catalogRelation, &copyTuple->t_self, copyTuple);
   17038         208 :             heap_freetuple(copyTuple);
   17039             :         }
   17040             :     }
   17041             : 
   17042             :     /* We should have matched all constraints */
   17043         542 :     if (connames != NIL || nncolumns != NIL)
   17044           0 :         elog(ERROR, "%d unmatched constraints while removing inheritance from \"%s\" to \"%s\"",
   17045             :              list_length(connames) + list_length(nncolumns),
   17046             :              RelationGetRelationName(child_rel), RelationGetRelationName(parent_rel));
   17047             : 
   17048         542 :     systable_endscan(scan);
   17049         542 :     table_close(catalogRelation, RowExclusiveLock);
   17050             : 
   17051         542 :     drop_parent_dependency(RelationGetRelid(child_rel),
   17052             :                            RelationRelationId,
   17053             :                            RelationGetRelid(parent_rel),
   17054             :                            child_dependency_type(is_partitioning));
   17055             : 
   17056             :     /*
   17057             :      * Post alter hook of this inherits. Since object_access_hook doesn't take
   17058             :      * multiple object identifiers, we relay oid of parent relation using
   17059             :      * auxiliary_id argument.
   17060             :      */
   17061         542 :     InvokeObjectPostAlterHookArg(InheritsRelationId,
   17062             :                                  RelationGetRelid(child_rel), 0,
   17063             :                                  RelationGetRelid(parent_rel), false);
   17064         542 : }
   17065             : 
   17066             : /*
   17067             :  * Drop the dependency created by StoreCatalogInheritance1 (CREATE TABLE
   17068             :  * INHERITS/ALTER TABLE INHERIT -- refclassid will be RelationRelationId) or
   17069             :  * heap_create_with_catalog (CREATE TABLE OF/ALTER TABLE OF -- refclassid will
   17070             :  * be TypeRelationId).  There's no convenient way to do this, so go trawling
   17071             :  * through pg_depend.
   17072             :  */
   17073             : static void
   17074         554 : drop_parent_dependency(Oid relid, Oid refclassid, Oid refobjid,
   17075             :                        DependencyType deptype)
   17076             : {
   17077             :     Relation    catalogRelation;
   17078             :     SysScanDesc scan;
   17079             :     ScanKeyData key[3];
   17080             :     HeapTuple   depTuple;
   17081             : 
   17082         554 :     catalogRelation = table_open(DependRelationId, RowExclusiveLock);
   17083             : 
   17084         554 :     ScanKeyInit(&key[0],
   17085             :                 Anum_pg_depend_classid,
   17086             :                 BTEqualStrategyNumber, F_OIDEQ,
   17087             :                 ObjectIdGetDatum(RelationRelationId));
   17088         554 :     ScanKeyInit(&key[1],
   17089             :                 Anum_pg_depend_objid,
   17090             :                 BTEqualStrategyNumber, F_OIDEQ,
   17091             :                 ObjectIdGetDatum(relid));
   17092         554 :     ScanKeyInit(&key[2],
   17093             :                 Anum_pg_depend_objsubid,
   17094             :                 BTEqualStrategyNumber, F_INT4EQ,
   17095             :                 Int32GetDatum(0));
   17096             : 
   17097         554 :     scan = systable_beginscan(catalogRelation, DependDependerIndexId, true,
   17098             :                               NULL, 3, key);
   17099             : 
   17100        1722 :     while (HeapTupleIsValid(depTuple = systable_getnext(scan)))
   17101             :     {
   17102        1168 :         Form_pg_depend dep = (Form_pg_depend) GETSTRUCT(depTuple);
   17103             : 
   17104        1168 :         if (dep->refclassid == refclassid &&
   17105         596 :             dep->refobjid == refobjid &&
   17106         554 :             dep->refobjsubid == 0 &&
   17107         554 :             dep->deptype == deptype)
   17108         554 :             CatalogTupleDelete(catalogRelation, &depTuple->t_self);
   17109             :     }
   17110             : 
   17111         554 :     systable_endscan(scan);
   17112         554 :     table_close(catalogRelation, RowExclusiveLock);
   17113         554 : }
   17114             : 
   17115             : /*
   17116             :  * ALTER TABLE OF
   17117             :  *
   17118             :  * Attach a table to a composite type, as though it had been created with CREATE
   17119             :  * TABLE OF.  All attname, atttypid, atttypmod and attcollation must match.  The
   17120             :  * subject table must not have inheritance parents.  These restrictions ensure
   17121             :  * that you cannot create a configuration impossible with CREATE TABLE OF alone.
   17122             :  *
   17123             :  * The address of the type is returned.
   17124             :  */
   17125             : static ObjectAddress
   17126          66 : ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode)
   17127             : {
   17128          66 :     Oid         relid = RelationGetRelid(rel);
   17129             :     Type        typetuple;
   17130             :     Form_pg_type typeform;
   17131             :     Oid         typeid;
   17132             :     Relation    inheritsRelation,
   17133             :                 relationRelation;
   17134             :     SysScanDesc scan;
   17135             :     ScanKeyData key;
   17136             :     AttrNumber  table_attno,
   17137             :                 type_attno;
   17138             :     TupleDesc   typeTupleDesc,
   17139             :                 tableTupleDesc;
   17140             :     ObjectAddress tableobj,
   17141             :                 typeobj;
   17142             :     HeapTuple   classtuple;
   17143             : 
   17144             :     /* Validate the type. */
   17145          66 :     typetuple = typenameType(NULL, ofTypename, NULL);
   17146          66 :     check_of_type(typetuple);
   17147          66 :     typeform = (Form_pg_type) GETSTRUCT(typetuple);
   17148          66 :     typeid = typeform->oid;
   17149             : 
   17150             :     /* Fail if the table has any inheritance parents. */
   17151          66 :     inheritsRelation = table_open(InheritsRelationId, AccessShareLock);
   17152          66 :     ScanKeyInit(&key,
   17153             :                 Anum_pg_inherits_inhrelid,
   17154             :                 BTEqualStrategyNumber, F_OIDEQ,
   17155             :                 ObjectIdGetDatum(relid));
   17156          66 :     scan = systable_beginscan(inheritsRelation, InheritsRelidSeqnoIndexId,
   17157             :                               true, NULL, 1, &key);
   17158          66 :     if (HeapTupleIsValid(systable_getnext(scan)))
   17159           6 :         ereport(ERROR,
   17160             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   17161             :                  errmsg("typed tables cannot inherit")));
   17162          60 :     systable_endscan(scan);
   17163          60 :     table_close(inheritsRelation, AccessShareLock);
   17164             : 
   17165             :     /*
   17166             :      * Check the tuple descriptors for compatibility.  Unlike inheritance, we
   17167             :      * require that the order also match.  However, attnotnull need not match.
   17168             :      */
   17169          60 :     typeTupleDesc = lookup_rowtype_tupdesc(typeid, -1);
   17170          60 :     tableTupleDesc = RelationGetDescr(rel);
   17171          60 :     table_attno = 1;
   17172         190 :     for (type_attno = 1; type_attno <= typeTupleDesc->natts; type_attno++)
   17173             :     {
   17174             :         Form_pg_attribute type_attr,
   17175             :                     table_attr;
   17176             :         const char *type_attname,
   17177             :                    *table_attname;
   17178             : 
   17179             :         /* Get the next non-dropped type attribute. */
   17180         154 :         type_attr = TupleDescAttr(typeTupleDesc, type_attno - 1);
   17181         154 :         if (type_attr->attisdropped)
   17182          44 :             continue;
   17183         110 :         type_attname = NameStr(type_attr->attname);
   17184             : 
   17185             :         /* Get the next non-dropped table attribute. */
   17186             :         do
   17187             :         {
   17188         122 :             if (table_attno > tableTupleDesc->natts)
   17189           6 :                 ereport(ERROR,
   17190             :                         (errcode(ERRCODE_DATATYPE_MISMATCH),
   17191             :                          errmsg("table is missing column \"%s\"",
   17192             :                                 type_attname)));
   17193         116 :             table_attr = TupleDescAttr(tableTupleDesc, table_attno - 1);
   17194         116 :             table_attno++;
   17195         116 :         } while (table_attr->attisdropped);
   17196         104 :         table_attname = NameStr(table_attr->attname);
   17197             : 
   17198             :         /* Compare name. */
   17199         104 :         if (strncmp(table_attname, type_attname, NAMEDATALEN) != 0)
   17200           6 :             ereport(ERROR,
   17201             :                     (errcode(ERRCODE_DATATYPE_MISMATCH),
   17202             :                      errmsg("table has column \"%s\" where type requires \"%s\"",
   17203             :                             table_attname, type_attname)));
   17204             : 
   17205             :         /* Compare type. */
   17206          98 :         if (table_attr->atttypid != type_attr->atttypid ||
   17207          92 :             table_attr->atttypmod != type_attr->atttypmod ||
   17208          86 :             table_attr->attcollation != type_attr->attcollation)
   17209          12 :             ereport(ERROR,
   17210             :                     (errcode(ERRCODE_DATATYPE_MISMATCH),
   17211             :                      errmsg("table \"%s\" has different type for column \"%s\"",
   17212             :                             RelationGetRelationName(rel), type_attname)));
   17213             :     }
   17214          36 :     ReleaseTupleDesc(typeTupleDesc);
   17215             : 
   17216             :     /* Any remaining columns at the end of the table had better be dropped. */
   17217          36 :     for (; table_attno <= tableTupleDesc->natts; table_attno++)
   17218             :     {
   17219           6 :         Form_pg_attribute table_attr = TupleDescAttr(tableTupleDesc,
   17220             :                                                      table_attno - 1);
   17221             : 
   17222           6 :         if (!table_attr->attisdropped)
   17223           6 :             ereport(ERROR,
   17224             :                     (errcode(ERRCODE_DATATYPE_MISMATCH),
   17225             :                      errmsg("table has extra column \"%s\"",
   17226             :                             NameStr(table_attr->attname))));
   17227             :     }
   17228             : 
   17229             :     /* If the table was already typed, drop the existing dependency. */
   17230          30 :     if (rel->rd_rel->reloftype)
   17231           6 :         drop_parent_dependency(relid, TypeRelationId, rel->rd_rel->reloftype,
   17232             :                                DEPENDENCY_NORMAL);
   17233             : 
   17234             :     /* Record a dependency on the new type. */
   17235          30 :     tableobj.classId = RelationRelationId;
   17236          30 :     tableobj.objectId = relid;
   17237          30 :     tableobj.objectSubId = 0;
   17238          30 :     typeobj.classId = TypeRelationId;
   17239          30 :     typeobj.objectId = typeid;
   17240          30 :     typeobj.objectSubId = 0;
   17241          30 :     recordDependencyOn(&tableobj, &typeobj, DEPENDENCY_NORMAL);
   17242             : 
   17243             :     /* Update pg_class.reloftype */
   17244          30 :     relationRelation = table_open(RelationRelationId, RowExclusiveLock);
   17245          30 :     classtuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
   17246          30 :     if (!HeapTupleIsValid(classtuple))
   17247           0 :         elog(ERROR, "cache lookup failed for relation %u", relid);
   17248          30 :     ((Form_pg_class) GETSTRUCT(classtuple))->reloftype = typeid;
   17249          30 :     CatalogTupleUpdate(relationRelation, &classtuple->t_self, classtuple);
   17250             : 
   17251          30 :     InvokeObjectPostAlterHook(RelationRelationId, relid, 0);
   17252             : 
   17253          30 :     heap_freetuple(classtuple);
   17254          30 :     table_close(relationRelation, RowExclusiveLock);
   17255             : 
   17256          30 :     ReleaseSysCache(typetuple);
   17257             : 
   17258          30 :     return typeobj;
   17259             : }
   17260             : 
   17261             : /*
   17262             :  * ALTER TABLE NOT OF
   17263             :  *
   17264             :  * Detach a typed table from its originating type.  Just clear reloftype and
   17265             :  * remove the dependency.
   17266             :  */
   17267             : static void
   17268           6 : ATExecDropOf(Relation rel, LOCKMODE lockmode)
   17269             : {
   17270           6 :     Oid         relid = RelationGetRelid(rel);
   17271             :     Relation    relationRelation;
   17272             :     HeapTuple   tuple;
   17273             : 
   17274           6 :     if (!OidIsValid(rel->rd_rel->reloftype))
   17275           0 :         ereport(ERROR,
   17276             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   17277             :                  errmsg("\"%s\" is not a typed table",
   17278             :                         RelationGetRelationName(rel))));
   17279             : 
   17280             :     /*
   17281             :      * We don't bother to check ownership of the type --- ownership of the
   17282             :      * table is presumed enough rights.  No lock required on the type, either.
   17283             :      */
   17284             : 
   17285           6 :     drop_parent_dependency(relid, TypeRelationId, rel->rd_rel->reloftype,
   17286             :                            DEPENDENCY_NORMAL);
   17287             : 
   17288             :     /* Clear pg_class.reloftype */
   17289           6 :     relationRelation = table_open(RelationRelationId, RowExclusiveLock);
   17290           6 :     tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
   17291           6 :     if (!HeapTupleIsValid(tuple))
   17292           0 :         elog(ERROR, "cache lookup failed for relation %u", relid);
   17293           6 :     ((Form_pg_class) GETSTRUCT(tuple))->reloftype = InvalidOid;
   17294           6 :     CatalogTupleUpdate(relationRelation, &tuple->t_self, tuple);
   17295             : 
   17296           6 :     InvokeObjectPostAlterHook(RelationRelationId, relid, 0);
   17297             : 
   17298           6 :     heap_freetuple(tuple);
   17299           6 :     table_close(relationRelation, RowExclusiveLock);
   17300           6 : }
   17301             : 
   17302             : /*
   17303             :  * relation_mark_replica_identity: Update a table's replica identity
   17304             :  *
   17305             :  * Iff ri_type = REPLICA_IDENTITY_INDEX, indexOid must be the Oid of a suitable
   17306             :  * index. Otherwise, it must be InvalidOid.
   17307             :  *
   17308             :  * Caller had better hold an exclusive lock on the relation, as the results
   17309             :  * of running two of these concurrently wouldn't be pretty.
   17310             :  */
   17311             : static void
   17312         454 : relation_mark_replica_identity(Relation rel, char ri_type, Oid indexOid,
   17313             :                                bool is_internal)
   17314             : {
   17315             :     Relation    pg_index;
   17316             :     Relation    pg_class;
   17317             :     HeapTuple   pg_class_tuple;
   17318             :     HeapTuple   pg_index_tuple;
   17319             :     Form_pg_class pg_class_form;
   17320             :     Form_pg_index pg_index_form;
   17321             :     ListCell   *index;
   17322             : 
   17323             :     /*
   17324             :      * Check whether relreplident has changed, and update it if so.
   17325             :      */
   17326         454 :     pg_class = table_open(RelationRelationId, RowExclusiveLock);
   17327         454 :     pg_class_tuple = SearchSysCacheCopy1(RELOID,
   17328             :                                          ObjectIdGetDatum(RelationGetRelid(rel)));
   17329         454 :     if (!HeapTupleIsValid(pg_class_tuple))
   17330           0 :         elog(ERROR, "cache lookup failed for relation \"%s\"",
   17331             :              RelationGetRelationName(rel));
   17332         454 :     pg_class_form = (Form_pg_class) GETSTRUCT(pg_class_tuple);
   17333         454 :     if (pg_class_form->relreplident != ri_type)
   17334             :     {
   17335         404 :         pg_class_form->relreplident = ri_type;
   17336         404 :         CatalogTupleUpdate(pg_class, &pg_class_tuple->t_self, pg_class_tuple);
   17337             :     }
   17338         454 :     table_close(pg_class, RowExclusiveLock);
   17339         454 :     heap_freetuple(pg_class_tuple);
   17340             : 
   17341             :     /*
   17342             :      * Update the per-index indisreplident flags correctly.
   17343             :      */
   17344         454 :     pg_index = table_open(IndexRelationId, RowExclusiveLock);
   17345        1174 :     foreach(index, RelationGetIndexList(rel))
   17346             :     {
   17347         720 :         Oid         thisIndexOid = lfirst_oid(index);
   17348         720 :         bool        dirty = false;
   17349             : 
   17350         720 :         pg_index_tuple = SearchSysCacheCopy1(INDEXRELID,
   17351             :                                              ObjectIdGetDatum(thisIndexOid));
   17352         720 :         if (!HeapTupleIsValid(pg_index_tuple))
   17353           0 :             elog(ERROR, "cache lookup failed for index %u", thisIndexOid);
   17354         720 :         pg_index_form = (Form_pg_index) GETSTRUCT(pg_index_tuple);
   17355             : 
   17356         720 :         if (thisIndexOid == indexOid)
   17357             :         {
   17358             :             /* Set the bit if not already set. */
   17359         240 :             if (!pg_index_form->indisreplident)
   17360             :             {
   17361         222 :                 dirty = true;
   17362         222 :                 pg_index_form->indisreplident = true;
   17363             :             }
   17364             :         }
   17365             :         else
   17366             :         {
   17367             :             /* Unset the bit if set. */
   17368         480 :             if (pg_index_form->indisreplident)
   17369             :             {
   17370          52 :                 dirty = true;
   17371          52 :                 pg_index_form->indisreplident = false;
   17372             :             }
   17373             :         }
   17374             : 
   17375         720 :         if (dirty)
   17376             :         {
   17377         274 :             CatalogTupleUpdate(pg_index, &pg_index_tuple->t_self, pg_index_tuple);
   17378         274 :             InvokeObjectPostAlterHookArg(IndexRelationId, thisIndexOid, 0,
   17379             :                                          InvalidOid, is_internal);
   17380             : 
   17381             :             /*
   17382             :              * Invalidate the relcache for the table, so that after we commit
   17383             :              * all sessions will refresh the table's replica identity index
   17384             :              * before attempting any UPDATE or DELETE on the table.  (If we
   17385             :              * changed the table's pg_class row above, then a relcache inval
   17386             :              * is already queued due to that; but we might not have.)
   17387             :              */
   17388         274 :             CacheInvalidateRelcache(rel);
   17389             :         }
   17390         720 :         heap_freetuple(pg_index_tuple);
   17391             :     }
   17392             : 
   17393         454 :     table_close(pg_index, RowExclusiveLock);
   17394         454 : }
   17395             : 
   17396             : /*
   17397             :  * ALTER TABLE <name> REPLICA IDENTITY ...
   17398             :  */
   17399             : static void
   17400         502 : ATExecReplicaIdentity(Relation rel, ReplicaIdentityStmt *stmt, LOCKMODE lockmode)
   17401             : {
   17402             :     Oid         indexOid;
   17403             :     Relation    indexRel;
   17404             :     int         key;
   17405             : 
   17406         502 :     if (stmt->identity_type == REPLICA_IDENTITY_DEFAULT)
   17407             :     {
   17408           6 :         relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
   17409           6 :         return;
   17410             :     }
   17411         496 :     else if (stmt->identity_type == REPLICA_IDENTITY_FULL)
   17412             :     {
   17413         160 :         relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
   17414         160 :         return;
   17415             :     }
   17416         336 :     else if (stmt->identity_type == REPLICA_IDENTITY_NOTHING)
   17417             :     {
   17418          48 :         relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
   17419          48 :         return;
   17420             :     }
   17421         288 :     else if (stmt->identity_type == REPLICA_IDENTITY_INDEX)
   17422             :     {
   17423             :          /* fallthrough */ ;
   17424             :     }
   17425             :     else
   17426           0 :         elog(ERROR, "unexpected identity type %u", stmt->identity_type);
   17427             : 
   17428             :     /* Check that the index exists */
   17429         288 :     indexOid = get_relname_relid(stmt->name, rel->rd_rel->relnamespace);
   17430         288 :     if (!OidIsValid(indexOid))
   17431           0 :         ereport(ERROR,
   17432             :                 (errcode(ERRCODE_UNDEFINED_OBJECT),
   17433             :                  errmsg("index \"%s\" for table \"%s\" does not exist",
   17434             :                         stmt->name, RelationGetRelationName(rel))));
   17435             : 
   17436         288 :     indexRel = index_open(indexOid, ShareLock);
   17437             : 
   17438             :     /* Check that the index is on the relation we're altering. */
   17439         288 :     if (indexRel->rd_index == NULL ||
   17440         288 :         indexRel->rd_index->indrelid != RelationGetRelid(rel))
   17441           6 :         ereport(ERROR,
   17442             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   17443             :                  errmsg("\"%s\" is not an index for table \"%s\"",
   17444             :                         RelationGetRelationName(indexRel),
   17445             :                         RelationGetRelationName(rel))));
   17446             : 
   17447             :     /*
   17448             :      * The AM must support uniqueness, and the index must in fact be unique.
   17449             :      * If we have a WITHOUT OVERLAPS constraint (identified by uniqueness +
   17450             :      * exclusion), we can use that too.
   17451             :      */
   17452         282 :     if ((!indexRel->rd_indam->amcanunique ||
   17453         262 :          !indexRel->rd_index->indisunique) &&
   17454          26 :         !(indexRel->rd_index->indisunique && indexRel->rd_index->indisexclusion))
   17455          12 :         ereport(ERROR,
   17456             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   17457             :                  errmsg("cannot use non-unique index \"%s\" as replica identity",
   17458             :                         RelationGetRelationName(indexRel))));
   17459             :     /* Deferred indexes are not guaranteed to be always unique. */
   17460         270 :     if (!indexRel->rd_index->indimmediate)
   17461          12 :         ereport(ERROR,
   17462             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   17463             :                  errmsg("cannot use non-immediate index \"%s\" as replica identity",
   17464             :                         RelationGetRelationName(indexRel))));
   17465             :     /* Expression indexes aren't supported. */
   17466         258 :     if (RelationGetIndexExpressions(indexRel) != NIL)
   17467           6 :         ereport(ERROR,
   17468             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   17469             :                  errmsg("cannot use expression index \"%s\" as replica identity",
   17470             :                         RelationGetRelationName(indexRel))));
   17471             :     /* Predicate indexes aren't supported. */
   17472         252 :     if (RelationGetIndexPredicate(indexRel) != NIL)
   17473           6 :         ereport(ERROR,
   17474             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   17475             :                  errmsg("cannot use partial index \"%s\" as replica identity",
   17476             :                         RelationGetRelationName(indexRel))));
   17477             : 
   17478             :     /* Check index for nullable columns. */
   17479         552 :     for (key = 0; key < IndexRelationGetNumberOfKeyAttributes(indexRel); key++)
   17480             :     {
   17481         312 :         int16       attno = indexRel->rd_index->indkey.values[key];
   17482             :         Form_pg_attribute attr;
   17483             : 
   17484             :         /*
   17485             :          * Reject any other system columns.  (Going forward, we'll disallow
   17486             :          * indexes containing such columns in the first place, but they might
   17487             :          * exist in older branches.)
   17488             :          */
   17489         312 :         if (attno <= 0)
   17490           0 :             ereport(ERROR,
   17491             :                     (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
   17492             :                      errmsg("index \"%s\" cannot be used as replica identity because column %d is a system column",
   17493             :                             RelationGetRelationName(indexRel), attno)));
   17494             : 
   17495         312 :         attr = TupleDescAttr(rel->rd_att, attno - 1);
   17496         312 :         if (!attr->attnotnull)
   17497           6 :             ereport(ERROR,
   17498             :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   17499             :                      errmsg("index \"%s\" cannot be used as replica identity because column \"%s\" is nullable",
   17500             :                             RelationGetRelationName(indexRel),
   17501             :                             NameStr(attr->attname))));
   17502             :     }
   17503             : 
   17504             :     /* This index is suitable for use as a replica identity. Mark it. */
   17505         240 :     relation_mark_replica_identity(rel, stmt->identity_type, indexOid, true);
   17506             : 
   17507         240 :     index_close(indexRel, NoLock);
   17508             : }
   17509             : 
   17510             : /*
   17511             :  * ALTER TABLE ENABLE/DISABLE ROW LEVEL SECURITY
   17512             :  */
   17513             : static void
   17514         294 : ATExecSetRowSecurity(Relation rel, bool rls)
   17515             : {
   17516             :     Relation    pg_class;
   17517             :     Oid         relid;
   17518             :     HeapTuple   tuple;
   17519             : 
   17520         294 :     relid = RelationGetRelid(rel);
   17521             : 
   17522             :     /* Pull the record for this relation and update it */
   17523         294 :     pg_class = table_open(RelationRelationId, RowExclusiveLock);
   17524             : 
   17525         294 :     tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
   17526             : 
   17527         294 :     if (!HeapTupleIsValid(tuple))
   17528           0 :         elog(ERROR, "cache lookup failed for relation %u", relid);
   17529             : 
   17530         294 :     ((Form_pg_class) GETSTRUCT(tuple))->relrowsecurity = rls;
   17531         294 :     CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
   17532             : 
   17533         294 :     InvokeObjectPostAlterHook(RelationRelationId,
   17534             :                               RelationGetRelid(rel), 0);
   17535             : 
   17536         294 :     table_close(pg_class, RowExclusiveLock);
   17537         294 :     heap_freetuple(tuple);
   17538         294 : }
   17539             : 
   17540             : /*
   17541             :  * ALTER TABLE FORCE/NO FORCE ROW LEVEL SECURITY
   17542             :  */
   17543             : static void
   17544         120 : ATExecForceNoForceRowSecurity(Relation rel, bool force_rls)
   17545             : {
   17546             :     Relation    pg_class;
   17547             :     Oid         relid;
   17548             :     HeapTuple   tuple;
   17549             : 
   17550         120 :     relid = RelationGetRelid(rel);
   17551             : 
   17552         120 :     pg_class = table_open(RelationRelationId, RowExclusiveLock);
   17553             : 
   17554         120 :     tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
   17555             : 
   17556         120 :     if (!HeapTupleIsValid(tuple))
   17557           0 :         elog(ERROR, "cache lookup failed for relation %u", relid);
   17558             : 
   17559         120 :     ((Form_pg_class) GETSTRUCT(tuple))->relforcerowsecurity = force_rls;
   17560         120 :     CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
   17561             : 
   17562         120 :     InvokeObjectPostAlterHook(RelationRelationId,
   17563             :                               RelationGetRelid(rel), 0);
   17564             : 
   17565         120 :     table_close(pg_class, RowExclusiveLock);
   17566         120 :     heap_freetuple(tuple);
   17567         120 : }
   17568             : 
   17569             : /*
   17570             :  * ALTER FOREIGN TABLE <name> OPTIONS (...)
   17571             :  */
   17572             : static void
   17573          58 : ATExecGenericOptions(Relation rel, List *options)
   17574             : {
   17575             :     Relation    ftrel;
   17576             :     ForeignServer *server;
   17577             :     ForeignDataWrapper *fdw;
   17578             :     HeapTuple   tuple;
   17579             :     bool        isnull;
   17580             :     Datum       repl_val[Natts_pg_foreign_table];
   17581             :     bool        repl_null[Natts_pg_foreign_table];
   17582             :     bool        repl_repl[Natts_pg_foreign_table];
   17583             :     Datum       datum;
   17584             :     Form_pg_foreign_table tableform;
   17585             : 
   17586          58 :     if (options == NIL)
   17587           0 :         return;
   17588             : 
   17589          58 :     ftrel = table_open(ForeignTableRelationId, RowExclusiveLock);
   17590             : 
   17591          58 :     tuple = SearchSysCacheCopy1(FOREIGNTABLEREL,
   17592             :                                 ObjectIdGetDatum(rel->rd_id));
   17593          58 :     if (!HeapTupleIsValid(tuple))
   17594           0 :         ereport(ERROR,
   17595             :                 (errcode(ERRCODE_UNDEFINED_OBJECT),
   17596             :                  errmsg("foreign table \"%s\" does not exist",
   17597             :                         RelationGetRelationName(rel))));
   17598          58 :     tableform = (Form_pg_foreign_table) GETSTRUCT(tuple);
   17599          58 :     server = GetForeignServer(tableform->ftserver);
   17600          58 :     fdw = GetForeignDataWrapper(server->fdwid);
   17601             : 
   17602          58 :     memset(repl_val, 0, sizeof(repl_val));
   17603          58 :     memset(repl_null, false, sizeof(repl_null));
   17604          58 :     memset(repl_repl, false, sizeof(repl_repl));
   17605             : 
   17606             :     /* Extract the current options */
   17607          58 :     datum = SysCacheGetAttr(FOREIGNTABLEREL,
   17608             :                             tuple,
   17609             :                             Anum_pg_foreign_table_ftoptions,
   17610             :                             &isnull);
   17611          58 :     if (isnull)
   17612           4 :         datum = PointerGetDatum(NULL);
   17613             : 
   17614             :     /* Transform the options */
   17615          58 :     datum = transformGenericOptions(ForeignTableRelationId,
   17616             :                                     datum,
   17617             :                                     options,
   17618             :                                     fdw->fdwvalidator);
   17619             : 
   17620          56 :     if (PointerIsValid(DatumGetPointer(datum)))
   17621          56 :         repl_val[Anum_pg_foreign_table_ftoptions - 1] = datum;
   17622             :     else
   17623           0 :         repl_null[Anum_pg_foreign_table_ftoptions - 1] = true;
   17624             : 
   17625          56 :     repl_repl[Anum_pg_foreign_table_ftoptions - 1] = true;
   17626             : 
   17627             :     /* Everything looks good - update the tuple */
   17628             : 
   17629          56 :     tuple = heap_modify_tuple(tuple, RelationGetDescr(ftrel),
   17630             :                               repl_val, repl_null, repl_repl);
   17631             : 
   17632          56 :     CatalogTupleUpdate(ftrel, &tuple->t_self, tuple);
   17633             : 
   17634             :     /*
   17635             :      * Invalidate relcache so that all sessions will refresh any cached plans
   17636             :      * that might depend on the old options.
   17637             :      */
   17638          56 :     CacheInvalidateRelcache(rel);
   17639             : 
   17640          56 :     InvokeObjectPostAlterHook(ForeignTableRelationId,
   17641             :                               RelationGetRelid(rel), 0);
   17642             : 
   17643          56 :     table_close(ftrel, RowExclusiveLock);
   17644             : 
   17645          56 :     heap_freetuple(tuple);
   17646             : }
   17647             : 
   17648             : /*
   17649             :  * ALTER TABLE ALTER COLUMN SET COMPRESSION
   17650             :  *
   17651             :  * Return value is the address of the modified column
   17652             :  */
   17653             : static ObjectAddress
   17654          68 : ATExecSetCompression(Relation rel,
   17655             :                      const char *column,
   17656             :                      Node *newValue,
   17657             :                      LOCKMODE lockmode)
   17658             : {
   17659             :     Relation    attrel;
   17660             :     HeapTuple   tuple;
   17661             :     Form_pg_attribute atttableform;
   17662             :     AttrNumber  attnum;
   17663             :     char       *compression;
   17664             :     char        cmethod;
   17665             :     ObjectAddress address;
   17666             : 
   17667          68 :     compression = strVal(newValue);
   17668             : 
   17669          68 :     attrel = table_open(AttributeRelationId, RowExclusiveLock);
   17670             : 
   17671             :     /* copy the cache entry so we can scribble on it below */
   17672          68 :     tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), column);
   17673          68 :     if (!HeapTupleIsValid(tuple))
   17674           0 :         ereport(ERROR,
   17675             :                 (errcode(ERRCODE_UNDEFINED_COLUMN),
   17676             :                  errmsg("column \"%s\" of relation \"%s\" does not exist",
   17677             :                         column, RelationGetRelationName(rel))));
   17678             : 
   17679             :     /* prevent them from altering a system attribute */
   17680          68 :     atttableform = (Form_pg_attribute) GETSTRUCT(tuple);
   17681          68 :     attnum = atttableform->attnum;
   17682          68 :     if (attnum <= 0)
   17683           0 :         ereport(ERROR,
   17684             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   17685             :                  errmsg("cannot alter system column \"%s\"", column)));
   17686             : 
   17687             :     /*
   17688             :      * Check that column type is compressible, then get the attribute
   17689             :      * compression method code
   17690             :      */
   17691          68 :     cmethod = GetAttributeCompression(atttableform->atttypid, compression);
   17692             : 
   17693             :     /* update pg_attribute entry */
   17694          62 :     atttableform->attcompression = cmethod;
   17695          62 :     CatalogTupleUpdate(attrel, &tuple->t_self, tuple);
   17696             : 
   17697          62 :     InvokeObjectPostAlterHook(RelationRelationId,
   17698             :                               RelationGetRelid(rel),
   17699             :                               attnum);
   17700             : 
   17701             :     /*
   17702             :      * Apply the change to indexes as well (only for simple index columns,
   17703             :      * matching behavior of index.c ConstructTupleDescriptor()).
   17704             :      */
   17705          62 :     SetIndexStorageProperties(rel, attrel, attnum,
   17706             :                               false, 0,
   17707             :                               true, cmethod,
   17708             :                               lockmode);
   17709             : 
   17710          62 :     heap_freetuple(tuple);
   17711             : 
   17712          62 :     table_close(attrel, RowExclusiveLock);
   17713             : 
   17714             :     /* make changes visible */
   17715          62 :     CommandCounterIncrement();
   17716             : 
   17717          62 :     ObjectAddressSubSet(address, RelationRelationId,
   17718             :                         RelationGetRelid(rel), attnum);
   17719          62 :     return address;
   17720             : }
   17721             : 
   17722             : 
   17723             : /*
   17724             :  * Preparation phase for SET LOGGED/UNLOGGED
   17725             :  *
   17726             :  * This verifies that we're not trying to change a temp table.  Also,
   17727             :  * existing foreign key constraints are checked to avoid ending up with
   17728             :  * permanent tables referencing unlogged tables.
   17729             :  */
   17730             : static void
   17731         100 : ATPrepChangePersistence(AlteredTableInfo *tab, Relation rel, bool toLogged)
   17732             : {
   17733             :     Relation    pg_constraint;
   17734             :     HeapTuple   tuple;
   17735             :     SysScanDesc scan;
   17736             :     ScanKeyData skey[1];
   17737             : 
   17738             :     /*
   17739             :      * Disallow changing status for a temp table.  Also verify whether we can
   17740             :      * get away with doing nothing; in such cases we don't need to run the
   17741             :      * checks below, either.
   17742             :      */
   17743         100 :     switch (rel->rd_rel->relpersistence)
   17744             :     {
   17745           0 :         case RELPERSISTENCE_TEMP:
   17746           0 :             ereport(ERROR,
   17747             :                     (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
   17748             :                      errmsg("cannot change logged status of table \"%s\" because it is temporary",
   17749             :                             RelationGetRelationName(rel)),
   17750             :                      errtable(rel)));
   17751             :             break;
   17752          56 :         case RELPERSISTENCE_PERMANENT:
   17753          56 :             if (toLogged)
   17754             :                 /* nothing to do */
   17755          12 :                 return;
   17756          50 :             break;
   17757          44 :         case RELPERSISTENCE_UNLOGGED:
   17758          44 :             if (!toLogged)
   17759             :                 /* nothing to do */
   17760           6 :                 return;
   17761          38 :             break;
   17762             :     }
   17763             : 
   17764             :     /*
   17765             :      * Check that the table is not part of any publication when changing to
   17766             :      * UNLOGGED, as UNLOGGED tables can't be published.
   17767             :      */
   17768         138 :     if (!toLogged &&
   17769          50 :         GetRelationPublications(RelationGetRelid(rel)) != NIL)
   17770           0 :         ereport(ERROR,
   17771             :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
   17772             :                  errmsg("cannot change table \"%s\" to unlogged because it is part of a publication",
   17773             :                         RelationGetRelationName(rel)),
   17774             :                  errdetail("Unlogged relations cannot be replicated.")));
   17775             : 
   17776             :     /*
   17777             :      * Check existing foreign key constraints to preserve the invariant that
   17778             :      * permanent tables cannot reference unlogged ones.  Self-referencing
   17779             :      * foreign keys can safely be ignored.
   17780             :      */
   17781          88 :     pg_constraint = table_open(ConstraintRelationId, AccessShareLock);
   17782             : 
   17783             :     /*
   17784             :      * Scan conrelid if changing to permanent, else confrelid.  This also
   17785             :      * determines whether a useful index exists.
   17786             :      */
   17787          88 :     ScanKeyInit(&skey[0],
   17788             :                 toLogged ? Anum_pg_constraint_conrelid :
   17789             :                 Anum_pg_constraint_confrelid,
   17790             :                 BTEqualStrategyNumber, F_OIDEQ,
   17791             :                 ObjectIdGetDatum(RelationGetRelid(rel)));
   17792          88 :     scan = systable_beginscan(pg_constraint,
   17793             :                               toLogged ? ConstraintRelidTypidNameIndexId : InvalidOid,
   17794             :                               true, NULL, 1, skey);
   17795             : 
   17796         142 :     while (HeapTupleIsValid(tuple = systable_getnext(scan)))
   17797             :     {
   17798          66 :         Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tuple);
   17799             : 
   17800          66 :         if (con->contype == CONSTRAINT_FOREIGN)
   17801             :         {
   17802             :             Oid         foreignrelid;
   17803             :             Relation    foreignrel;
   17804             : 
   17805             :             /* the opposite end of what we used as scankey */
   17806          30 :             foreignrelid = toLogged ? con->confrelid : con->conrelid;
   17807             : 
   17808             :             /* ignore if self-referencing */
   17809          30 :             if (RelationGetRelid(rel) == foreignrelid)
   17810          12 :                 continue;
   17811             : 
   17812          18 :             foreignrel = relation_open(foreignrelid, AccessShareLock);
   17813             : 
   17814          18 :             if (toLogged)
   17815             :             {
   17816           6 :                 if (!RelationIsPermanent(foreignrel))
   17817           6 :                     ereport(ERROR,
   17818             :                             (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
   17819             :                              errmsg("could not change table \"%s\" to logged because it references unlogged table \"%s\"",
   17820             :                                     RelationGetRelationName(rel),
   17821             :                                     RelationGetRelationName(foreignrel)),
   17822             :                              errtableconstraint(rel, NameStr(con->conname))));
   17823             :             }
   17824             :             else
   17825             :             {
   17826          12 :                 if (RelationIsPermanent(foreignrel))
   17827           6 :                     ereport(ERROR,
   17828             :                             (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
   17829             :                              errmsg("could not change table \"%s\" to unlogged because it references logged table \"%s\"",
   17830             :                                     RelationGetRelationName(rel),
   17831             :                                     RelationGetRelationName(foreignrel)),
   17832             :                              errtableconstraint(rel, NameStr(con->conname))));
   17833             :             }
   17834             : 
   17835           6 :             relation_close(foreignrel, AccessShareLock);
   17836             :         }
   17837             :     }
   17838             : 
   17839          76 :     systable_endscan(scan);
   17840             : 
   17841          76 :     table_close(pg_constraint, AccessShareLock);
   17842             : 
   17843             :     /* force rewrite if necessary; see comment in ATRewriteTables */
   17844          76 :     tab->rewrite |= AT_REWRITE_ALTER_PERSISTENCE;
   17845          76 :     if (toLogged)
   17846          32 :         tab->newrelpersistence = RELPERSISTENCE_PERMANENT;
   17847             :     else
   17848          44 :         tab->newrelpersistence = RELPERSISTENCE_UNLOGGED;
   17849          76 :     tab->chgPersistence = true;
   17850             : }
   17851             : 
   17852             : /*
   17853             :  * Execute ALTER TABLE SET SCHEMA
   17854             :  */
   17855             : ObjectAddress
   17856         104 : AlterTableNamespace(AlterObjectSchemaStmt *stmt, Oid *oldschema)
   17857             : {
   17858             :     Relation    rel;
   17859             :     Oid         relid;
   17860             :     Oid         oldNspOid;
   17861             :     Oid         nspOid;
   17862             :     RangeVar   *newrv;
   17863             :     ObjectAddresses *objsMoved;
   17864             :     ObjectAddress myself;
   17865             : 
   17866         104 :     relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
   17867         104 :                                      stmt->missing_ok ? RVR_MISSING_OK : 0,
   17868             :                                      RangeVarCallbackForAlterRelation,
   17869             :                                      stmt);
   17870             : 
   17871         102 :     if (!OidIsValid(relid))
   17872             :     {
   17873          12 :         ereport(NOTICE,
   17874             :                 (errmsg("relation \"%s\" does not exist, skipping",
   17875             :                         stmt->relation->relname)));
   17876          12 :         return InvalidObjectAddress;
   17877             :     }
   17878             : 
   17879          90 :     rel = relation_open(relid, NoLock);
   17880             : 
   17881          90 :     oldNspOid = RelationGetNamespace(rel);
   17882             : 
   17883             :     /* If it's an owned sequence, disallow moving it by itself. */
   17884          90 :     if (rel->rd_rel->relkind == RELKIND_SEQUENCE)
   17885             :     {
   17886             :         Oid         tableId;
   17887             :         int32       colId;
   17888             : 
   17889          10 :         if (sequenceIsOwned(relid, DEPENDENCY_AUTO, &tableId, &colId) ||
   17890           2 :             sequenceIsOwned(relid, DEPENDENCY_INTERNAL, &tableId, &colId))
   17891           6 :             ereport(ERROR,
   17892             :                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   17893             :                      errmsg("cannot move an owned sequence into another schema"),
   17894             :                      errdetail("Sequence \"%s\" is linked to table \"%s\".",
   17895             :                                RelationGetRelationName(rel),
   17896             :                                get_rel_name(tableId))));
   17897             :     }
   17898             : 
   17899             :     /* Get and lock schema OID and check its permissions. */
   17900          84 :     newrv = makeRangeVar(stmt->newschema, RelationGetRelationName(rel), -1);
   17901          84 :     nspOid = RangeVarGetAndCheckCreationNamespace(newrv, NoLock, NULL);
   17902             : 
   17903             :     /* common checks on switching namespaces */
   17904          84 :     CheckSetNamespace(oldNspOid, nspOid);
   17905             : 
   17906          84 :     objsMoved = new_object_addresses();
   17907          84 :     AlterTableNamespaceInternal(rel, oldNspOid, nspOid, objsMoved);
   17908          84 :     free_object_addresses(objsMoved);
   17909             : 
   17910          84 :     ObjectAddressSet(myself, RelationRelationId, relid);
   17911             : 
   17912          84 :     if (oldschema)
   17913          84 :         *oldschema = oldNspOid;
   17914             : 
   17915             :     /* close rel, but keep lock until commit */
   17916          84 :     relation_close(rel, NoLock);
   17917             : 
   17918          84 :     return myself;
   17919             : }
   17920             : 
   17921             : /*
   17922             :  * The guts of relocating a table or materialized view to another namespace:
   17923             :  * besides moving the relation itself, its dependent objects are relocated to
   17924             :  * the new schema.
   17925             :  */
   17926             : void
   17927          86 : AlterTableNamespaceInternal(Relation rel, Oid oldNspOid, Oid nspOid,
   17928             :                             ObjectAddresses *objsMoved)
   17929             : {
   17930             :     Relation    classRel;
   17931             : 
   17932             :     Assert(objsMoved != NULL);
   17933             : 
   17934             :     /* OK, modify the pg_class row and pg_depend entry */
   17935          86 :     classRel = table_open(RelationRelationId, RowExclusiveLock);
   17936             : 
   17937          86 :     AlterRelationNamespaceInternal(classRel, RelationGetRelid(rel), oldNspOid,
   17938             :                                    nspOid, true, objsMoved);
   17939             : 
   17940             :     /* Fix the table's row type too, if it has one */
   17941          86 :     if (OidIsValid(rel->rd_rel->reltype))
   17942          84 :         AlterTypeNamespaceInternal(rel->rd_rel->reltype, nspOid,
   17943             :                                    false,   /* isImplicitArray */
   17944             :                                    false,   /* ignoreDependent */
   17945             :                                    false,   /* errorOnTableType */
   17946             :                                    objsMoved);
   17947             : 
   17948             :     /* Fix other dependent stuff */
   17949          86 :     AlterIndexNamespaces(classRel, rel, oldNspOid, nspOid, objsMoved);
   17950          86 :     AlterSeqNamespaces(classRel, rel, oldNspOid, nspOid,
   17951             :                        objsMoved, AccessExclusiveLock);
   17952          86 :     AlterConstraintNamespaces(RelationGetRelid(rel), oldNspOid, nspOid,
   17953             :                               false, objsMoved);
   17954             : 
   17955          86 :     table_close(classRel, RowExclusiveLock);
   17956          86 : }
   17957             : 
   17958             : /*
   17959             :  * The guts of relocating a relation to another namespace: fix the pg_class
   17960             :  * entry, and the pg_depend entry if any.  Caller must already have
   17961             :  * opened and write-locked pg_class.
   17962             :  */
   17963             : void
   17964         188 : AlterRelationNamespaceInternal(Relation classRel, Oid relOid,
   17965             :                                Oid oldNspOid, Oid newNspOid,
   17966             :                                bool hasDependEntry,
   17967             :                                ObjectAddresses *objsMoved)
   17968             : {
   17969             :     HeapTuple   classTup;
   17970             :     Form_pg_class classForm;
   17971             :     ObjectAddress thisobj;
   17972         188 :     bool        already_done = false;
   17973             : 
   17974             :     /* no rel lock for relkind=c so use LOCKTAG_TUPLE */
   17975         188 :     classTup = SearchSysCacheLockedCopy1(RELOID, ObjectIdGetDatum(relOid));
   17976         188 :     if (!HeapTupleIsValid(classTup))
   17977           0 :         elog(ERROR, "cache lookup failed for relation %u", relOid);
   17978         188 :     classForm = (Form_pg_class) GETSTRUCT(classTup);
   17979             : 
   17980             :     Assert(classForm->relnamespace == oldNspOid);
   17981             : 
   17982         188 :     thisobj.classId = RelationRelationId;
   17983         188 :     thisobj.objectId = relOid;
   17984         188 :     thisobj.objectSubId = 0;
   17985             : 
   17986             :     /*
   17987             :      * If the object has already been moved, don't move it again.  If it's
   17988             :      * already in the right place, don't move it, but still fire the object
   17989             :      * access hook.
   17990             :      */
   17991         188 :     already_done = object_address_present(&thisobj, objsMoved);
   17992         188 :     if (!already_done && oldNspOid != newNspOid)
   17993         146 :     {
   17994         146 :         ItemPointerData otid = classTup->t_self;
   17995             : 
   17996             :         /* check for duplicate name (more friendly than unique-index failure) */
   17997         146 :         if (get_relname_relid(NameStr(classForm->relname),
   17998             :                               newNspOid) != InvalidOid)
   17999           0 :             ereport(ERROR,
   18000             :                     (errcode(ERRCODE_DUPLICATE_TABLE),
   18001             :                      errmsg("relation \"%s\" already exists in schema \"%s\"",
   18002             :                             NameStr(classForm->relname),
   18003             :                             get_namespace_name(newNspOid))));
   18004             : 
   18005             :         /* classTup is a copy, so OK to scribble on */
   18006         146 :         classForm->relnamespace = newNspOid;
   18007             : 
   18008         146 :         CatalogTupleUpdate(classRel, &otid, classTup);
   18009         146 :         UnlockTuple(classRel, &otid, InplaceUpdateTupleLock);
   18010             : 
   18011             : 
   18012             :         /* Update dependency on schema if caller said so */
   18013         250 :         if (hasDependEntry &&
   18014         104 :             changeDependencyFor(RelationRelationId,
   18015             :                                 relOid,
   18016             :                                 NamespaceRelationId,
   18017             :                                 oldNspOid,
   18018             :                                 newNspOid) != 1)
   18019           0 :             elog(ERROR, "could not change schema dependency for relation \"%s\"",
   18020             :                  NameStr(classForm->relname));
   18021             :     }
   18022             :     else
   18023          42 :         UnlockTuple(classRel, &classTup->t_self, InplaceUpdateTupleLock);
   18024         188 :     if (!already_done)
   18025             :     {
   18026         188 :         add_exact_object_address(&thisobj, objsMoved);
   18027             : 
   18028         188 :         InvokeObjectPostAlterHook(RelationRelationId, relOid, 0);
   18029             :     }
   18030             : 
   18031         188 :     heap_freetuple(classTup);
   18032         188 : }
   18033             : 
   18034             : /*
   18035             :  * Move all indexes for the specified relation to another namespace.
   18036             :  *
   18037             :  * Note: we assume adequate permission checking was done by the caller,
   18038             :  * and that the caller has a suitable lock on the owning relation.
   18039             :  */
   18040             : static void
   18041          86 : AlterIndexNamespaces(Relation classRel, Relation rel,
   18042             :                      Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved)
   18043             : {
   18044             :     List       *indexList;
   18045             :     ListCell   *l;
   18046             : 
   18047          86 :     indexList = RelationGetIndexList(rel);
   18048             : 
   18049         132 :     foreach(l, indexList)
   18050             :     {
   18051          46 :         Oid         indexOid = lfirst_oid(l);
   18052             :         ObjectAddress thisobj;
   18053             : 
   18054          46 :         thisobj.classId = RelationRelationId;
   18055          46 :         thisobj.objectId = indexOid;
   18056          46 :         thisobj.objectSubId = 0;
   18057             : 
   18058             :         /*
   18059             :          * Note: currently, the index will not have its own dependency on the
   18060             :          * namespace, so we don't need to do changeDependencyFor(). There's no
   18061             :          * row type in pg_type, either.
   18062             :          *
   18063             :          * XXX this objsMoved test may be pointless -- surely we have a single
   18064             :          * dependency link from a relation to each index?
   18065             :          */
   18066          46 :         if (!object_address_present(&thisobj, objsMoved))
   18067             :         {
   18068          46 :             AlterRelationNamespaceInternal(classRel, indexOid,
   18069             :                                            oldNspOid, newNspOid,
   18070             :                                            false, objsMoved);
   18071          46 :             add_exact_object_address(&thisobj, objsMoved);
   18072             :         }
   18073             :     }
   18074             : 
   18075          86 :     list_free(indexList);
   18076          86 : }
   18077             : 
   18078             : /*
   18079             :  * Move all identity and SERIAL-column sequences of the specified relation to another
   18080             :  * namespace.
   18081             :  *
   18082             :  * Note: we assume adequate permission checking was done by the caller,
   18083             :  * and that the caller has a suitable lock on the owning relation.
   18084             :  */
   18085             : static void
   18086          86 : AlterSeqNamespaces(Relation classRel, Relation rel,
   18087             :                    Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved,
   18088             :                    LOCKMODE lockmode)
   18089             : {
   18090             :     Relation    depRel;
   18091             :     SysScanDesc scan;
   18092             :     ScanKeyData key[2];
   18093             :     HeapTuple   tup;
   18094             : 
   18095             :     /*
   18096             :      * SERIAL sequences are those having an auto dependency on one of the
   18097             :      * table's columns (we don't care *which* column, exactly).
   18098             :      */
   18099          86 :     depRel = table_open(DependRelationId, AccessShareLock);
   18100             : 
   18101          86 :     ScanKeyInit(&key[0],
   18102             :                 Anum_pg_depend_refclassid,
   18103             :                 BTEqualStrategyNumber, F_OIDEQ,
   18104             :                 ObjectIdGetDatum(RelationRelationId));
   18105          86 :     ScanKeyInit(&key[1],
   18106             :                 Anum_pg_depend_refobjid,
   18107             :                 BTEqualStrategyNumber, F_OIDEQ,
   18108             :                 ObjectIdGetDatum(RelationGetRelid(rel)));
   18109             :     /* we leave refobjsubid unspecified */
   18110             : 
   18111          86 :     scan = systable_beginscan(depRel, DependReferenceIndexId, true,
   18112             :                               NULL, 2, key);
   18113             : 
   18114         616 :     while (HeapTupleIsValid(tup = systable_getnext(scan)))
   18115             :     {
   18116         530 :         Form_pg_depend depForm = (Form_pg_depend) GETSTRUCT(tup);
   18117             :         Relation    seqRel;
   18118             : 
   18119             :         /* skip dependencies other than auto dependencies on columns */
   18120         530 :         if (depForm->refobjsubid == 0 ||
   18121         382 :             depForm->classid != RelationRelationId ||
   18122          42 :             depForm->objsubid != 0 ||
   18123          42 :             !(depForm->deptype == DEPENDENCY_AUTO || depForm->deptype == DEPENDENCY_INTERNAL))
   18124         488 :             continue;
   18125             : 
   18126             :         /* Use relation_open just in case it's an index */
   18127          42 :         seqRel = relation_open(depForm->objid, lockmode);
   18128             : 
   18129             :         /* skip non-sequence relations */
   18130          42 :         if (RelationGetForm(seqRel)->relkind != RELKIND_SEQUENCE)
   18131             :         {
   18132             :             /* No need to keep the lock */
   18133           0 :             relation_close(seqRel, lockmode);
   18134           0 :             continue;
   18135             :         }
   18136             : 
   18137             :         /* Fix the pg_class and pg_depend entries */
   18138          42 :         AlterRelationNamespaceInternal(classRel, depForm->objid,
   18139             :                                        oldNspOid, newNspOid,
   18140             :                                        true, objsMoved);
   18141             : 
   18142             :         /*
   18143             :          * Sequences used to have entries in pg_type, but no longer do.  If we
   18144             :          * ever re-instate that, we'll need to move the pg_type entry to the
   18145             :          * new namespace, too (using AlterTypeNamespaceInternal).
   18146             :          */
   18147             :         Assert(RelationGetForm(seqRel)->reltype == InvalidOid);
   18148             : 
   18149             :         /* Now we can close it.  Keep the lock till end of transaction. */
   18150          42 :         relation_close(seqRel, NoLock);
   18151             :     }
   18152             : 
   18153          86 :     systable_endscan(scan);
   18154             : 
   18155          86 :     relation_close(depRel, AccessShareLock);
   18156          86 : }
   18157             : 
   18158             : 
   18159             : /*
   18160             :  * This code supports
   18161             :  *  CREATE TEMP TABLE ... ON COMMIT { DROP | PRESERVE ROWS | DELETE ROWS }
   18162             :  *
   18163             :  * Because we only support this for TEMP tables, it's sufficient to remember
   18164             :  * the state in a backend-local data structure.
   18165             :  */
   18166             : 
   18167             : /*
   18168             :  * Register a newly-created relation's ON COMMIT action.
   18169             :  */
   18170             : void
   18171         166 : register_on_commit_action(Oid relid, OnCommitAction action)
   18172             : {
   18173             :     OnCommitItem *oc;
   18174             :     MemoryContext oldcxt;
   18175             : 
   18176             :     /*
   18177             :      * We needn't bother registering the relation unless there is an ON COMMIT
   18178             :      * action we need to take.
   18179             :      */
   18180         166 :     if (action == ONCOMMIT_NOOP || action == ONCOMMIT_PRESERVE_ROWS)
   18181          24 :         return;
   18182             : 
   18183         142 :     oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
   18184             : 
   18185         142 :     oc = (OnCommitItem *) palloc(sizeof(OnCommitItem));
   18186         142 :     oc->relid = relid;
   18187         142 :     oc->oncommit = action;
   18188         142 :     oc->creating_subid = GetCurrentSubTransactionId();
   18189         142 :     oc->deleting_subid = InvalidSubTransactionId;
   18190             : 
   18191             :     /*
   18192             :      * We use lcons() here so that ON COMMIT actions are processed in reverse
   18193             :      * order of registration.  That might not be essential but it seems
   18194             :      * reasonable.
   18195             :      */
   18196         142 :     on_commits = lcons(oc, on_commits);
   18197             : 
   18198         142 :     MemoryContextSwitchTo(oldcxt);
   18199             : }
   18200             : 
   18201             : /*
   18202             :  * Unregister any ON COMMIT action when a relation is deleted.
   18203             :  *
   18204             :  * Actually, we only mark the OnCommitItem entry as to be deleted after commit.
   18205             :  */
   18206             : void
   18207       45446 : remove_on_commit_action(Oid relid)
   18208             : {
   18209             :     ListCell   *l;
   18210             : 
   18211       45580 :     foreach(l, on_commits)
   18212             :     {
   18213         264 :         OnCommitItem *oc = (OnCommitItem *) lfirst(l);
   18214             : 
   18215         264 :         if (oc->relid == relid)
   18216             :         {
   18217         130 :             oc->deleting_subid = GetCurrentSubTransactionId();
   18218         130 :             break;
   18219             :         }
   18220             :     }
   18221       45446 : }
   18222             : 
   18223             : /*
   18224             :  * Perform ON COMMIT actions.
   18225             :  *
   18226             :  * This is invoked just before actually committing, since it's possible
   18227             :  * to encounter errors.
   18228             :  */
   18229             : void
   18230      739254 : PreCommit_on_commit_actions(void)
   18231             : {
   18232             :     ListCell   *l;
   18233      739254 :     List       *oids_to_truncate = NIL;
   18234      739254 :     List       *oids_to_drop = NIL;
   18235             : 
   18236      739970 :     foreach(l, on_commits)
   18237             :     {
   18238         716 :         OnCommitItem *oc = (OnCommitItem *) lfirst(l);
   18239             : 
   18240             :         /* Ignore entry if already dropped in this xact */
   18241         716 :         if (oc->deleting_subid != InvalidSubTransactionId)
   18242          68 :             continue;
   18243             : 
   18244         648 :         switch (oc->oncommit)
   18245             :         {
   18246           0 :             case ONCOMMIT_NOOP:
   18247             :             case ONCOMMIT_PRESERVE_ROWS:
   18248             :                 /* Do nothing (there shouldn't be such entries, actually) */
   18249           0 :                 break;
   18250         598 :             case ONCOMMIT_DELETE_ROWS:
   18251             : 
   18252             :                 /*
   18253             :                  * If this transaction hasn't accessed any temporary
   18254             :                  * relations, we can skip truncating ON COMMIT DELETE ROWS
   18255             :                  * tables, as they must still be empty.
   18256             :                  */
   18257         598 :                 if ((MyXactFlags & XACT_FLAGS_ACCESSEDTEMPNAMESPACE))
   18258         400 :                     oids_to_truncate = lappend_oid(oids_to_truncate, oc->relid);
   18259         598 :                 break;
   18260          50 :             case ONCOMMIT_DROP:
   18261          50 :                 oids_to_drop = lappend_oid(oids_to_drop, oc->relid);
   18262          50 :                 break;
   18263             :         }
   18264         716 :     }
   18265             : 
   18266             :     /*
   18267             :      * Truncate relations before dropping so that all dependencies between
   18268             :      * relations are removed after they are worked on.  Doing it like this
   18269             :      * might be a waste as it is possible that a relation being truncated will
   18270             :      * be dropped anyway due to its parent being dropped, but this makes the
   18271             :      * code more robust because of not having to re-check that the relation
   18272             :      * exists at truncation time.
   18273             :      */
   18274      739254 :     if (oids_to_truncate != NIL)
   18275         334 :         heap_truncate(oids_to_truncate);
   18276             : 
   18277      739248 :     if (oids_to_drop != NIL)
   18278             :     {
   18279          44 :         ObjectAddresses *targetObjects = new_object_addresses();
   18280             : 
   18281          94 :         foreach(l, oids_to_drop)
   18282             :         {
   18283             :             ObjectAddress object;
   18284             : 
   18285          50 :             object.classId = RelationRelationId;
   18286          50 :             object.objectId = lfirst_oid(l);
   18287          50 :             object.objectSubId = 0;
   18288             : 
   18289             :             Assert(!object_address_present(&object, targetObjects));
   18290             : 
   18291          50 :             add_exact_object_address(&object, targetObjects);
   18292             :         }
   18293             : 
   18294             :         /*
   18295             :          * Object deletion might involve toast table access (to clean up
   18296             :          * toasted catalog entries), so ensure we have a valid snapshot.
   18297             :          */
   18298          44 :         PushActiveSnapshot(GetTransactionSnapshot());
   18299             : 
   18300             :         /*
   18301             :          * Since this is an automatic drop, rather than one directly initiated
   18302             :          * by the user, we pass the PERFORM_DELETION_INTERNAL flag.
   18303             :          */
   18304          44 :         performMultipleDeletions(targetObjects, DROP_CASCADE,
   18305             :                                  PERFORM_DELETION_INTERNAL | PERFORM_DELETION_QUIETLY);
   18306             : 
   18307          44 :         PopActiveSnapshot();
   18308             : 
   18309             : #ifdef USE_ASSERT_CHECKING
   18310             : 
   18311             :         /*
   18312             :          * Note that table deletion will call remove_on_commit_action, so the
   18313             :          * entry should get marked as deleted.
   18314             :          */
   18315             :         foreach(l, on_commits)
   18316             :         {
   18317             :             OnCommitItem *oc = (OnCommitItem *) lfirst(l);
   18318             : 
   18319             :             if (oc->oncommit != ONCOMMIT_DROP)
   18320             :                 continue;
   18321             : 
   18322             :             Assert(oc->deleting_subid != InvalidSubTransactionId);
   18323             :         }
   18324             : #endif
   18325             :     }
   18326      739248 : }
   18327             : 
   18328             : /*
   18329             :  * Post-commit or post-abort cleanup for ON COMMIT management.
   18330             :  *
   18331             :  * All we do here is remove no-longer-needed OnCommitItem entries.
   18332             :  *
   18333             :  * During commit, remove entries that were deleted during this transaction;
   18334             :  * during abort, remove those created during this transaction.
   18335             :  */
   18336             : void
   18337      785948 : AtEOXact_on_commit_actions(bool isCommit)
   18338             : {
   18339             :     ListCell   *cur_item;
   18340             : 
   18341      786694 :     foreach(cur_item, on_commits)
   18342             :     {
   18343         746 :         OnCommitItem *oc = (OnCommitItem *) lfirst(cur_item);
   18344             : 
   18345         848 :         if (isCommit ? oc->deleting_subid != InvalidSubTransactionId :
   18346         102 :             oc->creating_subid != InvalidSubTransactionId)
   18347             :         {
   18348             :             /* cur_item must be removed */
   18349         142 :             on_commits = foreach_delete_current(on_commits, cur_item);
   18350         142 :             pfree(oc);
   18351             :         }
   18352             :         else
   18353             :         {
   18354             :             /* cur_item must be preserved */
   18355         604 :             oc->creating_subid = InvalidSubTransactionId;
   18356         604 :             oc->deleting_subid = InvalidSubTransactionId;
   18357             :         }
   18358             :     }
   18359      785948 : }
   18360             : 
   18361             : /*
   18362             :  * Post-subcommit or post-subabort cleanup for ON COMMIT management.
   18363             :  *
   18364             :  * During subabort, we can immediately remove entries created during this
   18365             :  * subtransaction.  During subcommit, just relabel entries marked during
   18366             :  * this subtransaction as being the parent's responsibility.
   18367             :  */
   18368             : void
   18369       20040 : AtEOSubXact_on_commit_actions(bool isCommit, SubTransactionId mySubid,
   18370             :                               SubTransactionId parentSubid)
   18371             : {
   18372             :     ListCell   *cur_item;
   18373             : 
   18374       20040 :     foreach(cur_item, on_commits)
   18375             :     {
   18376           0 :         OnCommitItem *oc = (OnCommitItem *) lfirst(cur_item);
   18377             : 
   18378           0 :         if (!isCommit && oc->creating_subid == mySubid)
   18379             :         {
   18380             :             /* cur_item must be removed */
   18381           0 :             on_commits = foreach_delete_current(on_commits, cur_item);
   18382           0 :             pfree(oc);
   18383             :         }
   18384             :         else
   18385             :         {
   18386             :             /* cur_item must be preserved */
   18387           0 :             if (oc->creating_subid == mySubid)
   18388           0 :                 oc->creating_subid = parentSubid;
   18389           0 :             if (oc->deleting_subid == mySubid)
   18390           0 :                 oc->deleting_subid = isCommit ? parentSubid : InvalidSubTransactionId;
   18391             :         }
   18392             :     }
   18393       20040 : }
   18394             : 
   18395             : /*
   18396             :  * This is intended as a callback for RangeVarGetRelidExtended().  It allows
   18397             :  * the relation to be locked only if (1) it's a plain or partitioned table,
   18398             :  * materialized view, or TOAST table and (2) the current user is the owner (or
   18399             :  * the superuser) or has been granted MAINTAIN.  This meets the
   18400             :  * permission-checking needs of CLUSTER, REINDEX TABLE, and REFRESH
   18401             :  * MATERIALIZED VIEW; we expose it here so that it can be used by all.
   18402             :  */
   18403             : void
   18404        1020 : RangeVarCallbackMaintainsTable(const RangeVar *relation,
   18405             :                                Oid relId, Oid oldRelId, void *arg)
   18406             : {
   18407             :     char        relkind;
   18408             :     AclResult   aclresult;
   18409             : 
   18410             :     /* Nothing to do if the relation was not found. */
   18411        1020 :     if (!OidIsValid(relId))
   18412           6 :         return;
   18413             : 
   18414             :     /*
   18415             :      * If the relation does exist, check whether it's an index.  But note that
   18416             :      * the relation might have been dropped between the time we did the name
   18417             :      * lookup and now.  In that case, there's nothing to do.
   18418             :      */
   18419        1014 :     relkind = get_rel_relkind(relId);
   18420        1014 :     if (!relkind)
   18421           0 :         return;
   18422        1014 :     if (relkind != RELKIND_RELATION && relkind != RELKIND_TOASTVALUE &&
   18423         140 :         relkind != RELKIND_MATVIEW && relkind != RELKIND_PARTITIONED_TABLE)
   18424          28 :         ereport(ERROR,
   18425             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   18426             :                  errmsg("\"%s\" is not a table or materialized view", relation->relname)));
   18427             : 
   18428             :     /* Check permissions */
   18429         986 :     aclresult = pg_class_aclcheck(relId, GetUserId(), ACL_MAINTAIN);
   18430         986 :     if (aclresult != ACLCHECK_OK)
   18431          30 :         aclcheck_error(aclresult,
   18432          30 :                        get_relkind_objtype(get_rel_relkind(relId)),
   18433          30 :                        relation->relname);
   18434             : }
   18435             : 
   18436             : /*
   18437             :  * Callback to RangeVarGetRelidExtended() for TRUNCATE processing.
   18438             :  */
   18439             : static void
   18440        2052 : RangeVarCallbackForTruncate(const RangeVar *relation,
   18441             :                             Oid relId, Oid oldRelId, void *arg)
   18442             : {
   18443             :     HeapTuple   tuple;
   18444             : 
   18445             :     /* Nothing to do if the relation was not found. */
   18446        2052 :     if (!OidIsValid(relId))
   18447           0 :         return;
   18448             : 
   18449        2052 :     tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relId));
   18450        2052 :     if (!HeapTupleIsValid(tuple))   /* should not happen */
   18451           0 :         elog(ERROR, "cache lookup failed for relation %u", relId);
   18452             : 
   18453        2052 :     truncate_check_rel(relId, (Form_pg_class) GETSTRUCT(tuple));
   18454        2046 :     truncate_check_perms(relId, (Form_pg_class) GETSTRUCT(tuple));
   18455             : 
   18456        2014 :     ReleaseSysCache(tuple);
   18457             : }
   18458             : 
   18459             : /*
   18460             :  * Callback for RangeVarGetRelidExtended().  Checks that the current user is
   18461             :  * the owner of the relation, or superuser.
   18462             :  */
   18463             : void
   18464       15214 : RangeVarCallbackOwnsRelation(const RangeVar *relation,
   18465             :                              Oid relId, Oid oldRelId, void *arg)
   18466             : {
   18467             :     HeapTuple   tuple;
   18468             : 
   18469             :     /* Nothing to do if the relation was not found. */
   18470       15214 :     if (!OidIsValid(relId))
   18471          14 :         return;
   18472             : 
   18473       15200 :     tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relId));
   18474       15200 :     if (!HeapTupleIsValid(tuple))   /* should not happen */
   18475           0 :         elog(ERROR, "cache lookup failed for relation %u", relId);
   18476             : 
   18477       15200 :     if (!object_ownercheck(RelationRelationId, relId, GetUserId()))
   18478           6 :         aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relId)),
   18479           6 :                        relation->relname);
   18480             : 
   18481       30268 :     if (!allowSystemTableMods &&
   18482       15074 :         IsSystemClass(relId, (Form_pg_class) GETSTRUCT(tuple)))
   18483           2 :         ereport(ERROR,
   18484             :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
   18485             :                  errmsg("permission denied: \"%s\" is a system catalog",
   18486             :                         relation->relname)));
   18487             : 
   18488       15192 :     ReleaseSysCache(tuple);
   18489             : }
   18490             : 
   18491             : /*
   18492             :  * Common RangeVarGetRelid callback for rename, set schema, and alter table
   18493             :  * processing.
   18494             :  */
   18495             : static void
   18496       30728 : RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid, Oid oldrelid,
   18497             :                                  void *arg)
   18498             : {
   18499       30728 :     Node       *stmt = (Node *) arg;
   18500             :     ObjectType  reltype;
   18501             :     HeapTuple   tuple;
   18502             :     Form_pg_class classform;
   18503             :     AclResult   aclresult;
   18504             :     char        relkind;
   18505             : 
   18506       30728 :     tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
   18507       30728 :     if (!HeapTupleIsValid(tuple))
   18508         216 :         return;                 /* concurrently dropped */
   18509       30512 :     classform = (Form_pg_class) GETSTRUCT(tuple);
   18510       30512 :     relkind = classform->relkind;
   18511             : 
   18512             :     /* Must own relation. */
   18513       30512 :     if (!object_ownercheck(RelationRelationId, relid, GetUserId()))
   18514          60 :         aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relid)), rv->relname);
   18515             : 
   18516             :     /* No system table modifications unless explicitly allowed. */
   18517       30452 :     if (!allowSystemTableMods && IsSystemClass(relid, classform))
   18518          28 :         ereport(ERROR,
   18519             :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
   18520             :                  errmsg("permission denied: \"%s\" is a system catalog",
   18521             :                         rv->relname)));
   18522             : 
   18523             :     /*
   18524             :      * Extract the specified relation type from the statement parse tree.
   18525             :      *
   18526             :      * Also, for ALTER .. RENAME, check permissions: the user must (still)
   18527             :      * have CREATE rights on the containing namespace.
   18528             :      */
   18529       30424 :     if (IsA(stmt, RenameStmt))
   18530             :     {
   18531         496 :         aclresult = object_aclcheck(NamespaceRelationId, classform->relnamespace,
   18532             :                                     GetUserId(), ACL_CREATE);
   18533         496 :         if (aclresult != ACLCHECK_OK)
   18534           0 :             aclcheck_error(aclresult, OBJECT_SCHEMA,
   18535           0 :                            get_namespace_name(classform->relnamespace));
   18536         496 :         reltype = ((RenameStmt *) stmt)->renameType;
   18537             :     }
   18538       29928 :     else if (IsA(stmt, AlterObjectSchemaStmt))
   18539          90 :         reltype = ((AlterObjectSchemaStmt *) stmt)->objectType;
   18540             : 
   18541       29838 :     else if (IsA(stmt, AlterTableStmt))
   18542       29838 :         reltype = ((AlterTableStmt *) stmt)->objtype;
   18543             :     else
   18544             :     {
   18545           0 :         elog(ERROR, "unrecognized node type: %d", (int) nodeTag(stmt));
   18546             :         reltype = OBJECT_TABLE; /* placate compiler */
   18547             :     }
   18548             : 
   18549             :     /*
   18550             :      * For compatibility with prior releases, we allow ALTER TABLE to be used
   18551             :      * with most other types of relations (but not composite types). We allow
   18552             :      * similar flexibility for ALTER INDEX in the case of RENAME, but not
   18553             :      * otherwise.  Otherwise, the user must select the correct form of the
   18554             :      * command for the relation at issue.
   18555             :      */
   18556       30424 :     if (reltype == OBJECT_SEQUENCE && relkind != RELKIND_SEQUENCE)
   18557           0 :         ereport(ERROR,
   18558             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   18559             :                  errmsg("\"%s\" is not a sequence", rv->relname)));
   18560             : 
   18561       30424 :     if (reltype == OBJECT_VIEW && relkind != RELKIND_VIEW)
   18562           0 :         ereport(ERROR,
   18563             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   18564             :                  errmsg("\"%s\" is not a view", rv->relname)));
   18565             : 
   18566       30424 :     if (reltype == OBJECT_MATVIEW && relkind != RELKIND_MATVIEW)
   18567           0 :         ereport(ERROR,
   18568             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   18569             :                  errmsg("\"%s\" is not a materialized view", rv->relname)));
   18570             : 
   18571       30424 :     if (reltype == OBJECT_FOREIGN_TABLE && relkind != RELKIND_FOREIGN_TABLE)
   18572           0 :         ereport(ERROR,
   18573             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   18574             :                  errmsg("\"%s\" is not a foreign table", rv->relname)));
   18575             : 
   18576       30424 :     if (reltype == OBJECT_TYPE && relkind != RELKIND_COMPOSITE_TYPE)
   18577           0 :         ereport(ERROR,
   18578             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   18579             :                  errmsg("\"%s\" is not a composite type", rv->relname)));
   18580             : 
   18581       30424 :     if (reltype == OBJECT_INDEX && relkind != RELKIND_INDEX &&
   18582             :         relkind != RELKIND_PARTITIONED_INDEX
   18583          38 :         && !IsA(stmt, RenameStmt))
   18584           6 :         ereport(ERROR,
   18585             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   18586             :                  errmsg("\"%s\" is not an index", rv->relname)));
   18587             : 
   18588             :     /*
   18589             :      * Don't allow ALTER TABLE on composite types. We want people to use ALTER
   18590             :      * TYPE for that.
   18591             :      */
   18592       30418 :     if (reltype != OBJECT_TYPE && relkind == RELKIND_COMPOSITE_TYPE)
   18593           0 :         ereport(ERROR,
   18594             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   18595             :                  errmsg("\"%s\" is a composite type", rv->relname),
   18596             :         /* translator: %s is an SQL ALTER command */
   18597             :                  errhint("Use %s instead.",
   18598             :                          "ALTER TYPE")));
   18599             : 
   18600             :     /*
   18601             :      * Don't allow ALTER TABLE .. SET SCHEMA on relations that can't be moved
   18602             :      * to a different schema, such as indexes and TOAST tables.
   18603             :      */
   18604       30418 :     if (IsA(stmt, AlterObjectSchemaStmt))
   18605             :     {
   18606          90 :         if (relkind == RELKIND_INDEX || relkind == RELKIND_PARTITIONED_INDEX)
   18607           0 :             ereport(ERROR,
   18608             :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   18609             :                      errmsg("cannot change schema of index \"%s\"",
   18610             :                             rv->relname),
   18611             :                      errhint("Change the schema of the table instead.")));
   18612          90 :         else if (relkind == RELKIND_COMPOSITE_TYPE)
   18613           0 :             ereport(ERROR,
   18614             :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   18615             :                      errmsg("cannot change schema of composite type \"%s\"",
   18616             :                             rv->relname),
   18617             :             /* translator: %s is an SQL ALTER command */
   18618             :                      errhint("Use %s instead.",
   18619             :                              "ALTER TYPE")));
   18620          90 :         else if (relkind == RELKIND_TOASTVALUE)
   18621           0 :             ereport(ERROR,
   18622             :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   18623             :                      errmsg("cannot change schema of TOAST table \"%s\"",
   18624             :                             rv->relname),
   18625             :                      errhint("Change the schema of the table instead.")));
   18626             :     }
   18627             : 
   18628       30418 :     ReleaseSysCache(tuple);
   18629             : }
   18630             : 
   18631             : /*
   18632             :  * Transform any expressions present in the partition key
   18633             :  *
   18634             :  * Returns a transformed PartitionSpec.
   18635             :  */
   18636             : static PartitionSpec *
   18637        4766 : transformPartitionSpec(Relation rel, PartitionSpec *partspec)
   18638             : {
   18639             :     PartitionSpec *newspec;
   18640             :     ParseState *pstate;
   18641             :     ParseNamespaceItem *nsitem;
   18642             :     ListCell   *l;
   18643             : 
   18644        4766 :     newspec = makeNode(PartitionSpec);
   18645             : 
   18646        4766 :     newspec->strategy = partspec->strategy;
   18647        4766 :     newspec->partParams = NIL;
   18648        4766 :     newspec->location = partspec->location;
   18649             : 
   18650             :     /* Check valid number of columns for strategy */
   18651        7190 :     if (partspec->strategy == PARTITION_STRATEGY_LIST &&
   18652        2424 :         list_length(partspec->partParams) != 1)
   18653           6 :         ereport(ERROR,
   18654             :                 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
   18655             :                  errmsg("cannot use \"list\" partition strategy with more than one column")));
   18656             : 
   18657             :     /*
   18658             :      * Create a dummy ParseState and insert the target relation as its sole
   18659             :      * rangetable entry.  We need a ParseState for transformExpr.
   18660             :      */
   18661        4760 :     pstate = make_parsestate(NULL);
   18662        4760 :     nsitem = addRangeTableEntryForRelation(pstate, rel, AccessShareLock,
   18663             :                                            NULL, false, true);
   18664        4760 :     addNSItemToQuery(pstate, nsitem, true, true, true);
   18665             : 
   18666             :     /* take care of any partition expressions */
   18667        9928 :     foreach(l, partspec->partParams)
   18668             :     {
   18669        5192 :         PartitionElem *pelem = lfirst_node(PartitionElem, l);
   18670             : 
   18671        5192 :         if (pelem->expr)
   18672             :         {
   18673             :             /* Copy, to avoid scribbling on the input */
   18674         298 :             pelem = copyObject(pelem);
   18675             : 
   18676             :             /* Now do parse transformation of the expression */
   18677         298 :             pelem->expr = transformExpr(pstate, pelem->expr,
   18678             :                                         EXPR_KIND_PARTITION_EXPRESSION);
   18679             : 
   18680             :             /* we have to fix its collations too */
   18681         274 :             assign_expr_collations(pstate, pelem->expr);
   18682             :         }
   18683             : 
   18684        5168 :         newspec->partParams = lappend(newspec->partParams, pelem);
   18685             :     }
   18686             : 
   18687        4736 :     return newspec;
   18688             : }
   18689             : 
   18690             : /*
   18691             :  * Compute per-partition-column information from a list of PartitionElems.
   18692             :  * Expressions in the PartitionElems must be parse-analyzed already.
   18693             :  */
   18694             : static void
   18695        4736 : ComputePartitionAttrs(ParseState *pstate, Relation rel, List *partParams, AttrNumber *partattrs,
   18696             :                       List **partexprs, Oid *partopclass, Oid *partcollation,
   18697             :                       PartitionStrategy strategy)
   18698             : {
   18699             :     int         attn;
   18700             :     ListCell   *lc;
   18701             :     Oid         am_oid;
   18702             : 
   18703        4736 :     attn = 0;
   18704        9820 :     foreach(lc, partParams)
   18705             :     {
   18706        5168 :         PartitionElem *pelem = lfirst_node(PartitionElem, lc);
   18707             :         Oid         atttype;
   18708             :         Oid         attcollation;
   18709             : 
   18710        5168 :         if (pelem->name != NULL)
   18711             :         {
   18712             :             /* Simple attribute reference */
   18713             :             HeapTuple   atttuple;
   18714             :             Form_pg_attribute attform;
   18715             : 
   18716        4894 :             atttuple = SearchSysCacheAttName(RelationGetRelid(rel),
   18717        4894 :                                              pelem->name);
   18718        4894 :             if (!HeapTupleIsValid(atttuple))
   18719          12 :                 ereport(ERROR,
   18720             :                         (errcode(ERRCODE_UNDEFINED_COLUMN),
   18721             :                          errmsg("column \"%s\" named in partition key does not exist",
   18722             :                                 pelem->name),
   18723             :                          parser_errposition(pstate, pelem->location)));
   18724        4882 :             attform = (Form_pg_attribute) GETSTRUCT(atttuple);
   18725             : 
   18726        4882 :             if (attform->attnum <= 0)
   18727           6 :                 ereport(ERROR,
   18728             :                         (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
   18729             :                          errmsg("cannot use system column \"%s\" in partition key",
   18730             :                                 pelem->name),
   18731             :                          parser_errposition(pstate, pelem->location)));
   18732             : 
   18733             :             /*
   18734             :              * Generated columns cannot work: They are computed after BEFORE
   18735             :              * triggers, but partition routing is done before all triggers.
   18736             :              */
   18737        4876 :             if (attform->attgenerated)
   18738           6 :                 ereport(ERROR,
   18739             :                         (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
   18740             :                          errmsg("cannot use generated column in partition key"),
   18741             :                          errdetail("Column \"%s\" is a generated column.",
   18742             :                                    pelem->name),
   18743             :                          parser_errposition(pstate, pelem->location)));
   18744             : 
   18745        4870 :             partattrs[attn] = attform->attnum;
   18746        4870 :             atttype = attform->atttypid;
   18747        4870 :             attcollation = attform->attcollation;
   18748        4870 :             ReleaseSysCache(atttuple);
   18749             :         }
   18750             :         else
   18751             :         {
   18752             :             /* Expression */
   18753         274 :             Node       *expr = pelem->expr;
   18754             :             char        partattname[16];
   18755             : 
   18756             :             Assert(expr != NULL);
   18757         274 :             atttype = exprType(expr);
   18758         274 :             attcollation = exprCollation(expr);
   18759             : 
   18760             :             /*
   18761             :              * The expression must be of a storable type (e.g., not RECORD).
   18762             :              * The test is the same as for whether a table column is of a safe
   18763             :              * type (which is why we needn't check for the non-expression
   18764             :              * case).
   18765             :              */
   18766         274 :             snprintf(partattname, sizeof(partattname), "%d", attn + 1);
   18767         274 :             CheckAttributeType(partattname,
   18768             :                                atttype, attcollation,
   18769             :                                NIL, CHKATYPE_IS_PARTKEY);
   18770             : 
   18771             :             /*
   18772             :              * Strip any top-level COLLATE clause.  This ensures that we treat
   18773             :              * "x COLLATE y" and "(x COLLATE y)" alike.
   18774             :              */
   18775         262 :             while (IsA(expr, CollateExpr))
   18776           0 :                 expr = (Node *) ((CollateExpr *) expr)->arg;
   18777             : 
   18778         262 :             if (IsA(expr, Var) &&
   18779          12 :                 ((Var *) expr)->varattno > 0)
   18780             :             {
   18781             :                 /*
   18782             :                  * User wrote "(column)" or "(column COLLATE something)".
   18783             :                  * Treat it like simple attribute anyway.
   18784             :                  */
   18785           6 :                 partattrs[attn] = ((Var *) expr)->varattno;
   18786             :             }
   18787             :             else
   18788             :             {
   18789         256 :                 Bitmapset  *expr_attrs = NULL;
   18790             :                 int         i;
   18791             : 
   18792         256 :                 partattrs[attn] = 0;    /* marks the column as expression */
   18793         256 :                 *partexprs = lappend(*partexprs, expr);
   18794             : 
   18795             :                 /*
   18796             :                  * transformPartitionSpec() should have already rejected
   18797             :                  * subqueries, aggregates, window functions, and SRFs, based
   18798             :                  * on the EXPR_KIND_ for partition expressions.
   18799             :                  */
   18800             : 
   18801             :                 /*
   18802             :                  * Cannot allow system column references, since that would
   18803             :                  * make partition routing impossible: their values won't be
   18804             :                  * known yet when we need to do that.
   18805             :                  */
   18806         256 :                 pull_varattnos(expr, 1, &expr_attrs);
   18807        2048 :                 for (i = FirstLowInvalidHeapAttributeNumber; i < 0; i++)
   18808             :                 {
   18809        1792 :                     if (bms_is_member(i - FirstLowInvalidHeapAttributeNumber,
   18810             :                                       expr_attrs))
   18811           0 :                         ereport(ERROR,
   18812             :                                 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
   18813             :                                  errmsg("partition key expressions cannot contain system column references")));
   18814             :                 }
   18815             : 
   18816             :                 /*
   18817             :                  * Generated columns cannot work: They are computed after
   18818             :                  * BEFORE triggers, but partition routing is done before all
   18819             :                  * triggers.
   18820             :                  */
   18821         256 :                 i = -1;
   18822         564 :                 while ((i = bms_next_member(expr_attrs, i)) >= 0)
   18823             :                 {
   18824         314 :                     AttrNumber  attno = i + FirstLowInvalidHeapAttributeNumber;
   18825             : 
   18826         314 :                     if (attno > 0 &&
   18827         308 :                         TupleDescAttr(RelationGetDescr(rel), attno - 1)->attgenerated)
   18828           6 :                         ereport(ERROR,
   18829             :                                 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
   18830             :                                  errmsg("cannot use generated column in partition key"),
   18831             :                                  errdetail("Column \"%s\" is a generated column.",
   18832             :                                            get_attname(RelationGetRelid(rel), attno, false)),
   18833             :                                  parser_errposition(pstate, pelem->location)));
   18834             :                 }
   18835             : 
   18836             :                 /*
   18837             :                  * Preprocess the expression before checking for mutability.
   18838             :                  * This is essential for the reasons described in
   18839             :                  * contain_mutable_functions_after_planning.  However, we call
   18840             :                  * expression_planner for ourselves rather than using that
   18841             :                  * function, because if constant-folding reduces the
   18842             :                  * expression to a constant, we'd like to know that so we can
   18843             :                  * complain below.
   18844             :                  *
   18845             :                  * Like contain_mutable_functions_after_planning, assume that
   18846             :                  * expression_planner won't scribble on its input, so this
   18847             :                  * won't affect the partexprs entry we saved above.
   18848             :                  */
   18849         250 :                 expr = (Node *) expression_planner((Expr *) expr);
   18850             : 
   18851             :                 /*
   18852             :                  * Partition expressions cannot contain mutable functions,
   18853             :                  * because a given row must always map to the same partition
   18854             :                  * as long as there is no change in the partition boundary
   18855             :                  * structure.
   18856             :                  */
   18857         250 :                 if (contain_mutable_functions(expr))
   18858           6 :                     ereport(ERROR,
   18859             :                             (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
   18860             :                              errmsg("functions in partition key expression must be marked IMMUTABLE")));
   18861             : 
   18862             :                 /*
   18863             :                  * While it is not exactly *wrong* for a partition expression
   18864             :                  * to be a constant, it seems better to reject such keys.
   18865             :                  */
   18866         244 :                 if (IsA(expr, Const))
   18867          12 :                     ereport(ERROR,
   18868             :                             (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
   18869             :                              errmsg("cannot use constant expression as partition key")));
   18870             :             }
   18871             :         }
   18872             : 
   18873             :         /*
   18874             :          * Apply collation override if any
   18875             :          */
   18876        5108 :         if (pelem->collation)
   18877          54 :             attcollation = get_collation_oid(pelem->collation, false);
   18878             : 
   18879             :         /*
   18880             :          * Check we have a collation iff it's a collatable type.  The only
   18881             :          * expected failures here are (1) COLLATE applied to a noncollatable
   18882             :          * type, or (2) partition expression had an unresolved collation. But
   18883             :          * we might as well code this to be a complete consistency check.
   18884             :          */
   18885        5108 :         if (type_is_collatable(atttype))
   18886             :         {
   18887         596 :             if (!OidIsValid(attcollation))
   18888           0 :                 ereport(ERROR,
   18889             :                         (errcode(ERRCODE_INDETERMINATE_COLLATION),
   18890             :                          errmsg("could not determine which collation to use for partition expression"),
   18891             :                          errhint("Use the COLLATE clause to set the collation explicitly.")));
   18892             :         }
   18893             :         else
   18894             :         {
   18895        4512 :             if (OidIsValid(attcollation))
   18896           0 :                 ereport(ERROR,
   18897             :                         (errcode(ERRCODE_DATATYPE_MISMATCH),
   18898             :                          errmsg("collations are not supported by type %s",
   18899             :                                 format_type_be(atttype))));
   18900             :         }
   18901             : 
   18902        5108 :         partcollation[attn] = attcollation;
   18903             : 
   18904             :         /*
   18905             :          * Identify the appropriate operator class.  For list and range
   18906             :          * partitioning, we use a btree operator class; hash partitioning uses
   18907             :          * a hash operator class.
   18908             :          */
   18909        5108 :         if (strategy == PARTITION_STRATEGY_HASH)
   18910         294 :             am_oid = HASH_AM_OID;
   18911             :         else
   18912        4814 :             am_oid = BTREE_AM_OID;
   18913             : 
   18914        5108 :         if (!pelem->opclass)
   18915             :         {
   18916        4976 :             partopclass[attn] = GetDefaultOpClass(atttype, am_oid);
   18917             : 
   18918        4976 :             if (!OidIsValid(partopclass[attn]))
   18919             :             {
   18920          12 :                 if (strategy == PARTITION_STRATEGY_HASH)
   18921           0 :                     ereport(ERROR,
   18922             :                             (errcode(ERRCODE_UNDEFINED_OBJECT),
   18923             :                              errmsg("data type %s has no default operator class for access method \"%s\"",
   18924             :                                     format_type_be(atttype), "hash"),
   18925             :                              errhint("You must specify a hash operator class or define a default hash operator class for the data type.")));
   18926             :                 else
   18927          12 :                     ereport(ERROR,
   18928             :                             (errcode(ERRCODE_UNDEFINED_OBJECT),
   18929             :                              errmsg("data type %s has no default operator class for access method \"%s\"",
   18930             :                                     format_type_be(atttype), "btree"),
   18931             :                              errhint("You must specify a btree operator class or define a default btree operator class for the data type.")));
   18932             :             }
   18933             :         }
   18934             :         else
   18935         132 :             partopclass[attn] = ResolveOpClass(pelem->opclass,
   18936             :                                                atttype,
   18937             :                                                am_oid == HASH_AM_OID ? "hash" : "btree",
   18938             :                                                am_oid);
   18939             : 
   18940        5084 :         attn++;
   18941             :     }
   18942        4652 : }
   18943             : 
   18944             : /*
   18945             :  * PartConstraintImpliedByRelConstraint
   18946             :  *      Do scanrel's existing constraints imply the partition constraint?
   18947             :  *
   18948             :  * "Existing constraints" include its check constraints and column-level
   18949             :  * not-null constraints.  partConstraint describes the partition constraint,
   18950             :  * in implicit-AND form.
   18951             :  */
   18952             : bool
   18953        3032 : PartConstraintImpliedByRelConstraint(Relation scanrel,
   18954             :                                      List *partConstraint)
   18955             : {
   18956        3032 :     List       *existConstraint = NIL;
   18957        3032 :     TupleConstr *constr = RelationGetDescr(scanrel)->constr;
   18958             :     int         i;
   18959             : 
   18960        3032 :     if (constr && constr->has_not_null)
   18961             :     {
   18962         754 :         int         natts = scanrel->rd_att->natts;
   18963             : 
   18964        2440 :         for (i = 1; i <= natts; i++)
   18965             :         {
   18966        1686 :             Form_pg_attribute att = TupleDescAttr(scanrel->rd_att, i - 1);
   18967             : 
   18968        1686 :             if (att->attnotnull && !att->attisdropped)
   18969             :             {
   18970        1058 :                 NullTest   *ntest = makeNode(NullTest);
   18971             : 
   18972        1058 :                 ntest->arg = (Expr *) makeVar(1,
   18973             :                                               i,
   18974             :                                               att->atttypid,
   18975             :                                               att->atttypmod,
   18976             :                                               att->attcollation,
   18977             :                                               0);
   18978        1058 :                 ntest->nulltesttype = IS_NOT_NULL;
   18979             : 
   18980             :                 /*
   18981             :                  * argisrow=false is correct even for a composite column,
   18982             :                  * because attnotnull does not represent a SQL-spec IS NOT
   18983             :                  * NULL test in such a case, just IS DISTINCT FROM NULL.
   18984             :                  */
   18985        1058 :                 ntest->argisrow = false;
   18986        1058 :                 ntest->location = -1;
   18987        1058 :                 existConstraint = lappend(existConstraint, ntest);
   18988             :             }
   18989             :         }
   18990             :     }
   18991             : 
   18992        3032 :     return ConstraintImpliedByRelConstraint(scanrel, partConstraint, existConstraint);
   18993             : }
   18994             : 
   18995             : /*
   18996             :  * ConstraintImpliedByRelConstraint
   18997             :  *      Do scanrel's existing constraints imply the given constraint?
   18998             :  *
   18999             :  * testConstraint is the constraint to validate. provenConstraint is a
   19000             :  * caller-provided list of conditions which this function may assume
   19001             :  * to be true. Both provenConstraint and testConstraint must be in
   19002             :  * implicit-AND form, must only contain immutable clauses, and must
   19003             :  * contain only Vars with varno = 1.
   19004             :  */
   19005             : bool
   19006        4180 : ConstraintImpliedByRelConstraint(Relation scanrel, List *testConstraint, List *provenConstraint)
   19007             : {
   19008        4180 :     List       *existConstraint = list_copy(provenConstraint);
   19009        4180 :     TupleConstr *constr = RelationGetDescr(scanrel)->constr;
   19010             :     int         num_check,
   19011             :                 i;
   19012             : 
   19013        4180 :     num_check = (constr != NULL) ? constr->num_check : 0;
   19014        4696 :     for (i = 0; i < num_check; i++)
   19015             :     {
   19016             :         Node       *cexpr;
   19017             : 
   19018             :         /*
   19019             :          * If this constraint hasn't been fully validated yet, we must ignore
   19020             :          * it here.
   19021             :          */
   19022         516 :         if (!constr->check[i].ccvalid)
   19023           6 :             continue;
   19024             : 
   19025             :         /*
   19026             :          * NOT ENFORCED constraints are always marked as invalid, which should
   19027             :          * have been ignored.
   19028             :          */
   19029             :         Assert(constr->check[i].ccenforced);
   19030             : 
   19031         510 :         cexpr = stringToNode(constr->check[i].ccbin);
   19032             : 
   19033             :         /*
   19034             :          * Run each expression through const-simplification and
   19035             :          * canonicalization.  It is necessary, because we will be comparing it
   19036             :          * to similarly-processed partition constraint expressions, and may
   19037             :          * fail to detect valid matches without this.
   19038             :          */
   19039         510 :         cexpr = eval_const_expressions(NULL, cexpr);
   19040         510 :         cexpr = (Node *) canonicalize_qual((Expr *) cexpr, true);
   19041             : 
   19042         510 :         existConstraint = list_concat(existConstraint,
   19043         510 :                                       make_ands_implicit((Expr *) cexpr));
   19044             :     }
   19045             : 
   19046             :     /*
   19047             :      * Try to make the proof.  Since we are comparing CHECK constraints, we
   19048             :      * need to use weak implication, i.e., we assume existConstraint is
   19049             :      * not-false and try to prove the same for testConstraint.
   19050             :      *
   19051             :      * Note that predicate_implied_by assumes its first argument is known
   19052             :      * immutable.  That should always be true for both NOT NULL and partition
   19053             :      * constraints, so we don't test it here.
   19054             :      */
   19055        4180 :     return predicate_implied_by(testConstraint, existConstraint, true);
   19056             : }
   19057             : 
   19058             : /*
   19059             :  * QueuePartitionConstraintValidation
   19060             :  *
   19061             :  * Add an entry to wqueue to have the given partition constraint validated by
   19062             :  * Phase 3, for the given relation, and all its children.
   19063             :  *
   19064             :  * We first verify whether the given constraint is implied by pre-existing
   19065             :  * relation constraints; if it is, there's no need to scan the table to
   19066             :  * validate, so don't queue in that case.
   19067             :  */
   19068             : static void
   19069        2402 : QueuePartitionConstraintValidation(List **wqueue, Relation scanrel,
   19070             :                                    List *partConstraint,
   19071             :                                    bool validate_default)
   19072             : {
   19073             :     /*
   19074             :      * Based on the table's existing constraints, determine whether or not we
   19075             :      * may skip scanning the table.
   19076             :      */
   19077        2402 :     if (PartConstraintImpliedByRelConstraint(scanrel, partConstraint))
   19078             :     {
   19079          90 :         if (!validate_default)
   19080          68 :             ereport(DEBUG1,
   19081             :                     (errmsg_internal("partition constraint for table \"%s\" is implied by existing constraints",
   19082             :                                      RelationGetRelationName(scanrel))));
   19083             :         else
   19084          22 :             ereport(DEBUG1,
   19085             :                     (errmsg_internal("updated partition constraint for default partition \"%s\" is implied by existing constraints",
   19086             :                                      RelationGetRelationName(scanrel))));
   19087          90 :         return;
   19088             :     }
   19089             : 
   19090             :     /*
   19091             :      * Constraints proved insufficient. For plain relations, queue a
   19092             :      * validation item now; for partitioned tables, recurse to process each
   19093             :      * partition.
   19094             :      */
   19095        2312 :     if (scanrel->rd_rel->relkind == RELKIND_RELATION)
   19096             :     {
   19097             :         AlteredTableInfo *tab;
   19098             : 
   19099             :         /* Grab a work queue entry. */
   19100        1924 :         tab = ATGetQueueEntry(wqueue, scanrel);
   19101             :         Assert(tab->partition_constraint == NULL);
   19102        1924 :         tab->partition_constraint = (Expr *) linitial(partConstraint);
   19103        1924 :         tab->validate_default = validate_default;
   19104             :     }
   19105         388 :     else if (scanrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
   19106             :     {
   19107         340 :         PartitionDesc partdesc = RelationGetPartitionDesc(scanrel, true);
   19108             :         int         i;
   19109             : 
   19110         710 :         for (i = 0; i < partdesc->nparts; i++)
   19111             :         {
   19112             :             Relation    part_rel;
   19113             :             List       *thisPartConstraint;
   19114             : 
   19115             :             /*
   19116             :              * This is the minimum lock we need to prevent deadlocks.
   19117             :              */
   19118         370 :             part_rel = table_open(partdesc->oids[i], AccessExclusiveLock);
   19119             : 
   19120             :             /*
   19121             :              * Adjust the constraint for scanrel so that it matches this
   19122             :              * partition's attribute numbers.
   19123             :              */
   19124             :             thisPartConstraint =
   19125         370 :                 map_partition_varattnos(partConstraint, 1,
   19126             :                                         part_rel, scanrel);
   19127             : 
   19128         370 :             QueuePartitionConstraintValidation(wqueue, part_rel,
   19129             :                                                thisPartConstraint,
   19130             :                                                validate_default);
   19131         370 :             table_close(part_rel, NoLock);  /* keep lock till commit */
   19132             :         }
   19133             :     }
   19134             : }
   19135             : 
   19136             : /*
   19137             :  * ALTER TABLE <name> ATTACH PARTITION <partition-name> FOR VALUES
   19138             :  *
   19139             :  * Return the address of the newly attached partition.
   19140             :  */
   19141             : static ObjectAddress
   19142        2204 : ATExecAttachPartition(List **wqueue, Relation rel, PartitionCmd *cmd,
   19143             :                       AlterTableUtilityContext *context)
   19144             : {
   19145             :     Relation    attachrel,
   19146             :                 catalog;
   19147             :     List       *attachrel_children;
   19148             :     List       *partConstraint;
   19149             :     SysScanDesc scan;
   19150             :     ScanKeyData skey;
   19151             :     AttrNumber  attno;
   19152             :     int         natts;
   19153             :     TupleDesc   tupleDesc;
   19154             :     ObjectAddress address;
   19155             :     const char *trigger_name;
   19156             :     Oid         defaultPartOid;
   19157             :     List       *partBoundConstraint;
   19158        2204 :     ParseState *pstate = make_parsestate(NULL);
   19159             : 
   19160        2204 :     pstate->p_sourcetext = context->queryString;
   19161             : 
   19162             :     /*
   19163             :      * We must lock the default partition if one exists, because attaching a
   19164             :      * new partition will change its partition constraint.
   19165             :      */
   19166             :     defaultPartOid =
   19167        2204 :         get_default_oid_from_partdesc(RelationGetPartitionDesc(rel, true));
   19168        2204 :     if (OidIsValid(defaultPartOid))
   19169         182 :         LockRelationOid(defaultPartOid, AccessExclusiveLock);
   19170             : 
   19171        2204 :     attachrel = table_openrv(cmd->name, AccessExclusiveLock);
   19172             : 
   19173             :     /*
   19174             :      * XXX I think it'd be a good idea to grab locks on all tables referenced
   19175             :      * by FKs at this point also.
   19176             :      */
   19177             : 
   19178             :     /*
   19179             :      * Must be owner of both parent and source table -- parent was checked by
   19180             :      * ATSimplePermissions call in ATPrepCmd
   19181             :      */
   19182        2198 :     ATSimplePermissions(AT_AttachPartition, attachrel,
   19183             :                         ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
   19184             : 
   19185             :     /* A partition can only have one parent */
   19186        2192 :     if (attachrel->rd_rel->relispartition)
   19187           6 :         ereport(ERROR,
   19188             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   19189             :                  errmsg("\"%s\" is already a partition",
   19190             :                         RelationGetRelationName(attachrel))));
   19191             : 
   19192        2186 :     if (OidIsValid(attachrel->rd_rel->reloftype))
   19193           6 :         ereport(ERROR,
   19194             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   19195             :                  errmsg("cannot attach a typed table as partition")));
   19196             : 
   19197             :     /*
   19198             :      * Table being attached should not already be part of inheritance; either
   19199             :      * as a child table...
   19200             :      */
   19201        2180 :     catalog = table_open(InheritsRelationId, AccessShareLock);
   19202        2180 :     ScanKeyInit(&skey,
   19203             :                 Anum_pg_inherits_inhrelid,
   19204             :                 BTEqualStrategyNumber, F_OIDEQ,
   19205             :                 ObjectIdGetDatum(RelationGetRelid(attachrel)));
   19206        2180 :     scan = systable_beginscan(catalog, InheritsRelidSeqnoIndexId, true,
   19207             :                               NULL, 1, &skey);
   19208        2180 :     if (HeapTupleIsValid(systable_getnext(scan)))
   19209           6 :         ereport(ERROR,
   19210             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   19211             :                  errmsg("cannot attach inheritance child as partition")));
   19212        2174 :     systable_endscan(scan);
   19213             : 
   19214             :     /* ...or as a parent table (except the case when it is partitioned) */
   19215        2174 :     ScanKeyInit(&skey,
   19216             :                 Anum_pg_inherits_inhparent,
   19217             :                 BTEqualStrategyNumber, F_OIDEQ,
   19218             :                 ObjectIdGetDatum(RelationGetRelid(attachrel)));
   19219        2174 :     scan = systable_beginscan(catalog, InheritsParentIndexId, true, NULL,
   19220             :                               1, &skey);
   19221        2174 :     if (HeapTupleIsValid(systable_getnext(scan)) &&
   19222         248 :         attachrel->rd_rel->relkind == RELKIND_RELATION)
   19223           6 :         ereport(ERROR,
   19224             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   19225             :                  errmsg("cannot attach inheritance parent as partition")));
   19226        2168 :     systable_endscan(scan);
   19227        2168 :     table_close(catalog, AccessShareLock);
   19228             : 
   19229             :     /*
   19230             :      * Prevent circularity by seeing if rel is a partition of attachrel. (In
   19231             :      * particular, this disallows making a rel a partition of itself.)
   19232             :      *
   19233             :      * We do that by checking if rel is a member of the list of attachrel's
   19234             :      * partitions provided the latter is partitioned at all.  We want to avoid
   19235             :      * having to construct this list again, so we request the strongest lock
   19236             :      * on all partitions.  We need the strongest lock, because we may decide
   19237             :      * to scan them if we find out that the table being attached (or its leaf
   19238             :      * partitions) may contain rows that violate the partition constraint. If
   19239             :      * the table has a constraint that would prevent such rows, which by
   19240             :      * definition is present in all the partitions, we need not scan the
   19241             :      * table, nor its partitions.  But we cannot risk a deadlock by taking a
   19242             :      * weaker lock now and the stronger one only when needed.
   19243             :      */
   19244        2168 :     attachrel_children = find_all_inheritors(RelationGetRelid(attachrel),
   19245             :                                              AccessExclusiveLock, NULL);
   19246        2168 :     if (list_member_oid(attachrel_children, RelationGetRelid(rel)))
   19247          12 :         ereport(ERROR,
   19248             :                 (errcode(ERRCODE_DUPLICATE_TABLE),
   19249             :                  errmsg("circular inheritance not allowed"),
   19250             :                  errdetail("\"%s\" is already a child of \"%s\".",
   19251             :                            RelationGetRelationName(rel),
   19252             :                            RelationGetRelationName(attachrel))));
   19253             : 
   19254             :     /* If the parent is permanent, so must be all of its partitions. */
   19255        2156 :     if (rel->rd_rel->relpersistence != RELPERSISTENCE_TEMP &&
   19256        2114 :         attachrel->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
   19257           6 :         ereport(ERROR,
   19258             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   19259             :                  errmsg("cannot attach a temporary relation as partition of permanent relation \"%s\"",
   19260             :                         RelationGetRelationName(rel))));
   19261             : 
   19262             :     /* Temp parent cannot have a partition that is itself not a temp */
   19263        2150 :     if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
   19264          42 :         attachrel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
   19265          18 :         ereport(ERROR,
   19266             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   19267             :                  errmsg("cannot attach a permanent relation as partition of temporary relation \"%s\"",
   19268             :                         RelationGetRelationName(rel))));
   19269             : 
   19270             :     /* If the parent is temp, it must belong to this session */
   19271        2132 :     if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
   19272          24 :         !rel->rd_islocaltemp)
   19273           0 :         ereport(ERROR,
   19274             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   19275             :                  errmsg("cannot attach as partition of temporary relation of another session")));
   19276             : 
   19277             :     /* Ditto for the partition */
   19278        2132 :     if (attachrel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
   19279          24 :         !attachrel->rd_islocaltemp)
   19280           0 :         ereport(ERROR,
   19281             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   19282             :                  errmsg("cannot attach temporary relation of another session as partition")));
   19283             : 
   19284             :     /*
   19285             :      * Check if attachrel has any identity columns or any columns that aren't
   19286             :      * in the parent.
   19287             :      */
   19288        2132 :     tupleDesc = RelationGetDescr(attachrel);
   19289        2132 :     natts = tupleDesc->natts;
   19290        7302 :     for (attno = 1; attno <= natts; attno++)
   19291             :     {
   19292        5206 :         Form_pg_attribute attribute = TupleDescAttr(tupleDesc, attno - 1);
   19293        5206 :         char       *attributeName = NameStr(attribute->attname);
   19294             : 
   19295             :         /* Ignore dropped */
   19296        5206 :         if (attribute->attisdropped)
   19297         580 :             continue;
   19298             : 
   19299        4626 :         if (attribute->attidentity)
   19300          18 :             ereport(ERROR,
   19301             :                     errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
   19302             :                     errmsg("table \"%s\" being attached contains an identity column \"%s\"",
   19303             :                            RelationGetRelationName(attachrel), attributeName),
   19304             :                     errdetail("The new partition may not contain an identity column."));
   19305             : 
   19306             :         /* Try to find the column in parent (matching on column name) */
   19307        4608 :         if (!SearchSysCacheExists2(ATTNAME,
   19308             :                                    ObjectIdGetDatum(RelationGetRelid(rel)),
   19309             :                                    CStringGetDatum(attributeName)))
   19310          18 :             ereport(ERROR,
   19311             :                     (errcode(ERRCODE_DATATYPE_MISMATCH),
   19312             :                      errmsg("table \"%s\" contains column \"%s\" not found in parent \"%s\"",
   19313             :                             RelationGetRelationName(attachrel), attributeName,
   19314             :                             RelationGetRelationName(rel)),
   19315             :                      errdetail("The new partition may contain only the columns present in parent.")));
   19316             :     }
   19317             : 
   19318             :     /*
   19319             :      * If child_rel has row-level triggers with transition tables, we
   19320             :      * currently don't allow it to become a partition.  See also prohibitions
   19321             :      * in ATExecAddInherit() and CreateTrigger().
   19322             :      */
   19323        2096 :     trigger_name = FindTriggerIncompatibleWithInheritance(attachrel->trigdesc);
   19324        2096 :     if (trigger_name != NULL)
   19325           6 :         ereport(ERROR,
   19326             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   19327             :                  errmsg("trigger \"%s\" prevents table \"%s\" from becoming a partition",
   19328             :                         trigger_name, RelationGetRelationName(attachrel)),
   19329             :                  errdetail("ROW triggers with transition tables are not supported on partitions.")));
   19330             : 
   19331             :     /*
   19332             :      * Check that the new partition's bound is valid and does not overlap any
   19333             :      * of existing partitions of the parent - note that it does not return on
   19334             :      * error.
   19335             :      */
   19336        2090 :     check_new_partition_bound(RelationGetRelationName(attachrel), rel,
   19337             :                               cmd->bound, pstate);
   19338             : 
   19339             :     /* OK to create inheritance.  Rest of the checks performed there */
   19340        2054 :     CreateInheritance(attachrel, rel, true);
   19341             : 
   19342             :     /* Update the pg_class entry. */
   19343        1982 :     StorePartitionBound(attachrel, rel, cmd->bound);
   19344             : 
   19345             :     /* Ensure there exists a correct set of indexes in the partition. */
   19346        1982 :     AttachPartitionEnsureIndexes(wqueue, rel, attachrel);
   19347             : 
   19348             :     /* and triggers */
   19349        1952 :     CloneRowTriggersToPartition(rel, attachrel);
   19350             : 
   19351             :     /*
   19352             :      * Clone foreign key constraints.  Callee is responsible for setting up
   19353             :      * for phase 3 constraint verification.
   19354             :      */
   19355        1946 :     CloneForeignKeyConstraints(wqueue, rel, attachrel);
   19356             : 
   19357             :     /*
   19358             :      * Generate partition constraint from the partition bound specification.
   19359             :      * If the parent itself is a partition, make sure to include its
   19360             :      * constraint as well.
   19361             :      */
   19362        1934 :     partBoundConstraint = get_qual_from_partbound(rel, cmd->bound);
   19363             : 
   19364             :     /*
   19365             :      * Use list_concat_copy() to avoid modifying partBoundConstraint in place,
   19366             :      * since it's needed later to construct the constraint expression for
   19367             :      * validating against the default partition, if any.
   19368             :      */
   19369        1934 :     partConstraint = list_concat_copy(partBoundConstraint,
   19370        1934 :                                       RelationGetPartitionQual(rel));
   19371             : 
   19372             :     /* Skip validation if there are no constraints to validate. */
   19373        1934 :     if (partConstraint)
   19374             :     {
   19375             :         /*
   19376             :          * Run the partition quals through const-simplification similar to
   19377             :          * check constraints.  We skip canonicalize_qual, though, because
   19378             :          * partition quals should be in canonical form already.
   19379             :          */
   19380             :         partConstraint =
   19381        1886 :             (List *) eval_const_expressions(NULL,
   19382             :                                             (Node *) partConstraint);
   19383             : 
   19384             :         /* XXX this sure looks wrong */
   19385        1886 :         partConstraint = list_make1(make_ands_explicit(partConstraint));
   19386             : 
   19387             :         /*
   19388             :          * Adjust the generated constraint to match this partition's attribute
   19389             :          * numbers.
   19390             :          */
   19391        1886 :         partConstraint = map_partition_varattnos(partConstraint, 1, attachrel,
   19392             :                                                  rel);
   19393             : 
   19394             :         /* Validate partition constraints against the table being attached. */
   19395        1886 :         QueuePartitionConstraintValidation(wqueue, attachrel, partConstraint,
   19396             :                                            false);
   19397             :     }
   19398             : 
   19399             :     /*
   19400             :      * If we're attaching a partition other than the default partition and a
   19401             :      * default one exists, then that partition's partition constraint changes,
   19402             :      * so add an entry to the work queue to validate it, too.  (We must not do
   19403             :      * this when the partition being attached is the default one; we already
   19404             :      * did it above!)
   19405             :      */
   19406        1934 :     if (OidIsValid(defaultPartOid))
   19407             :     {
   19408             :         Relation    defaultrel;
   19409             :         List       *defPartConstraint;
   19410             : 
   19411             :         Assert(!cmd->bound->is_default);
   19412             : 
   19413             :         /* we already hold a lock on the default partition */
   19414         146 :         defaultrel = table_open(defaultPartOid, NoLock);
   19415             :         defPartConstraint =
   19416         146 :             get_proposed_default_constraint(partBoundConstraint);
   19417             : 
   19418             :         /*
   19419             :          * Map the Vars in the constraint expression from rel's attnos to
   19420             :          * defaultrel's.
   19421             :          */
   19422             :         defPartConstraint =
   19423         146 :             map_partition_varattnos(defPartConstraint,
   19424             :                                     1, defaultrel, rel);
   19425         146 :         QueuePartitionConstraintValidation(wqueue, defaultrel,
   19426             :                                            defPartConstraint, true);
   19427             : 
   19428             :         /* keep our lock until commit. */
   19429         146 :         table_close(defaultrel, NoLock);
   19430             :     }
   19431             : 
   19432        1934 :     ObjectAddressSet(address, RelationRelationId, RelationGetRelid(attachrel));
   19433             : 
   19434             :     /*
   19435             :      * If the partition we just attached is partitioned itself, invalidate
   19436             :      * relcache for all descendent partitions too to ensure that their
   19437             :      * rd_partcheck expression trees are rebuilt; partitions already locked at
   19438             :      * the beginning of this function.
   19439             :      */
   19440        1934 :     if (attachrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
   19441             :     {
   19442             :         ListCell   *l;
   19443             : 
   19444         960 :         foreach(l, attachrel_children)
   19445             :         {
   19446         644 :             CacheInvalidateRelcacheByRelid(lfirst_oid(l));
   19447             :         }
   19448             :     }
   19449             : 
   19450             :     /* keep our lock until commit */
   19451        1934 :     table_close(attachrel, NoLock);
   19452             : 
   19453        1934 :     return address;
   19454             : }
   19455             : 
   19456             : /*
   19457             :  * AttachPartitionEnsureIndexes
   19458             :  *      subroutine for ATExecAttachPartition to create/match indexes
   19459             :  *
   19460             :  * Enforce the indexing rule for partitioned tables during ALTER TABLE / ATTACH
   19461             :  * PARTITION: every partition must have an index attached to each index on the
   19462             :  * partitioned table.
   19463             :  */
   19464             : static void
   19465        1982 : AttachPartitionEnsureIndexes(List **wqueue, Relation rel, Relation attachrel)
   19466             : {
   19467             :     List       *idxes;
   19468             :     List       *attachRelIdxs;
   19469             :     Relation   *attachrelIdxRels;
   19470             :     IndexInfo **attachInfos;
   19471             :     ListCell   *cell;
   19472             :     MemoryContext cxt;
   19473             :     MemoryContext oldcxt;
   19474             : 
   19475        1982 :     cxt = AllocSetContextCreate(CurrentMemoryContext,
   19476             :                                 "AttachPartitionEnsureIndexes",
   19477             :                                 ALLOCSET_DEFAULT_SIZES);
   19478        1982 :     oldcxt = MemoryContextSwitchTo(cxt);
   19479             : 
   19480        1982 :     idxes = RelationGetIndexList(rel);
   19481        1982 :     attachRelIdxs = RelationGetIndexList(attachrel);
   19482        1982 :     attachrelIdxRels = palloc(sizeof(Relation) * list_length(attachRelIdxs));
   19483        1982 :     attachInfos = palloc(sizeof(IndexInfo *) * list_length(attachRelIdxs));
   19484             : 
   19485             :     /* Build arrays of all existing indexes and their IndexInfos */
   19486        4328 :     foreach_oid(cldIdxId, attachRelIdxs)
   19487             :     {
   19488         364 :         int         i = foreach_current_index(cldIdxId);
   19489             : 
   19490         364 :         attachrelIdxRels[i] = index_open(cldIdxId, AccessShareLock);
   19491         364 :         attachInfos[i] = BuildIndexInfo(attachrelIdxRels[i]);
   19492             :     }
   19493             : 
   19494             :     /*
   19495             :      * If we're attaching a foreign table, we must fail if any of the indexes
   19496             :      * is a constraint index; otherwise, there's nothing to do here.  Do this
   19497             :      * before starting work, to avoid wasting the effort of building a few
   19498             :      * non-unique indexes before coming across a unique one.
   19499             :      */
   19500        1982 :     if (attachrel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
   19501             :     {
   19502          86 :         foreach(cell, idxes)
   19503             :         {
   19504          36 :             Oid         idx = lfirst_oid(cell);
   19505          36 :             Relation    idxRel = index_open(idx, AccessShareLock);
   19506             : 
   19507          36 :             if (idxRel->rd_index->indisunique ||
   19508          24 :                 idxRel->rd_index->indisprimary)
   19509          12 :                 ereport(ERROR,
   19510             :                         (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   19511             :                          errmsg("cannot attach foreign table \"%s\" as partition of partitioned table \"%s\"",
   19512             :                                 RelationGetRelationName(attachrel),
   19513             :                                 RelationGetRelationName(rel)),
   19514             :                          errdetail("Partitioned table \"%s\" contains unique indexes.",
   19515             :                                    RelationGetRelationName(rel))));
   19516          24 :             index_close(idxRel, AccessShareLock);
   19517             :         }
   19518             : 
   19519          50 :         goto out;
   19520             :     }
   19521             : 
   19522             :     /*
   19523             :      * For each index on the partitioned table, find a matching one in the
   19524             :      * partition-to-be; if one is not found, create one.
   19525             :      */
   19526        2336 :     foreach(cell, idxes)
   19527             :     {
   19528         434 :         Oid         idx = lfirst_oid(cell);
   19529         434 :         Relation    idxRel = index_open(idx, AccessShareLock);
   19530             :         IndexInfo  *info;
   19531             :         AttrMap    *attmap;
   19532         434 :         bool        found = false;
   19533             :         Oid         constraintOid;
   19534             : 
   19535             :         /*
   19536             :          * Ignore indexes in the partitioned table other than partitioned
   19537             :          * indexes.
   19538             :          */
   19539         434 :         if (idxRel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
   19540             :         {
   19541           0 :             index_close(idxRel, AccessShareLock);
   19542           0 :             continue;
   19543             :         }
   19544             : 
   19545             :         /* construct an indexinfo to compare existing indexes against */
   19546         434 :         info = BuildIndexInfo(idxRel);
   19547         434 :         attmap = build_attrmap_by_name(RelationGetDescr(attachrel),
   19548             :                                        RelationGetDescr(rel),
   19549             :                                        false);
   19550         434 :         constraintOid = get_relation_idx_constraint_oid(RelationGetRelid(rel), idx);
   19551             : 
   19552             :         /*
   19553             :          * Scan the list of existing indexes in the partition-to-be, and mark
   19554             :          * the first matching, valid, unattached one we find, if any, as
   19555             :          * partition of the parent index.  If we find one, we're done.
   19556             :          */
   19557         494 :         for (int i = 0; i < list_length(attachRelIdxs); i++)
   19558             :         {
   19559         262 :             Oid         cldIdxId = RelationGetRelid(attachrelIdxRels[i]);
   19560         262 :             Oid         cldConstrOid = InvalidOid;
   19561             : 
   19562             :             /* does this index have a parent?  if so, can't use it */
   19563         262 :             if (attachrelIdxRels[i]->rd_rel->relispartition)
   19564          12 :                 continue;
   19565             : 
   19566             :             /* If this index is invalid, can't use it */
   19567         250 :             if (!attachrelIdxRels[i]->rd_index->indisvalid)
   19568           6 :                 continue;
   19569             : 
   19570         244 :             if (CompareIndexInfo(attachInfos[i], info,
   19571         244 :                                  attachrelIdxRels[i]->rd_indcollation,
   19572         244 :                                  idxRel->rd_indcollation,
   19573         244 :                                  attachrelIdxRels[i]->rd_opfamily,
   19574         244 :                                  idxRel->rd_opfamily,
   19575             :                                  attmap))
   19576             :             {
   19577             :                 /*
   19578             :                  * If this index is being created in the parent because of a
   19579             :                  * constraint, then the child needs to have a constraint also,
   19580             :                  * so look for one.  If there is no such constraint, this
   19581             :                  * index is no good, so keep looking.
   19582             :                  */
   19583         208 :                 if (OidIsValid(constraintOid))
   19584             :                 {
   19585             :                     cldConstrOid =
   19586         110 :                         get_relation_idx_constraint_oid(RelationGetRelid(attachrel),
   19587             :                                                         cldIdxId);
   19588             :                     /* no dice */
   19589         110 :                     if (!OidIsValid(cldConstrOid))
   19590           6 :                         continue;
   19591             : 
   19592             :                     /* Ensure they're both the same type of constraint */
   19593         208 :                     if (get_constraint_type(constraintOid) !=
   19594         104 :                         get_constraint_type(cldConstrOid))
   19595           0 :                         continue;
   19596             :                 }
   19597             : 
   19598             :                 /* bingo. */
   19599         202 :                 IndexSetParentIndex(attachrelIdxRels[i], idx);
   19600         202 :                 if (OidIsValid(constraintOid))
   19601         104 :                     ConstraintSetParentConstraint(cldConstrOid, constraintOid,
   19602             :                                                   RelationGetRelid(attachrel));
   19603         202 :                 found = true;
   19604             : 
   19605         202 :                 CommandCounterIncrement();
   19606         202 :                 break;
   19607             :             }
   19608             :         }
   19609             : 
   19610             :         /*
   19611             :          * If no suitable index was found in the partition-to-be, create one
   19612             :          * now.  Note that if this is a PK, not-null constraints must already
   19613             :          * exist.
   19614             :          */
   19615         434 :         if (!found)
   19616             :         {
   19617             :             IndexStmt  *stmt;
   19618             :             Oid         conOid;
   19619             : 
   19620         232 :             stmt = generateClonedIndexStmt(NULL,
   19621             :                                            idxRel, attmap,
   19622             :                                            &conOid);
   19623         232 :             DefineIndex(RelationGetRelid(attachrel), stmt, InvalidOid,
   19624             :                         RelationGetRelid(idxRel),
   19625             :                         conOid,
   19626             :                         -1,
   19627             :                         true, false, false, false, false);
   19628             :         }
   19629             : 
   19630         416 :         index_close(idxRel, AccessShareLock);
   19631             :     }
   19632             : 
   19633        1952 : out:
   19634             :     /* Clean up. */
   19635        2304 :     for (int i = 0; i < list_length(attachRelIdxs); i++)
   19636         352 :         index_close(attachrelIdxRels[i], AccessShareLock);
   19637        1952 :     MemoryContextSwitchTo(oldcxt);
   19638        1952 :     MemoryContextDelete(cxt);
   19639        1952 : }
   19640             : 
   19641             : /*
   19642             :  * CloneRowTriggersToPartition
   19643             :  *      subroutine for ATExecAttachPartition/DefineRelation to create row
   19644             :  *      triggers on partitions
   19645             :  */
   19646             : static void
   19647        2360 : CloneRowTriggersToPartition(Relation parent, Relation partition)
   19648             : {
   19649             :     Relation    pg_trigger;
   19650             :     ScanKeyData key;
   19651             :     SysScanDesc scan;
   19652             :     HeapTuple   tuple;
   19653             :     MemoryContext perTupCxt;
   19654             : 
   19655        2360 :     ScanKeyInit(&key, Anum_pg_trigger_tgrelid, BTEqualStrategyNumber,
   19656             :                 F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(parent)));
   19657        2360 :     pg_trigger = table_open(TriggerRelationId, RowExclusiveLock);
   19658        2360 :     scan = systable_beginscan(pg_trigger, TriggerRelidNameIndexId,
   19659             :                               true, NULL, 1, &key);
   19660             : 
   19661        2360 :     perTupCxt = AllocSetContextCreate(CurrentMemoryContext,
   19662             :                                       "clone trig", ALLOCSET_SMALL_SIZES);
   19663             : 
   19664        3868 :     while (HeapTupleIsValid(tuple = systable_getnext(scan)))
   19665             :     {
   19666        1514 :         Form_pg_trigger trigForm = (Form_pg_trigger) GETSTRUCT(tuple);
   19667             :         CreateTrigStmt *trigStmt;
   19668        1514 :         Node       *qual = NULL;
   19669             :         Datum       value;
   19670             :         bool        isnull;
   19671        1514 :         List       *cols = NIL;
   19672        1514 :         List       *trigargs = NIL;
   19673             :         MemoryContext oldcxt;
   19674             : 
   19675             :         /*
   19676             :          * Ignore statement-level triggers; those are not cloned.
   19677             :          */
   19678        1514 :         if (!TRIGGER_FOR_ROW(trigForm->tgtype))
   19679        1358 :             continue;
   19680             : 
   19681             :         /*
   19682             :          * Don't clone internal triggers, because the constraint cloning code
   19683             :          * will.
   19684             :          */
   19685        1514 :         if (trigForm->tgisinternal)
   19686        1358 :             continue;
   19687             : 
   19688             :         /*
   19689             :          * Complain if we find an unexpected trigger type.
   19690             :          */
   19691         156 :         if (!TRIGGER_FOR_BEFORE(trigForm->tgtype) &&
   19692         138 :             !TRIGGER_FOR_AFTER(trigForm->tgtype))
   19693           0 :             elog(ERROR, "unexpected trigger \"%s\" found",
   19694             :                  NameStr(trigForm->tgname));
   19695             : 
   19696             :         /* Use short-lived context for CREATE TRIGGER */
   19697         156 :         oldcxt = MemoryContextSwitchTo(perTupCxt);
   19698             : 
   19699             :         /*
   19700             :          * If there is a WHEN clause, generate a 'cooked' version of it that's
   19701             :          * appropriate for the partition.
   19702             :          */
   19703         156 :         value = heap_getattr(tuple, Anum_pg_trigger_tgqual,
   19704             :                              RelationGetDescr(pg_trigger), &isnull);
   19705         156 :         if (!isnull)
   19706             :         {
   19707           6 :             qual = stringToNode(TextDatumGetCString(value));
   19708           6 :             qual = (Node *) map_partition_varattnos((List *) qual, PRS2_OLD_VARNO,
   19709             :                                                     partition, parent);
   19710           6 :             qual = (Node *) map_partition_varattnos((List *) qual, PRS2_NEW_VARNO,
   19711             :                                                     partition, parent);
   19712             :         }
   19713             : 
   19714             :         /*
   19715             :          * If there is a column list, transform it to a list of column names.
   19716             :          * Note we don't need to map this list in any way ...
   19717             :          */
   19718         156 :         if (trigForm->tgattr.dim1 > 0)
   19719             :         {
   19720             :             int         i;
   19721             : 
   19722          12 :             for (i = 0; i < trigForm->tgattr.dim1; i++)
   19723             :             {
   19724             :                 Form_pg_attribute col;
   19725             : 
   19726           6 :                 col = TupleDescAttr(parent->rd_att,
   19727           6 :                                     trigForm->tgattr.values[i] - 1);
   19728           6 :                 cols = lappend(cols,
   19729           6 :                                makeString(pstrdup(NameStr(col->attname))));
   19730             :             }
   19731             :         }
   19732             : 
   19733             :         /* Reconstruct trigger arguments list. */
   19734         156 :         if (trigForm->tgnargs > 0)
   19735             :         {
   19736             :             char       *p;
   19737             : 
   19738          12 :             value = heap_getattr(tuple, Anum_pg_trigger_tgargs,
   19739             :                                  RelationGetDescr(pg_trigger), &isnull);
   19740          12 :             if (isnull)
   19741           0 :                 elog(ERROR, "tgargs is null for trigger \"%s\" in partition \"%s\"",
   19742             :                      NameStr(trigForm->tgname), RelationGetRelationName(partition));
   19743             : 
   19744          12 :             p = (char *) VARDATA_ANY(DatumGetByteaPP(value));
   19745             : 
   19746          36 :             for (int i = 0; i < trigForm->tgnargs; i++)
   19747             :             {
   19748          24 :                 trigargs = lappend(trigargs, makeString(pstrdup(p)));
   19749          24 :                 p += strlen(p) + 1;
   19750             :             }
   19751             :         }
   19752             : 
   19753         156 :         trigStmt = makeNode(CreateTrigStmt);
   19754         156 :         trigStmt->replace = false;
   19755         156 :         trigStmt->isconstraint = OidIsValid(trigForm->tgconstraint);
   19756         156 :         trigStmt->trigname = NameStr(trigForm->tgname);
   19757         156 :         trigStmt->relation = NULL;
   19758         156 :         trigStmt->funcname = NULL;   /* passed separately */
   19759         156 :         trigStmt->args = trigargs;
   19760         156 :         trigStmt->row = true;
   19761         156 :         trigStmt->timing = trigForm->tgtype & TRIGGER_TYPE_TIMING_MASK;
   19762         156 :         trigStmt->events = trigForm->tgtype & TRIGGER_TYPE_EVENT_MASK;
   19763         156 :         trigStmt->columns = cols;
   19764         156 :         trigStmt->whenClause = NULL; /* passed separately */
   19765         156 :         trigStmt->transitionRels = NIL; /* not supported at present */
   19766         156 :         trigStmt->deferrable = trigForm->tgdeferrable;
   19767         156 :         trigStmt->initdeferred = trigForm->tginitdeferred;
   19768         156 :         trigStmt->constrrel = NULL; /* passed separately */
   19769             : 
   19770         156 :         CreateTriggerFiringOn(trigStmt, NULL, RelationGetRelid(partition),
   19771             :                               trigForm->tgconstrrelid, InvalidOid, InvalidOid,
   19772             :                               trigForm->tgfoid, trigForm->oid, qual,
   19773         156 :                               false, true, trigForm->tgenabled);
   19774             : 
   19775         150 :         MemoryContextSwitchTo(oldcxt);
   19776         150 :         MemoryContextReset(perTupCxt);
   19777             :     }
   19778             : 
   19779        2354 :     MemoryContextDelete(perTupCxt);
   19780             : 
   19781        2354 :     systable_endscan(scan);
   19782        2354 :     table_close(pg_trigger, RowExclusiveLock);
   19783        2354 : }
   19784             : 
   19785             : /*
   19786             :  * ALTER TABLE DETACH PARTITION
   19787             :  *
   19788             :  * Return the address of the relation that is no longer a partition of rel.
   19789             :  *
   19790             :  * If concurrent mode is requested, we run in two transactions.  A side-
   19791             :  * effect is that this command cannot run in a multi-part ALTER TABLE.
   19792             :  * Currently, that's enforced by the grammar.
   19793             :  *
   19794             :  * The strategy for concurrency is to first modify the partition's
   19795             :  * pg_inherit catalog row to make it visible to everyone that the
   19796             :  * partition is detached, lock the partition against writes, and commit
   19797             :  * the transaction; anyone who requests the partition descriptor from
   19798             :  * that point onwards has to ignore such a partition.  In a second
   19799             :  * transaction, we wait until all transactions that could have seen the
   19800             :  * partition as attached are gone, then we remove the rest of partition
   19801             :  * metadata (pg_inherits and pg_class.relpartbounds).
   19802             :  */
   19803             : static ObjectAddress
   19804         546 : ATExecDetachPartition(List **wqueue, AlteredTableInfo *tab, Relation rel,
   19805             :                       RangeVar *name, bool concurrent)
   19806             : {
   19807             :     Relation    partRel;
   19808             :     ObjectAddress address;
   19809             :     Oid         defaultPartOid;
   19810             : 
   19811             :     /*
   19812             :      * We must lock the default partition, because detaching this partition
   19813             :      * will change its partition constraint.
   19814             :      */
   19815             :     defaultPartOid =
   19816         546 :         get_default_oid_from_partdesc(RelationGetPartitionDesc(rel, true));
   19817         546 :     if (OidIsValid(defaultPartOid))
   19818             :     {
   19819             :         /*
   19820             :          * Concurrent detaching when a default partition exists is not
   19821             :          * supported. The main problem is that the default partition
   19822             :          * constraint would change.  And there's a definitional problem: what
   19823             :          * should happen to the tuples that are being inserted that belong to
   19824             :          * the partition being detached?  Putting them on the partition being
   19825             :          * detached would be wrong, since they'd become "lost" after the
   19826             :          * detaching completes but we cannot put them in the default partition
   19827             :          * either until we alter its partition constraint.
   19828             :          *
   19829             :          * I think we could solve this problem if we effected the constraint
   19830             :          * change before committing the first transaction.  But the lock would
   19831             :          * have to remain AEL and it would cause concurrent query planning to
   19832             :          * be blocked, so changing it that way would be even worse.
   19833             :          */
   19834         106 :         if (concurrent)
   19835          12 :             ereport(ERROR,
   19836             :                     (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
   19837             :                      errmsg("cannot detach partitions concurrently when a default partition exists")));
   19838          94 :         LockRelationOid(defaultPartOid, AccessExclusiveLock);
   19839             :     }
   19840             : 
   19841             :     /*
   19842             :      * In concurrent mode, the partition is locked with share-update-exclusive
   19843             :      * in the first transaction.  This allows concurrent transactions to be
   19844             :      * doing DML to the partition.
   19845             :      */
   19846         534 :     partRel = table_openrv(name, concurrent ? ShareUpdateExclusiveLock :
   19847             :                            AccessExclusiveLock);
   19848             : 
   19849             :     /*
   19850             :      * Check inheritance conditions and either delete the pg_inherits row (in
   19851             :      * non-concurrent mode) or just set the inhdetachpending flag.
   19852             :      */
   19853         522 :     if (!concurrent)
   19854         376 :         RemoveInheritance(partRel, rel, false);
   19855             :     else
   19856         146 :         MarkInheritDetached(partRel, rel);
   19857             : 
   19858             :     /*
   19859             :      * Ensure that foreign keys still hold after this detach.  This keeps
   19860             :      * locks on the referencing tables, which prevents concurrent transactions
   19861             :      * from adding rows that we wouldn't see.  For this to work in concurrent
   19862             :      * mode, it is critical that the partition appears as no longer attached
   19863             :      * for the RI queries as soon as the first transaction commits.
   19864             :      */
   19865         502 :     ATDetachCheckNoForeignKeyRefs(partRel);
   19866             : 
   19867             :     /*
   19868             :      * Concurrent mode has to work harder; first we add a new constraint to
   19869             :      * the partition that matches the partition constraint.  Then we close our
   19870             :      * existing transaction, and in a new one wait for all processes to catch
   19871             :      * up on the catalog updates we've done so far; at that point we can
   19872             :      * complete the operation.
   19873             :      */
   19874         468 :     if (concurrent)
   19875             :     {
   19876             :         Oid         partrelid,
   19877             :                     parentrelid;
   19878             :         LOCKTAG     tag;
   19879             :         char       *parentrelname;
   19880             :         char       *partrelname;
   19881             : 
   19882             :         /*
   19883             :          * Add a new constraint to the partition being detached, which
   19884             :          * supplants the partition constraint (unless there is one already).
   19885             :          */
   19886         140 :         DetachAddConstraintIfNeeded(wqueue, partRel);
   19887             : 
   19888             :         /*
   19889             :          * We're almost done now; the only traces that remain are the
   19890             :          * pg_inherits tuple and the partition's relpartbounds.  Before we can
   19891             :          * remove those, we need to wait until all transactions that know that
   19892             :          * this is a partition are gone.
   19893             :          */
   19894             : 
   19895             :         /*
   19896             :          * Remember relation OIDs to re-acquire them later; and relation names
   19897             :          * too, for error messages if something is dropped in between.
   19898             :          */
   19899         140 :         partrelid = RelationGetRelid(partRel);
   19900         140 :         parentrelid = RelationGetRelid(rel);
   19901         140 :         parentrelname = MemoryContextStrdup(PortalContext,
   19902         140 :                                             RelationGetRelationName(rel));
   19903         140 :         partrelname = MemoryContextStrdup(PortalContext,
   19904         140 :                                           RelationGetRelationName(partRel));
   19905             : 
   19906             :         /* Invalidate relcache entries for the parent -- must be before close */
   19907         140 :         CacheInvalidateRelcache(rel);
   19908             : 
   19909         140 :         table_close(partRel, NoLock);
   19910         140 :         table_close(rel, NoLock);
   19911         140 :         tab->rel = NULL;
   19912             : 
   19913             :         /* Make updated catalog entry visible */
   19914         140 :         PopActiveSnapshot();
   19915         140 :         CommitTransactionCommand();
   19916             : 
   19917         140 :         StartTransactionCommand();
   19918             : 
   19919             :         /*
   19920             :          * Now wait.  This ensures that all queries that were planned
   19921             :          * including the partition are finished before we remove the rest of
   19922             :          * catalog entries.  We don't need or indeed want to acquire this
   19923             :          * lock, though -- that would block later queries.
   19924             :          *
   19925             :          * We don't need to concern ourselves with waiting for a lock on the
   19926             :          * partition itself, since we will acquire AccessExclusiveLock below.
   19927             :          */
   19928         140 :         SET_LOCKTAG_RELATION(tag, MyDatabaseId, parentrelid);
   19929         140 :         WaitForLockersMultiple(list_make1(&tag), AccessExclusiveLock, false);
   19930             : 
   19931             :         /*
   19932             :          * Now acquire locks in both relations again.  Note they may have been
   19933             :          * removed in the meantime, so care is required.
   19934             :          */
   19935          90 :         rel = try_relation_open(parentrelid, ShareUpdateExclusiveLock);
   19936          90 :         partRel = try_relation_open(partrelid, AccessExclusiveLock);
   19937             : 
   19938             :         /* If the relations aren't there, something bad happened; bail out */
   19939          90 :         if (rel == NULL)
   19940             :         {
   19941           0 :             if (partRel != NULL)    /* shouldn't happen */
   19942           0 :                 elog(WARNING, "dangling partition \"%s\" remains, can't fix",
   19943             :                      partrelname);
   19944           0 :             ereport(ERROR,
   19945             :                     (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
   19946             :                      errmsg("partitioned table \"%s\" was removed concurrently",
   19947             :                             parentrelname)));
   19948             :         }
   19949          90 :         if (partRel == NULL)
   19950           0 :             ereport(ERROR,
   19951             :                     (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
   19952             :                      errmsg("partition \"%s\" was removed concurrently", partrelname)));
   19953             : 
   19954          90 :         tab->rel = rel;
   19955             :     }
   19956             : 
   19957             :     /* Do the final part of detaching */
   19958         418 :     DetachPartitionFinalize(rel, partRel, concurrent, defaultPartOid);
   19959             : 
   19960         416 :     ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partRel));
   19961             : 
   19962             :     /* keep our lock until commit */
   19963         416 :     table_close(partRel, NoLock);
   19964             : 
   19965         416 :     return address;
   19966             : }
   19967             : 
   19968             : /*
   19969             :  * Second part of ALTER TABLE .. DETACH.
   19970             :  *
   19971             :  * This is separate so that it can be run independently when the second
   19972             :  * transaction of the concurrent algorithm fails (crash or abort).
   19973             :  */
   19974             : static void
   19975         432 : DetachPartitionFinalize(Relation rel, Relation partRel, bool concurrent,
   19976             :                         Oid defaultPartOid)
   19977             : {
   19978             :     Relation    classRel;
   19979             :     List       *fks;
   19980             :     ListCell   *cell;
   19981             :     List       *indexes;
   19982             :     Datum       new_val[Natts_pg_class];
   19983             :     bool        new_null[Natts_pg_class],
   19984             :                 new_repl[Natts_pg_class];
   19985             :     HeapTuple   tuple,
   19986             :                 newtuple;
   19987         432 :     Relation    trigrel = NULL;
   19988             : 
   19989         432 :     if (concurrent)
   19990             :     {
   19991             :         /*
   19992             :          * We can remove the pg_inherits row now. (In the non-concurrent case,
   19993             :          * this was already done).
   19994             :          */
   19995         104 :         RemoveInheritance(partRel, rel, true);
   19996             :     }
   19997             : 
   19998             :     /* Drop any triggers that were cloned on creation/attach. */
   19999         432 :     DropClonedTriggersFromPartition(RelationGetRelid(partRel));
   20000             : 
   20001             :     /*
   20002             :      * Detach any foreign keys that are inherited.  This includes creating
   20003             :      * additional action triggers.
   20004             :      */
   20005         432 :     fks = copyObject(RelationGetFKeyList(partRel));
   20006         432 :     if (fks != NIL)
   20007          60 :         trigrel = table_open(TriggerRelationId, RowExclusiveLock);
   20008         522 :     foreach(cell, fks)
   20009             :     {
   20010          90 :         ForeignKeyCacheInfo *fk = lfirst(cell);
   20011             :         HeapTuple   contup;
   20012             :         Form_pg_constraint conform;
   20013             :         Oid         insertTriggerOid,
   20014             :                     updateTriggerOid;
   20015             : 
   20016          90 :         contup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(fk->conoid));
   20017          90 :         if (!HeapTupleIsValid(contup))
   20018           0 :             elog(ERROR, "cache lookup failed for constraint %u", fk->conoid);
   20019          90 :         conform = (Form_pg_constraint) GETSTRUCT(contup);
   20020             : 
   20021             :         /* consider only the inherited foreign keys */
   20022          90 :         if (conform->contype != CONSTRAINT_FOREIGN ||
   20023          90 :             !OidIsValid(conform->conparentid))
   20024             :         {
   20025          18 :             ReleaseSysCache(contup);
   20026          18 :             continue;
   20027             :         }
   20028             : 
   20029             :         /*
   20030             :          * The constraint on this table must be marked no longer a child of
   20031             :          * the parent's constraint, as do its check triggers.
   20032             :          */
   20033          72 :         ConstraintSetParentConstraint(fk->conoid, InvalidOid, InvalidOid);
   20034             : 
   20035             :         /*
   20036             :          * Also, look up the partition's "check" triggers corresponding to the
   20037             :          * constraint being detached and detach them from the parent triggers.
   20038             :          */
   20039          72 :         GetForeignKeyCheckTriggers(trigrel,
   20040             :                                    fk->conoid, fk->confrelid, fk->conrelid,
   20041             :                                    &insertTriggerOid, &updateTriggerOid);
   20042             :         Assert(OidIsValid(insertTriggerOid));
   20043          72 :         TriggerSetParentTrigger(trigrel, insertTriggerOid, InvalidOid,
   20044             :                                 RelationGetRelid(partRel));
   20045             :         Assert(OidIsValid(updateTriggerOid));
   20046          72 :         TriggerSetParentTrigger(trigrel, updateTriggerOid, InvalidOid,
   20047             :                                 RelationGetRelid(partRel));
   20048             : 
   20049             :         /*
   20050             :          * Lastly, create the action triggers on the referenced table, using
   20051             :          * addFkRecurseReferenced, which requires some elaborate setup (so put
   20052             :          * it in a separate block).  While at it, if the table is partitioned,
   20053             :          * that function will recurse to create the pg_constraint rows and
   20054             :          * action triggers for each partition.
   20055             :          *
   20056             :          * Note there's no need to do addFkConstraint() here, because the
   20057             :          * pg_constraint row already exists.
   20058             :          */
   20059             :         {
   20060             :             Constraint *fkconstraint;
   20061             :             int         numfks;
   20062             :             AttrNumber  conkey[INDEX_MAX_KEYS];
   20063             :             AttrNumber  confkey[INDEX_MAX_KEYS];
   20064             :             Oid         conpfeqop[INDEX_MAX_KEYS];
   20065             :             Oid         conppeqop[INDEX_MAX_KEYS];
   20066             :             Oid         conffeqop[INDEX_MAX_KEYS];
   20067             :             int         numfkdelsetcols;
   20068             :             AttrNumber  confdelsetcols[INDEX_MAX_KEYS];
   20069             :             Relation    refdRel;
   20070             : 
   20071          72 :             DeconstructFkConstraintRow(contup,
   20072             :                                        &numfks,
   20073             :                                        conkey,
   20074             :                                        confkey,
   20075             :                                        conpfeqop,
   20076             :                                        conppeqop,
   20077             :                                        conffeqop,
   20078             :                                        &numfkdelsetcols,
   20079             :                                        confdelsetcols);
   20080             : 
   20081             :             /* Create a synthetic node we'll use throughout */
   20082          72 :             fkconstraint = makeNode(Constraint);
   20083          72 :             fkconstraint->contype = CONSTRAINT_FOREIGN;
   20084          72 :             fkconstraint->conname = pstrdup(NameStr(conform->conname));
   20085          72 :             fkconstraint->deferrable = conform->condeferrable;
   20086          72 :             fkconstraint->initdeferred = conform->condeferred;
   20087          72 :             fkconstraint->skip_validation = true;
   20088          72 :             fkconstraint->initially_valid = true;
   20089             :             /* a few irrelevant fields omitted here */
   20090          72 :             fkconstraint->pktable = NULL;
   20091          72 :             fkconstraint->fk_attrs = NIL;
   20092          72 :             fkconstraint->pk_attrs = NIL;
   20093          72 :             fkconstraint->fk_matchtype = conform->confmatchtype;
   20094          72 :             fkconstraint->fk_upd_action = conform->confupdtype;
   20095          72 :             fkconstraint->fk_del_action = conform->confdeltype;
   20096          72 :             fkconstraint->fk_del_set_cols = NIL;
   20097          72 :             fkconstraint->old_conpfeqop = NIL;
   20098          72 :             fkconstraint->old_pktable_oid = InvalidOid;
   20099          72 :             fkconstraint->location = -1;
   20100             : 
   20101             :             /* set up colnames, used to generate the constraint name */
   20102         192 :             for (int i = 0; i < numfks; i++)
   20103             :             {
   20104             :                 Form_pg_attribute att;
   20105             : 
   20106         120 :                 att = TupleDescAttr(RelationGetDescr(partRel),
   20107         120 :                                     conkey[i] - 1);
   20108             : 
   20109         120 :                 fkconstraint->fk_attrs = lappend(fkconstraint->fk_attrs,
   20110         120 :                                                  makeString(NameStr(att->attname)));
   20111             :             }
   20112             : 
   20113          72 :             refdRel = table_open(fk->confrelid, ShareRowExclusiveLock);
   20114             : 
   20115          72 :             addFkRecurseReferenced(fkconstraint, partRel,
   20116             :                                    refdRel,
   20117             :                                    conform->conindid,
   20118             :                                    fk->conoid,
   20119             :                                    numfks,
   20120             :                                    confkey,
   20121             :                                    conkey,
   20122             :                                    conpfeqop,
   20123             :                                    conppeqop,
   20124             :                                    conffeqop,
   20125             :                                    numfkdelsetcols,
   20126             :                                    confdelsetcols,
   20127             :                                    true,
   20128             :                                    InvalidOid, InvalidOid,
   20129          72 :                                    conform->conperiod);
   20130          72 :             table_close(refdRel, NoLock);   /* keep lock till end of xact */
   20131             :         }
   20132             : 
   20133          72 :         ReleaseSysCache(contup);
   20134             :     }
   20135         432 :     list_free_deep(fks);
   20136         432 :     if (trigrel)
   20137          60 :         table_close(trigrel, RowExclusiveLock);
   20138             : 
   20139             :     /*
   20140             :      * Any sub-constraints that are in the referenced-side of a larger
   20141             :      * constraint have to be removed.  This partition is no longer part of the
   20142             :      * key space of the constraint.
   20143             :      */
   20144         468 :     foreach(cell, GetParentedForeignKeyRefs(partRel))
   20145             :     {
   20146          38 :         Oid         constrOid = lfirst_oid(cell);
   20147             :         ObjectAddress constraint;
   20148             : 
   20149          38 :         ConstraintSetParentConstraint(constrOid, InvalidOid, InvalidOid);
   20150          38 :         deleteDependencyRecordsForClass(ConstraintRelationId,
   20151             :                                         constrOid,
   20152             :                                         ConstraintRelationId,
   20153             :                                         DEPENDENCY_INTERNAL);
   20154          38 :         CommandCounterIncrement();
   20155             : 
   20156          38 :         ObjectAddressSet(constraint, ConstraintRelationId, constrOid);
   20157          38 :         performDeletion(&constraint, DROP_RESTRICT, 0);
   20158             :     }
   20159             : 
   20160             :     /* Now we can detach indexes */
   20161         430 :     indexes = RelationGetIndexList(partRel);
   20162         612 :     foreach(cell, indexes)
   20163             :     {
   20164         182 :         Oid         idxid = lfirst_oid(cell);
   20165             :         Oid         parentidx;
   20166             :         Relation    idx;
   20167             :         Oid         constrOid;
   20168             :         Oid         parentConstrOid;
   20169             : 
   20170         182 :         if (!has_superclass(idxid))
   20171          12 :             continue;
   20172             : 
   20173         170 :         parentidx = get_partition_parent(idxid, false);
   20174             :         Assert((IndexGetRelation(parentidx, false) == RelationGetRelid(rel)));
   20175             : 
   20176         170 :         idx = index_open(idxid, AccessExclusiveLock);
   20177         170 :         IndexSetParentIndex(idx, InvalidOid);
   20178             : 
   20179             :         /*
   20180             :          * If there's a constraint associated with the index, detach it too.
   20181             :          * Careful: it is possible for a constraint index in a partition to be
   20182             :          * the child of a non-constraint index, so verify whether the parent
   20183             :          * index does actually have a constraint.
   20184             :          */
   20185         170 :         constrOid = get_relation_idx_constraint_oid(RelationGetRelid(partRel),
   20186             :                                                     idxid);
   20187         170 :         parentConstrOid = get_relation_idx_constraint_oid(RelationGetRelid(rel),
   20188             :                                                           parentidx);
   20189         170 :         if (OidIsValid(parentConstrOid) && OidIsValid(constrOid))
   20190          72 :             ConstraintSetParentConstraint(constrOid, InvalidOid, InvalidOid);
   20191             : 
   20192         170 :         index_close(idx, NoLock);
   20193             :     }
   20194             : 
   20195             :     /* Update pg_class tuple */
   20196         430 :     classRel = table_open(RelationRelationId, RowExclusiveLock);
   20197         430 :     tuple = SearchSysCacheCopy1(RELOID,
   20198             :                                 ObjectIdGetDatum(RelationGetRelid(partRel)));
   20199         430 :     if (!HeapTupleIsValid(tuple))
   20200           0 :         elog(ERROR, "cache lookup failed for relation %u",
   20201             :              RelationGetRelid(partRel));
   20202             :     Assert(((Form_pg_class) GETSTRUCT(tuple))->relispartition);
   20203             : 
   20204             :     /* Clear relpartbound and reset relispartition */
   20205         430 :     memset(new_val, 0, sizeof(new_val));
   20206         430 :     memset(new_null, false, sizeof(new_null));
   20207         430 :     memset(new_repl, false, sizeof(new_repl));
   20208         430 :     new_val[Anum_pg_class_relpartbound - 1] = (Datum) 0;
   20209         430 :     new_null[Anum_pg_class_relpartbound - 1] = true;
   20210         430 :     new_repl[Anum_pg_class_relpartbound - 1] = true;
   20211         430 :     newtuple = heap_modify_tuple(tuple, RelationGetDescr(classRel),
   20212             :                                  new_val, new_null, new_repl);
   20213             : 
   20214         430 :     ((Form_pg_class) GETSTRUCT(newtuple))->relispartition = false;
   20215         430 :     CatalogTupleUpdate(classRel, &newtuple->t_self, newtuple);
   20216         430 :     heap_freetuple(newtuple);
   20217         430 :     table_close(classRel, RowExclusiveLock);
   20218             : 
   20219             :     /*
   20220             :      * Drop identity property from all identity columns of partition.
   20221             :      */
   20222        1228 :     for (int attno = 0; attno < RelationGetNumberOfAttributes(partRel); attno++)
   20223             :     {
   20224         798 :         Form_pg_attribute attr = TupleDescAttr(partRel->rd_att, attno);
   20225             : 
   20226         798 :         if (!attr->attisdropped && attr->attidentity)
   20227           6 :             ATExecDropIdentity(partRel, NameStr(attr->attname), false,
   20228             :                                AccessExclusiveLock, true, true);
   20229             :     }
   20230             : 
   20231         430 :     if (OidIsValid(defaultPartOid))
   20232             :     {
   20233             :         /*
   20234             :          * If the relation being detached is the default partition itself,
   20235             :          * remove it from the parent's pg_partitioned_table entry.
   20236             :          *
   20237             :          * If not, we must invalidate default partition's relcache entry, as
   20238             :          * in StorePartitionBound: its partition constraint depends on every
   20239             :          * other partition's partition constraint.
   20240             :          */
   20241          46 :         if (RelationGetRelid(partRel) == defaultPartOid)
   20242           2 :             update_default_partition_oid(RelationGetRelid(rel), InvalidOid);
   20243             :         else
   20244          44 :             CacheInvalidateRelcacheByRelid(defaultPartOid);
   20245             :     }
   20246             : 
   20247             :     /*
   20248             :      * Invalidate the parent's relcache so that the partition is no longer
   20249             :      * included in its partition descriptor.
   20250             :      */
   20251         430 :     CacheInvalidateRelcache(rel);
   20252             : 
   20253             :     /*
   20254             :      * If the partition we just detached is partitioned itself, invalidate
   20255             :      * relcache for all descendent partitions too to ensure that their
   20256             :      * rd_partcheck expression trees are rebuilt; must lock partitions before
   20257             :      * doing so, using the same lockmode as what partRel has been locked with
   20258             :      * by the caller.
   20259             :      */
   20260         430 :     if (partRel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
   20261             :     {
   20262             :         List       *children;
   20263             : 
   20264          56 :         children = find_all_inheritors(RelationGetRelid(partRel),
   20265             :                                        AccessExclusiveLock, NULL);
   20266         180 :         foreach(cell, children)
   20267             :         {
   20268         124 :             CacheInvalidateRelcacheByRelid(lfirst_oid(cell));
   20269             :         }
   20270             :     }
   20271         430 : }
   20272             : 
   20273             : /*
   20274             :  * ALTER TABLE ... DETACH PARTITION ... FINALIZE
   20275             :  *
   20276             :  * To use when a DETACH PARTITION command previously did not run to
   20277             :  * completion; this completes the detaching process.
   20278             :  */
   20279             : static ObjectAddress
   20280          14 : ATExecDetachPartitionFinalize(Relation rel, RangeVar *name)
   20281             : {
   20282             :     Relation    partRel;
   20283             :     ObjectAddress address;
   20284          14 :     Snapshot    snap = GetActiveSnapshot();
   20285             : 
   20286          14 :     partRel = table_openrv(name, AccessExclusiveLock);
   20287             : 
   20288             :     /*
   20289             :      * Wait until existing snapshots are gone.  This is important if the
   20290             :      * second transaction of DETACH PARTITION CONCURRENTLY is canceled: the
   20291             :      * user could immediately run DETACH FINALIZE without actually waiting for
   20292             :      * existing transactions.  We must not complete the detach action until
   20293             :      * all such queries are complete (otherwise we would present them with an
   20294             :      * inconsistent view of catalogs).
   20295             :      */
   20296          14 :     WaitForOlderSnapshots(snap->xmin, false);
   20297             : 
   20298          14 :     DetachPartitionFinalize(rel, partRel, true, InvalidOid);
   20299             : 
   20300          14 :     ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partRel));
   20301             : 
   20302          14 :     table_close(partRel, NoLock);
   20303             : 
   20304          14 :     return address;
   20305             : }
   20306             : 
   20307             : /*
   20308             :  * DetachAddConstraintIfNeeded
   20309             :  *      Subroutine for ATExecDetachPartition.  Create a constraint that
   20310             :  *      takes the place of the partition constraint, but avoid creating
   20311             :  *      a dupe if a constraint already exists which implies the needed
   20312             :  *      constraint.
   20313             :  */
   20314             : static void
   20315         140 : DetachAddConstraintIfNeeded(List **wqueue, Relation partRel)
   20316             : {
   20317             :     List       *constraintExpr;
   20318             : 
   20319         140 :     constraintExpr = RelationGetPartitionQual(partRel);
   20320         140 :     constraintExpr = (List *) eval_const_expressions(NULL, (Node *) constraintExpr);
   20321             : 
   20322             :     /*
   20323             :      * Avoid adding a new constraint if the needed constraint is implied by an
   20324             :      * existing constraint
   20325             :      */
   20326         140 :     if (!PartConstraintImpliedByRelConstraint(partRel, constraintExpr))
   20327             :     {
   20328             :         AlteredTableInfo *tab;
   20329             :         Constraint *n;
   20330             : 
   20331         134 :         tab = ATGetQueueEntry(wqueue, partRel);
   20332             : 
   20333             :         /* Add constraint on partition, equivalent to the partition constraint */
   20334         134 :         n = makeNode(Constraint);
   20335         134 :         n->contype = CONSTR_CHECK;
   20336         134 :         n->conname = NULL;
   20337         134 :         n->location = -1;
   20338         134 :         n->is_no_inherit = false;
   20339         134 :         n->raw_expr = NULL;
   20340         134 :         n->cooked_expr = nodeToString(make_ands_explicit(constraintExpr));
   20341         134 :         n->is_enforced = true;
   20342         134 :         n->initially_valid = true;
   20343         134 :         n->skip_validation = true;
   20344             :         /* It's a re-add, since it nominally already exists */
   20345         134 :         ATAddCheckNNConstraint(wqueue, tab, partRel, n,
   20346             :                                true, false, true, ShareUpdateExclusiveLock);
   20347             :     }
   20348         140 : }
   20349             : 
   20350             : /*
   20351             :  * DropClonedTriggersFromPartition
   20352             :  *      subroutine for ATExecDetachPartition to remove any triggers that were
   20353             :  *      cloned to the partition when it was created-as-partition or attached.
   20354             :  *      This undoes what CloneRowTriggersToPartition did.
   20355             :  */
   20356             : static void
   20357         432 : DropClonedTriggersFromPartition(Oid partitionId)
   20358             : {
   20359             :     ScanKeyData skey;
   20360             :     SysScanDesc scan;
   20361             :     HeapTuple   trigtup;
   20362             :     Relation    tgrel;
   20363             :     ObjectAddresses *objects;
   20364             : 
   20365         432 :     objects = new_object_addresses();
   20366             : 
   20367             :     /*
   20368             :      * Scan pg_trigger to search for all triggers on this rel.
   20369             :      */
   20370         432 :     ScanKeyInit(&skey, Anum_pg_trigger_tgrelid, BTEqualStrategyNumber,
   20371             :                 F_OIDEQ, ObjectIdGetDatum(partitionId));
   20372         432 :     tgrel = table_open(TriggerRelationId, RowExclusiveLock);
   20373         432 :     scan = systable_beginscan(tgrel, TriggerRelidNameIndexId,
   20374             :                               true, NULL, 1, &skey);
   20375         706 :     while (HeapTupleIsValid(trigtup = systable_getnext(scan)))
   20376             :     {
   20377         274 :         Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(trigtup);
   20378             :         ObjectAddress trig;
   20379             : 
   20380             :         /* Ignore triggers that weren't cloned */
   20381         274 :         if (!OidIsValid(pg_trigger->tgparentid))
   20382         256 :             continue;
   20383             : 
   20384             :         /*
   20385             :          * Ignore internal triggers that are implementation objects of foreign
   20386             :          * keys, because these will be detached when the foreign keys
   20387             :          * themselves are.
   20388             :          */
   20389         238 :         if (OidIsValid(pg_trigger->tgconstrrelid))
   20390         220 :             continue;
   20391             : 
   20392             :         /*
   20393             :          * This is ugly, but necessary: remove the dependency markings on the
   20394             :          * trigger so that it can be removed.
   20395             :          */
   20396          18 :         deleteDependencyRecordsForClass(TriggerRelationId, pg_trigger->oid,
   20397             :                                         TriggerRelationId,
   20398             :                                         DEPENDENCY_PARTITION_PRI);
   20399          18 :         deleteDependencyRecordsForClass(TriggerRelationId, pg_trigger->oid,
   20400             :                                         RelationRelationId,
   20401             :                                         DEPENDENCY_PARTITION_SEC);
   20402             : 
   20403             :         /* remember this trigger to remove it below */
   20404          18 :         ObjectAddressSet(trig, TriggerRelationId, pg_trigger->oid);
   20405          18 :         add_exact_object_address(&trig, objects);
   20406             :     }
   20407             : 
   20408             :     /* make the dependency removal visible to the deletion below */
   20409         432 :     CommandCounterIncrement();
   20410         432 :     performMultipleDeletions(objects, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
   20411             : 
   20412             :     /* done */
   20413         432 :     free_object_addresses(objects);
   20414         432 :     systable_endscan(scan);
   20415         432 :     table_close(tgrel, RowExclusiveLock);
   20416         432 : }
   20417             : 
   20418             : /*
   20419             :  * Before acquiring lock on an index, acquire the same lock on the owning
   20420             :  * table.
   20421             :  */
   20422             : struct AttachIndexCallbackState
   20423             : {
   20424             :     Oid         partitionOid;
   20425             :     Oid         parentTblOid;
   20426             :     bool        lockedParentTbl;
   20427             : };
   20428             : 
   20429             : static void
   20430         400 : RangeVarCallbackForAttachIndex(const RangeVar *rv, Oid relOid, Oid oldRelOid,
   20431             :                                void *arg)
   20432             : {
   20433             :     struct AttachIndexCallbackState *state;
   20434             :     Form_pg_class classform;
   20435             :     HeapTuple   tuple;
   20436             : 
   20437         400 :     state = (struct AttachIndexCallbackState *) arg;
   20438             : 
   20439         400 :     if (!state->lockedParentTbl)
   20440             :     {
   20441         392 :         LockRelationOid(state->parentTblOid, AccessShareLock);
   20442         392 :         state->lockedParentTbl = true;
   20443             :     }
   20444             : 
   20445             :     /*
   20446             :      * If we previously locked some other heap, and the name we're looking up
   20447             :      * no longer refers to an index on that relation, release the now-useless
   20448             :      * lock.  XXX maybe we should do *after* we verify whether the index does
   20449             :      * not actually belong to the same relation ...
   20450             :      */
   20451         400 :     if (relOid != oldRelOid && OidIsValid(state->partitionOid))
   20452             :     {
   20453           0 :         UnlockRelationOid(state->partitionOid, AccessShareLock);
   20454           0 :         state->partitionOid = InvalidOid;
   20455             :     }
   20456             : 
   20457             :     /* Didn't find a relation, so no need for locking or permission checks. */
   20458         400 :     if (!OidIsValid(relOid))
   20459           6 :         return;
   20460             : 
   20461         394 :     tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid));
   20462         394 :     if (!HeapTupleIsValid(tuple))
   20463           0 :         return;                 /* concurrently dropped, so nothing to do */
   20464         394 :     classform = (Form_pg_class) GETSTRUCT(tuple);
   20465         394 :     if (classform->relkind != RELKIND_PARTITIONED_INDEX &&
   20466         302 :         classform->relkind != RELKIND_INDEX)
   20467           6 :         ereport(ERROR,
   20468             :                 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
   20469             :                  errmsg("\"%s\" is not an index", rv->relname)));
   20470         388 :     ReleaseSysCache(tuple);
   20471             : 
   20472             :     /*
   20473             :      * Since we need only examine the heap's tupledesc, an access share lock
   20474             :      * on it (preventing any DDL) is sufficient.
   20475             :      */
   20476         388 :     state->partitionOid = IndexGetRelation(relOid, false);
   20477         388 :     LockRelationOid(state->partitionOid, AccessShareLock);
   20478             : }
   20479             : 
   20480             : /*
   20481             :  * ALTER INDEX i1 ATTACH PARTITION i2
   20482             :  */
   20483             : static ObjectAddress
   20484         392 : ATExecAttachPartitionIdx(List **wqueue, Relation parentIdx, RangeVar *name)
   20485             : {
   20486             :     Relation    partIdx;
   20487             :     Relation    partTbl;
   20488             :     Relation    parentTbl;
   20489             :     ObjectAddress address;
   20490             :     Oid         partIdxId;
   20491             :     Oid         currParent;
   20492             :     struct AttachIndexCallbackState state;
   20493             : 
   20494             :     /*
   20495             :      * We need to obtain lock on the index 'name' to modify it, but we also
   20496             :      * need to read its owning table's tuple descriptor -- so we need to lock
   20497             :      * both.  To avoid deadlocks, obtain lock on the table before doing so on
   20498             :      * the index.  Furthermore, we need to examine the parent table of the
   20499             :      * partition, so lock that one too.
   20500             :      */
   20501         392 :     state.partitionOid = InvalidOid;
   20502         392 :     state.parentTblOid = parentIdx->rd_index->indrelid;
   20503         392 :     state.lockedParentTbl = false;
   20504             :     partIdxId =
   20505         392 :         RangeVarGetRelidExtended(name, AccessExclusiveLock, 0,
   20506             :                                  RangeVarCallbackForAttachIndex,
   20507             :                                  &state);
   20508             :     /* Not there? */
   20509         380 :     if (!OidIsValid(partIdxId))
   20510           0 :         ereport(ERROR,
   20511             :                 (errcode(ERRCODE_UNDEFINED_OBJECT),
   20512             :                  errmsg("index \"%s\" does not exist", name->relname)));
   20513             : 
   20514             :     /* no deadlock risk: RangeVarGetRelidExtended already acquired the lock */
   20515         380 :     partIdx = relation_open(partIdxId, AccessExclusiveLock);
   20516             : 
   20517             :     /* we already hold locks on both tables, so this is safe: */
   20518         380 :     parentTbl = relation_open(parentIdx->rd_index->indrelid, AccessShareLock);
   20519         380 :     partTbl = relation_open(partIdx->rd_index->indrelid, NoLock);
   20520             : 
   20521         380 :     ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partIdx));
   20522             : 
   20523             :     /* Silently do nothing if already in the right state */
   20524         760 :     currParent = partIdx->rd_rel->relispartition ?
   20525         380 :         get_partition_parent(partIdxId, false) : InvalidOid;
   20526         380 :     if (currParent != RelationGetRelid(parentIdx))
   20527             :     {
   20528             :         IndexInfo  *childInfo;
   20529             :         IndexInfo  *parentInfo;
   20530             :         AttrMap    *attmap;
   20531             :         bool        found;
   20532             :         int         i;
   20533             :         PartitionDesc partDesc;
   20534             :         Oid         constraintOid,
   20535         356 :                     cldConstrId = InvalidOid;
   20536             : 
   20537             :         /*
   20538             :          * If this partition already has an index attached, refuse the
   20539             :          * operation.
   20540             :          */
   20541         356 :         refuseDupeIndexAttach(parentIdx, partIdx, partTbl);
   20542             : 
   20543         350 :         if (OidIsValid(currParent))
   20544           0 :             ereport(ERROR,
   20545             :                     (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
   20546             :                      errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
   20547             :                             RelationGetRelationName(partIdx),
   20548             :                             RelationGetRelationName(parentIdx)),
   20549             :                      errdetail("Index \"%s\" is already attached to another index.",
   20550             :                                RelationGetRelationName(partIdx))));
   20551             : 
   20552             :         /* Make sure it indexes a partition of the other index's table */
   20553         350 :         partDesc = RelationGetPartitionDesc(parentTbl, true);
   20554         350 :         found = false;
   20555         552 :         for (i = 0; i < partDesc->nparts; i++)
   20556             :         {
   20557         546 :             if (partDesc->oids[i] == state.partitionOid)
   20558             :             {
   20559         344 :                 found = true;
   20560         344 :                 break;
   20561             :             }
   20562             :         }
   20563         350 :         if (!found)
   20564           6 :             ereport(ERROR,
   20565             :                     (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
   20566             :                      errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
   20567             :                             RelationGetRelationName(partIdx),
   20568             :                             RelationGetRelationName(parentIdx)),
   20569             :                      errdetail("Index \"%s\" is not an index on any partition of table \"%s\".",
   20570             :                                RelationGetRelationName(partIdx),
   20571             :                                RelationGetRelationName(parentTbl))));
   20572             : 
   20573             :         /* Ensure the indexes are compatible */
   20574         344 :         childInfo = BuildIndexInfo(partIdx);
   20575         344 :         parentInfo = BuildIndexInfo(parentIdx);
   20576         344 :         attmap = build_attrmap_by_name(RelationGetDescr(partTbl),
   20577             :                                        RelationGetDescr(parentTbl),
   20578             :                                        false);
   20579         344 :         if (!CompareIndexInfo(childInfo, parentInfo,
   20580         344 :                               partIdx->rd_indcollation,
   20581         344 :                               parentIdx->rd_indcollation,
   20582         344 :                               partIdx->rd_opfamily,
   20583         344 :                               parentIdx->rd_opfamily,
   20584             :                               attmap))
   20585          42 :             ereport(ERROR,
   20586             :                     (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
   20587             :                      errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
   20588             :                             RelationGetRelationName(partIdx),
   20589             :                             RelationGetRelationName(parentIdx)),
   20590             :                      errdetail("The index definitions do not match.")));
   20591             : 
   20592             :         /*
   20593             :          * If there is a constraint in the parent, make sure there is one in
   20594             :          * the child too.
   20595             :          */
   20596         302 :         constraintOid = get_relation_idx_constraint_oid(RelationGetRelid(parentTbl),
   20597             :                                                         RelationGetRelid(parentIdx));
   20598             : 
   20599         302 :         if (OidIsValid(constraintOid))
   20600             :         {
   20601         122 :             cldConstrId = get_relation_idx_constraint_oid(RelationGetRelid(partTbl),
   20602             :                                                           partIdxId);
   20603         122 :             if (!OidIsValid(cldConstrId))
   20604           6 :                 ereport(ERROR,
   20605             :                         (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
   20606             :                          errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
   20607             :                                 RelationGetRelationName(partIdx),
   20608             :                                 RelationGetRelationName(parentIdx)),
   20609             :                          errdetail("The index \"%s\" belongs to a constraint in table \"%s\" but no constraint exists for index \"%s\".",
   20610             :                                    RelationGetRelationName(parentIdx),
   20611             :                                    RelationGetRelationName(parentTbl),
   20612             :                                    RelationGetRelationName(partIdx))));
   20613             :         }
   20614             : 
   20615             :         /*
   20616             :          * If it's a primary key, make sure the columns in the partition are
   20617             :          * NOT NULL.
   20618             :          */
   20619         296 :         if (parentIdx->rd_index->indisprimary)
   20620          92 :             verifyPartitionIndexNotNull(childInfo, partTbl);
   20621             : 
   20622             :         /* All good -- do it */
   20623         296 :         IndexSetParentIndex(partIdx, RelationGetRelid(parentIdx));
   20624         296 :         if (OidIsValid(constraintOid))
   20625         116 :             ConstraintSetParentConstraint(cldConstrId, constraintOid,
   20626             :                                           RelationGetRelid(partTbl));
   20627             : 
   20628         296 :         free_attrmap(attmap);
   20629             : 
   20630         296 :         validatePartitionedIndex(parentIdx, parentTbl);
   20631             :     }
   20632             : 
   20633         320 :     relation_close(parentTbl, AccessShareLock);
   20634             :     /* keep these locks till commit */
   20635         320 :     relation_close(partTbl, NoLock);
   20636         320 :     relation_close(partIdx, NoLock);
   20637             : 
   20638         320 :     return address;
   20639             : }
   20640             : 
   20641             : /*
   20642             :  * Verify whether the given partition already contains an index attached
   20643             :  * to the given partitioned index.  If so, raise an error.
   20644             :  */
   20645             : static void
   20646         356 : refuseDupeIndexAttach(Relation parentIdx, Relation partIdx, Relation partitionTbl)
   20647             : {
   20648             :     Oid         existingIdx;
   20649             : 
   20650         356 :     existingIdx = index_get_partition(partitionTbl,
   20651             :                                       RelationGetRelid(parentIdx));
   20652         356 :     if (OidIsValid(existingIdx))
   20653           6 :         ereport(ERROR,
   20654             :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
   20655             :                  errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
   20656             :                         RelationGetRelationName(partIdx),
   20657             :                         RelationGetRelationName(parentIdx)),
   20658             :                  errdetail("Another index is already attached for partition \"%s\".",
   20659             :                            RelationGetRelationName(partitionTbl))));
   20660         350 : }
   20661             : 
   20662             : /*
   20663             :  * Verify whether the set of attached partition indexes to a parent index on
   20664             :  * a partitioned table is complete.  If it is, mark the parent index valid.
   20665             :  *
   20666             :  * This should be called each time a partition index is attached.
   20667             :  */
   20668             : static void
   20669         338 : validatePartitionedIndex(Relation partedIdx, Relation partedTbl)
   20670             : {
   20671             :     Relation    inheritsRel;
   20672             :     SysScanDesc scan;
   20673             :     ScanKeyData key;
   20674         338 :     int         tuples = 0;
   20675             :     HeapTuple   inhTup;
   20676         338 :     bool        updated = false;
   20677             : 
   20678             :     Assert(partedIdx->rd_rel->relkind == RELKIND_PARTITIONED_INDEX);
   20679             : 
   20680             :     /*
   20681             :      * Scan pg_inherits for this parent index.  Count each valid index we find
   20682             :      * (verifying the pg_index entry for each), and if we reach the total
   20683             :      * amount we expect, we can mark this parent index as valid.
   20684             :      */
   20685         338 :     inheritsRel = table_open(InheritsRelationId, AccessShareLock);
   20686         338 :     ScanKeyInit(&key, Anum_pg_inherits_inhparent,
   20687             :                 BTEqualStrategyNumber, F_OIDEQ,
   20688             :                 ObjectIdGetDatum(RelationGetRelid(partedIdx)));
   20689         338 :     scan = systable_beginscan(inheritsRel, InheritsParentIndexId, true,
   20690             :                               NULL, 1, &key);
   20691         880 :     while ((inhTup = systable_getnext(scan)) != NULL)
   20692             :     {
   20693         542 :         Form_pg_inherits inhForm = (Form_pg_inherits) GETSTRUCT(inhTup);
   20694             :         HeapTuple   indTup;
   20695             :         Form_pg_index indexForm;
   20696             : 
   20697         542 :         indTup = SearchSysCache1(INDEXRELID,
   20698             :                                  ObjectIdGetDatum(inhForm->inhrelid));
   20699         542 :         if (!HeapTupleIsValid(indTup))
   20700           0 :             elog(ERROR, "cache lookup failed for index %u", inhForm->inhrelid);
   20701         542 :         indexForm = (Form_pg_index) GETSTRUCT(indTup);
   20702         542 :         if (indexForm->indisvalid)
   20703         484 :             tuples += 1;
   20704         542 :         ReleaseSysCache(indTup);
   20705             :     }
   20706             : 
   20707             :     /* Done with pg_inherits */
   20708         338 :     systable_endscan(scan);
   20709         338 :     table_close(inheritsRel, AccessShareLock);
   20710             : 
   20711             :     /*
   20712             :      * If we found as many inherited indexes as the partitioned table has
   20713             :      * partitions, we're good; update pg_index to set indisvalid.
   20714             :      */
   20715         338 :     if (tuples == RelationGetPartitionDesc(partedTbl, true)->nparts)
   20716             :     {
   20717             :         Relation    idxRel;
   20718             :         HeapTuple   indTup;
   20719             :         Form_pg_index indexForm;
   20720             : 
   20721         168 :         idxRel = table_open(IndexRelationId, RowExclusiveLock);
   20722         168 :         indTup = SearchSysCacheCopy1(INDEXRELID,
   20723             :                                      ObjectIdGetDatum(RelationGetRelid(partedIdx)));
   20724         168 :         if (!HeapTupleIsValid(indTup))
   20725           0 :             elog(ERROR, "cache lookup failed for index %u",
   20726             :                  RelationGetRelid(partedIdx));
   20727         168 :         indexForm = (Form_pg_index) GETSTRUCT(indTup);
   20728             : 
   20729         168 :         indexForm->indisvalid = true;
   20730         168 :         updated = true;
   20731             : 
   20732         168 :         CatalogTupleUpdate(idxRel, &indTup->t_self, indTup);
   20733             : 
   20734         168 :         table_close(idxRel, RowExclusiveLock);
   20735         168 :         heap_freetuple(indTup);
   20736             :     }
   20737             : 
   20738             :     /*
   20739             :      * If this index is in turn a partition of a larger index, validating it
   20740             :      * might cause the parent to become valid also.  Try that.
   20741             :      */
   20742         338 :     if (updated && partedIdx->rd_rel->relispartition)
   20743             :     {
   20744             :         Oid         parentIdxId,
   20745             :                     parentTblId;
   20746             :         Relation    parentIdx,
   20747             :                     parentTbl;
   20748             : 
   20749             :         /* make sure we see the validation we just did */
   20750          42 :         CommandCounterIncrement();
   20751             : 
   20752          42 :         parentIdxId = get_partition_parent(RelationGetRelid(partedIdx), false);
   20753          42 :         parentTblId = get_partition_parent(RelationGetRelid(partedTbl), false);
   20754          42 :         parentIdx = relation_open(parentIdxId, AccessExclusiveLock);
   20755          42 :         parentTbl = relation_open(parentTblId, AccessExclusiveLock);
   20756             :         Assert(!parentIdx->rd_index->indisvalid);
   20757             : 
   20758          42 :         validatePartitionedIndex(parentIdx, parentTbl);
   20759             : 
   20760          42 :         relation_close(parentIdx, AccessExclusiveLock);
   20761          42 :         relation_close(parentTbl, AccessExclusiveLock);
   20762             :     }
   20763         338 : }
   20764             : 
   20765             : /*
   20766             :  * When attaching an index as a partition of a partitioned index which is a
   20767             :  * primary key, verify that all the columns in the partition are marked NOT
   20768             :  * NULL.
   20769             :  */
   20770             : static void
   20771          92 : verifyPartitionIndexNotNull(IndexInfo *iinfo, Relation partition)
   20772             : {
   20773         186 :     for (int i = 0; i < iinfo->ii_NumIndexKeyAttrs; i++)
   20774             :     {
   20775          94 :         Form_pg_attribute att = TupleDescAttr(RelationGetDescr(partition),
   20776          94 :                                               iinfo->ii_IndexAttrNumbers[i] - 1);
   20777             : 
   20778          94 :         if (!att->attnotnull)
   20779           0 :             ereport(ERROR,
   20780             :                     errcode(ERRCODE_INVALID_TABLE_DEFINITION),
   20781             :                     errmsg("invalid primary key definition"),
   20782             :                     errdetail("Column \"%s\" of relation \"%s\" is not marked NOT NULL.",
   20783             :                               NameStr(att->attname),
   20784             :                               RelationGetRelationName(partition)));
   20785             :     }
   20786          92 : }
   20787             : 
   20788             : /*
   20789             :  * Return an OID list of constraints that reference the given relation
   20790             :  * that are marked as having a parent constraints.
   20791             :  */
   20792             : static List *
   20793         934 : GetParentedForeignKeyRefs(Relation partition)
   20794             : {
   20795             :     Relation    pg_constraint;
   20796             :     HeapTuple   tuple;
   20797             :     SysScanDesc scan;
   20798             :     ScanKeyData key[2];
   20799         934 :     List       *constraints = NIL;
   20800             : 
   20801             :     /*
   20802             :      * If no indexes, or no columns are referenceable by FKs, we can avoid the
   20803             :      * scan.
   20804             :      */
   20805        1338 :     if (RelationGetIndexList(partition) == NIL ||
   20806         404 :         bms_is_empty(RelationGetIndexAttrBitmap(partition,
   20807             :                                                 INDEX_ATTR_BITMAP_KEY)))
   20808         686 :         return NIL;
   20809             : 
   20810             :     /* Search for constraints referencing this table */
   20811         248 :     pg_constraint = table_open(ConstraintRelationId, AccessShareLock);
   20812         248 :     ScanKeyInit(&key[0],
   20813             :                 Anum_pg_constraint_confrelid, BTEqualStrategyNumber,
   20814             :                 F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(partition)));
   20815         248 :     ScanKeyInit(&key[1],
   20816             :                 Anum_pg_constraint_contype, BTEqualStrategyNumber,
   20817             :                 F_CHAREQ, CharGetDatum(CONSTRAINT_FOREIGN));
   20818             : 
   20819             :     /* XXX This is a seqscan, as we don't have a usable index */
   20820         248 :     scan = systable_beginscan(pg_constraint, InvalidOid, true, NULL, 2, key);
   20821         372 :     while ((tuple = systable_getnext(scan)) != NULL)
   20822             :     {
   20823         124 :         Form_pg_constraint constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
   20824             : 
   20825             :         /*
   20826             :          * We only need to process constraints that are part of larger ones.
   20827             :          */
   20828         124 :         if (!OidIsValid(constrForm->conparentid))
   20829           0 :             continue;
   20830             : 
   20831         124 :         constraints = lappend_oid(constraints, constrForm->oid);
   20832             :     }
   20833             : 
   20834         248 :     systable_endscan(scan);
   20835         248 :     table_close(pg_constraint, AccessShareLock);
   20836             : 
   20837         248 :     return constraints;
   20838             : }
   20839             : 
   20840             : /*
   20841             :  * During DETACH PARTITION, verify that any foreign keys pointing to the
   20842             :  * partitioned table would not become invalid.  An error is raised if any
   20843             :  * referenced values exist.
   20844             :  */
   20845             : static void
   20846         502 : ATDetachCheckNoForeignKeyRefs(Relation partition)
   20847             : {
   20848             :     List       *constraints;
   20849             :     ListCell   *cell;
   20850             : 
   20851         502 :     constraints = GetParentedForeignKeyRefs(partition);
   20852             : 
   20853         554 :     foreach(cell, constraints)
   20854             :     {
   20855          86 :         Oid         constrOid = lfirst_oid(cell);
   20856             :         HeapTuple   tuple;
   20857             :         Form_pg_constraint constrForm;
   20858             :         Relation    rel;
   20859          86 :         Trigger     trig = {0};
   20860             : 
   20861          86 :         tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constrOid));
   20862          86 :         if (!HeapTupleIsValid(tuple))
   20863           0 :             elog(ERROR, "cache lookup failed for constraint %u", constrOid);
   20864          86 :         constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
   20865             : 
   20866             :         Assert(OidIsValid(constrForm->conparentid));
   20867             :         Assert(constrForm->confrelid == RelationGetRelid(partition));
   20868             : 
   20869             :         /* prevent data changes into the referencing table until commit */
   20870          86 :         rel = table_open(constrForm->conrelid, ShareLock);
   20871             : 
   20872          86 :         trig.tgoid = InvalidOid;
   20873          86 :         trig.tgname = NameStr(constrForm->conname);
   20874          86 :         trig.tgenabled = TRIGGER_FIRES_ON_ORIGIN;
   20875          86 :         trig.tgisinternal = true;
   20876          86 :         trig.tgconstrrelid = RelationGetRelid(partition);
   20877          86 :         trig.tgconstrindid = constrForm->conindid;
   20878          86 :         trig.tgconstraint = constrForm->oid;
   20879          86 :         trig.tgdeferrable = false;
   20880          86 :         trig.tginitdeferred = false;
   20881             :         /* we needn't fill in remaining fields */
   20882             : 
   20883          86 :         RI_PartitionRemove_Check(&trig, rel, partition);
   20884             : 
   20885          52 :         ReleaseSysCache(tuple);
   20886             : 
   20887          52 :         table_close(rel, NoLock);
   20888             :     }
   20889         468 : }
   20890             : 
   20891             : /*
   20892             :  * resolve column compression specification to compression method.
   20893             :  */
   20894             : static char
   20895      231688 : GetAttributeCompression(Oid atttypid, const char *compression)
   20896             : {
   20897             :     char        cmethod;
   20898             : 
   20899      231688 :     if (compression == NULL || strcmp(compression, "default") == 0)
   20900      231538 :         return InvalidCompressionMethod;
   20901             : 
   20902             :     /*
   20903             :      * To specify a nondefault method, the column data type must be toastable.
   20904             :      * Note this says nothing about whether the column's attstorage setting
   20905             :      * permits compression; we intentionally allow attstorage and
   20906             :      * attcompression to be independent.  But with a non-toastable type,
   20907             :      * attstorage could not be set to a value that would permit compression.
   20908             :      *
   20909             :      * We don't actually need to enforce this, since nothing bad would happen
   20910             :      * if attcompression were non-default; it would never be consulted.  But
   20911             :      * it seems more user-friendly to complain about a certainly-useless
   20912             :      * attempt to set the property.
   20913             :      */
   20914         150 :     if (!TypeIsToastable(atttypid))
   20915           6 :         ereport(ERROR,
   20916             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   20917             :                  errmsg("column data type %s does not support compression",
   20918             :                         format_type_be(atttypid))));
   20919             : 
   20920         144 :     cmethod = CompressionNameToMethod(compression);
   20921         144 :     if (!CompressionMethodIsValid(cmethod))
   20922          12 :         ereport(ERROR,
   20923             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
   20924             :                  errmsg("invalid compression method \"%s\"", compression)));
   20925             : 
   20926         132 :     return cmethod;
   20927             : }
   20928             : 
   20929             : /*
   20930             :  * resolve column storage specification
   20931             :  */
   20932             : static char
   20933         242 : GetAttributeStorage(Oid atttypid, const char *storagemode)
   20934             : {
   20935         242 :     char        cstorage = 0;
   20936             : 
   20937         242 :     if (pg_strcasecmp(storagemode, "plain") == 0)
   20938          50 :         cstorage = TYPSTORAGE_PLAIN;
   20939         192 :     else if (pg_strcasecmp(storagemode, "external") == 0)
   20940         156 :         cstorage = TYPSTORAGE_EXTERNAL;
   20941          36 :     else if (pg_strcasecmp(storagemode, "extended") == 0)
   20942          16 :         cstorage = TYPSTORAGE_EXTENDED;
   20943          20 :     else if (pg_strcasecmp(storagemode, "main") == 0)
   20944          14 :         cstorage = TYPSTORAGE_MAIN;
   20945           6 :     else if (pg_strcasecmp(storagemode, "default") == 0)
   20946           6 :         cstorage = get_typstorage(atttypid);
   20947             :     else
   20948           0 :         ereport(ERROR,
   20949             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
   20950             :                  errmsg("invalid storage type \"%s\"",
   20951             :                         storagemode)));
   20952             : 
   20953             :     /*
   20954             :      * safety check: do not allow toasted storage modes unless column datatype
   20955             :      * is TOAST-aware.
   20956             :      */
   20957         242 :     if (!(cstorage == TYPSTORAGE_PLAIN || TypeIsToastable(atttypid)))
   20958           6 :         ereport(ERROR,
   20959             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   20960             :                  errmsg("column data type %s can only have storage PLAIN",
   20961             :                         format_type_be(atttypid))));
   20962             : 
   20963         236 :     return cstorage;
   20964             : }

Generated by: LCOV version 1.14