LCOV - code coverage report
Current view: top level - src/backend/commands - tablecmds.c (source / functions) Hit Total Coverage
Test: PostgreSQL 17devel Lines: 6283 6842 91.8 %
Date: 2024-04-20 09:11:07 Functions: 201 202 99.5 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * tablecmds.c
       4             :  *    Commands for creating and altering table structures and settings
       5             :  *
       6             :  * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
       7             :  * Portions Copyright (c) 1994, Regents of the University of California
       8             :  *
       9             :  *
      10             :  * IDENTIFICATION
      11             :  *    src/backend/commands/tablecmds.c
      12             :  *
      13             :  *-------------------------------------------------------------------------
      14             :  */
      15             : #include "postgres.h"
      16             : 
      17             : #include "access/attmap.h"
      18             : #include "access/genam.h"
      19             : #include "access/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_rewrite.h"
      49             : #include "catalog/pg_statistic_ext.h"
      50             : #include "catalog/pg_tablespace.h"
      51             : #include "catalog/pg_trigger.h"
      52             : #include "catalog/pg_type.h"
      53             : #include "catalog/storage.h"
      54             : #include "catalog/storage_xlog.h"
      55             : #include "catalog/toasting.h"
      56             : #include "commands/cluster.h"
      57             : #include "commands/comment.h"
      58             : #include "commands/defrem.h"
      59             : #include "commands/event_trigger.h"
      60             : #include "commands/sequence.h"
      61             : #include "commands/tablecmds.h"
      62             : #include "commands/tablespace.h"
      63             : #include "commands/trigger.h"
      64             : #include "commands/typecmds.h"
      65             : #include "commands/user.h"
      66             : #include "commands/vacuum.h"
      67             : #include "executor/executor.h"
      68             : #include "foreign/fdwapi.h"
      69             : #include "foreign/foreign.h"
      70             : #include "miscadmin.h"
      71             : #include "nodes/makefuncs.h"
      72             : #include "nodes/nodeFuncs.h"
      73             : #include "nodes/parsenodes.h"
      74             : #include "optimizer/optimizer.h"
      75             : #include "parser/parse_coerce.h"
      76             : #include "parser/parse_collate.h"
      77             : #include "parser/parse_expr.h"
      78             : #include "parser/parse_relation.h"
      79             : #include "parser/parse_type.h"
      80             : #include "parser/parse_utilcmd.h"
      81             : #include "parser/parser.h"
      82             : #include "partitioning/partbounds.h"
      83             : #include "partitioning/partdesc.h"
      84             : #include "pgstat.h"
      85             : #include "rewrite/rewriteDefine.h"
      86             : #include "rewrite/rewriteHandler.h"
      87             : #include "rewrite/rewriteManip.h"
      88             : #include "storage/bufmgr.h"
      89             : #include "storage/lmgr.h"
      90             : #include "storage/lock.h"
      91             : #include "storage/predicate.h"
      92             : #include "storage/smgr.h"
      93             : #include "tcop/utility.h"
      94             : #include "utils/acl.h"
      95             : #include "utils/builtins.h"
      96             : #include "utils/fmgroids.h"
      97             : #include "utils/inval.h"
      98             : #include "utils/lsyscache.h"
      99             : #include "utils/memutils.h"
     100             : #include "utils/partcache.h"
     101             : #include "utils/relcache.h"
     102             : #include "utils/ruleutils.h"
     103             : #include "utils/snapmgr.h"
     104             : #include "utils/syscache.h"
     105             : #include "utils/timestamp.h"
     106             : #include "utils/typcache.h"
     107             : #include "utils/usercontext.h"
     108             : 
     109             : /*
     110             :  * ON COMMIT action list
     111             :  */
     112             : typedef struct OnCommitItem
     113             : {
     114             :     Oid         relid;          /* relid of relation */
     115             :     OnCommitAction oncommit;    /* what to do at end of xact */
     116             : 
     117             :     /*
     118             :      * If this entry was created during the current transaction,
     119             :      * creating_subid is the ID of the creating subxact; if created in a prior
     120             :      * transaction, creating_subid is zero.  If deleted during the current
     121             :      * transaction, deleting_subid is the ID of the deleting subxact; if no
     122             :      * deletion request is pending, deleting_subid is zero.
     123             :      */
     124             :     SubTransactionId creating_subid;
     125             :     SubTransactionId deleting_subid;
     126             : } OnCommitItem;
     127             : 
     128             : static List *on_commits = NIL;
     129             : 
     130             : 
     131             : /*
     132             :  * State information for ALTER TABLE
     133             :  *
     134             :  * The pending-work queue for an ALTER TABLE is a List of AlteredTableInfo
     135             :  * structs, one for each table modified by the operation (the named table
     136             :  * plus any child tables that are affected).  We save lists of subcommands
     137             :  * to apply to this table (possibly modified by parse transformation steps);
     138             :  * these lists will be executed in Phase 2.  If a Phase 3 step is needed,
     139             :  * necessary information is stored in the constraints and newvals lists.
     140             :  *
     141             :  * Phase 2 is divided into multiple passes; subcommands are executed in
     142             :  * a pass determined by subcommand type.
     143             :  */
     144             : 
     145             : typedef enum AlterTablePass
     146             : {
     147             :     AT_PASS_UNSET = -1,         /* UNSET will cause ERROR */
     148             :     AT_PASS_DROP,               /* DROP (all flavors) */
     149             :     AT_PASS_ALTER_TYPE,         /* ALTER COLUMN TYPE */
     150             :     AT_PASS_ADD_COL,            /* ADD COLUMN */
     151             :     AT_PASS_SET_EXPRESSION,     /* ALTER SET EXPRESSION */
     152             :     AT_PASS_OLD_COL_ATTRS,      /* re-install attnotnull */
     153             :     AT_PASS_OLD_INDEX,          /* re-add existing indexes */
     154             :     AT_PASS_OLD_CONSTR,         /* re-add existing constraints */
     155             :     /* We could support a RENAME COLUMN pass here, but not currently used */
     156             :     AT_PASS_ADD_CONSTR,         /* ADD constraints (initial examination) */
     157             :     AT_PASS_COL_ATTRS,          /* set column attributes, eg NOT NULL */
     158             :     AT_PASS_ADD_INDEXCONSTR,    /* ADD index-based constraints */
     159             :     AT_PASS_ADD_INDEX,          /* ADD indexes */
     160             :     AT_PASS_ADD_OTHERCONSTR,    /* ADD other constraints, defaults */
     161             :     AT_PASS_MISC,               /* other stuff */
     162             : } AlterTablePass;
     163             : 
     164             : #define AT_NUM_PASSES           (AT_PASS_MISC + 1)
     165             : 
     166             : typedef struct AlteredTableInfo
     167             : {
     168             :     /* Information saved before any work commences: */
     169             :     Oid         relid;          /* Relation to work on */
     170             :     char        relkind;        /* Its relkind */
     171             :     TupleDesc   oldDesc;        /* Pre-modification tuple descriptor */
     172             : 
     173             :     /*
     174             :      * Transiently set during Phase 2, normally set to NULL.
     175             :      *
     176             :      * ATRewriteCatalogs sets this when it starts, and closes when ATExecCmd
     177             :      * returns control.  This can be exploited by ATExecCmd subroutines to
     178             :      * close/reopen across transaction boundaries.
     179             :      */
     180             :     Relation    rel;
     181             : 
     182             :     /* Information saved by Phase 1 for Phase 2: */
     183             :     List       *subcmds[AT_NUM_PASSES]; /* Lists of AlterTableCmd */
     184             :     /* Information saved by Phases 1/2 for Phase 3: */
     185             :     List       *constraints;    /* List of NewConstraint */
     186             :     List       *newvals;        /* List of NewColumnValue */
     187             :     List       *afterStmts;     /* List of utility command parsetrees */
     188             :     bool        verify_new_notnull; /* T if we should recheck NOT NULL */
     189             :     int         rewrite;        /* Reason for forced rewrite, if any */
     190             :     bool        chgAccessMethod;    /* T if SET ACCESS METHOD is used */
     191             :     Oid         newAccessMethod;    /* new access method; 0 means no change,
     192             :                                      * if above is true */
     193             :     Oid         newTableSpace;  /* new tablespace; 0 means no change */
     194             :     bool        chgPersistence; /* T if SET LOGGED/UNLOGGED is used */
     195             :     char        newrelpersistence;  /* if above is true */
     196             :     Expr       *partition_constraint;   /* for attach partition validation */
     197             :     /* true, if validating default due to some other attach/detach */
     198             :     bool        validate_default;
     199             :     /* Objects to rebuild after completing ALTER TYPE operations */
     200             :     List       *changedConstraintOids;  /* OIDs of constraints to rebuild */
     201             :     List       *changedConstraintDefs;  /* string definitions of same */
     202             :     List       *changedIndexOids;   /* OIDs of indexes to rebuild */
     203             :     List       *changedIndexDefs;   /* string definitions of same */
     204             :     char       *replicaIdentityIndex;   /* index to reset as REPLICA IDENTITY */
     205             :     char       *clusterOnIndex; /* index to use for CLUSTER */
     206             :     List       *changedStatisticsOids;  /* OIDs of statistics to rebuild */
     207             :     List       *changedStatisticsDefs;  /* string definitions of same */
     208             : } AlteredTableInfo;
     209             : 
     210             : /* Struct describing one new constraint to check in Phase 3 scan */
     211             : /* Note: new not-null constraints are handled elsewhere */
     212             : typedef struct NewConstraint
     213             : {
     214             :     char       *name;           /* Constraint name, or NULL if none */
     215             :     ConstrType  contype;        /* CHECK or FOREIGN */
     216             :     Oid         refrelid;       /* PK rel, if FOREIGN */
     217             :     Oid         refindid;       /* OID of PK's index, if FOREIGN */
     218             :     bool        conwithperiod;  /* Whether the new FOREIGN KEY uses PERIOD */
     219             :     Oid         conid;          /* OID of pg_constraint entry, if FOREIGN */
     220             :     Node       *qual;           /* Check expr or CONSTR_FOREIGN Constraint */
     221             :     ExprState  *qualstate;      /* Execution state for CHECK expr */
     222             : } NewConstraint;
     223             : 
     224             : /*
     225             :  * Struct describing one new column value that needs to be computed during
     226             :  * Phase 3 copy (this could be either a new column with a non-null default, or
     227             :  * a column that we're changing the type of).  Columns without such an entry
     228             :  * are just copied from the old table during ATRewriteTable.  Note that the
     229             :  * expr is an expression over *old* table values, except when is_generated
     230             :  * is true; then it is an expression over columns of the *new* tuple.
     231             :  */
     232             : typedef struct NewColumnValue
     233             : {
     234             :     AttrNumber  attnum;         /* which column */
     235             :     Expr       *expr;           /* expression to compute */
     236             :     ExprState  *exprstate;      /* execution state */
     237             :     bool        is_generated;   /* is it a GENERATED expression? */
     238             : } NewColumnValue;
     239             : 
     240             : /*
     241             :  * Error-reporting support for RemoveRelations
     242             :  */
     243             : struct dropmsgstrings
     244             : {
     245             :     char        kind;
     246             :     int         nonexistent_code;
     247             :     const char *nonexistent_msg;
     248             :     const char *skipping_msg;
     249             :     const char *nota_msg;
     250             :     const char *drophint_msg;
     251             : };
     252             : 
     253             : static const struct dropmsgstrings dropmsgstringarray[] = {
     254             :     {RELKIND_RELATION,
     255             :         ERRCODE_UNDEFINED_TABLE,
     256             :         gettext_noop("table \"%s\" does not exist"),
     257             :         gettext_noop("table \"%s\" does not exist, skipping"),
     258             :         gettext_noop("\"%s\" is not a table"),
     259             :     gettext_noop("Use DROP TABLE to remove a table.")},
     260             :     {RELKIND_SEQUENCE,
     261             :         ERRCODE_UNDEFINED_TABLE,
     262             :         gettext_noop("sequence \"%s\" does not exist"),
     263             :         gettext_noop("sequence \"%s\" does not exist, skipping"),
     264             :         gettext_noop("\"%s\" is not a sequence"),
     265             :     gettext_noop("Use DROP SEQUENCE to remove a sequence.")},
     266             :     {RELKIND_VIEW,
     267             :         ERRCODE_UNDEFINED_TABLE,
     268             :         gettext_noop("view \"%s\" does not exist"),
     269             :         gettext_noop("view \"%s\" does not exist, skipping"),
     270             :         gettext_noop("\"%s\" is not a view"),
     271             :     gettext_noop("Use DROP VIEW to remove a view.")},
     272             :     {RELKIND_MATVIEW,
     273             :         ERRCODE_UNDEFINED_TABLE,
     274             :         gettext_noop("materialized view \"%s\" does not exist"),
     275             :         gettext_noop("materialized view \"%s\" does not exist, skipping"),
     276             :         gettext_noop("\"%s\" is not a materialized view"),
     277             :     gettext_noop("Use DROP MATERIALIZED VIEW to remove a materialized view.")},
     278             :     {RELKIND_INDEX,
     279             :         ERRCODE_UNDEFINED_OBJECT,
     280             :         gettext_noop("index \"%s\" does not exist"),
     281             :         gettext_noop("index \"%s\" does not exist, skipping"),
     282             :         gettext_noop("\"%s\" is not an index"),
     283             :     gettext_noop("Use DROP INDEX to remove an index.")},
     284             :     {RELKIND_COMPOSITE_TYPE,
     285             :         ERRCODE_UNDEFINED_OBJECT,
     286             :         gettext_noop("type \"%s\" does not exist"),
     287             :         gettext_noop("type \"%s\" does not exist, skipping"),
     288             :         gettext_noop("\"%s\" is not a type"),
     289             :     gettext_noop("Use DROP TYPE to remove a type.")},
     290             :     {RELKIND_FOREIGN_TABLE,
     291             :         ERRCODE_UNDEFINED_OBJECT,
     292             :         gettext_noop("foreign table \"%s\" does not exist"),
     293             :         gettext_noop("foreign table \"%s\" does not exist, skipping"),
     294             :         gettext_noop("\"%s\" is not a foreign table"),
     295             :     gettext_noop("Use DROP FOREIGN TABLE to remove a foreign table.")},
     296             :     {RELKIND_PARTITIONED_TABLE,
     297             :         ERRCODE_UNDEFINED_TABLE,
     298             :         gettext_noop("table \"%s\" does not exist"),
     299             :         gettext_noop("table \"%s\" does not exist, skipping"),
     300             :         gettext_noop("\"%s\" is not a table"),
     301             :     gettext_noop("Use DROP TABLE to remove a table.")},
     302             :     {RELKIND_PARTITIONED_INDEX,
     303             :         ERRCODE_UNDEFINED_OBJECT,
     304             :         gettext_noop("index \"%s\" does not exist"),
     305             :         gettext_noop("index \"%s\" does not exist, skipping"),
     306             :         gettext_noop("\"%s\" is not an index"),
     307             :     gettext_noop("Use DROP INDEX to remove an index.")},
     308             :     {'\0', 0, NULL, NULL, NULL, NULL}
     309             : };
     310             : 
     311             : /* communication between RemoveRelations and RangeVarCallbackForDropRelation */
     312             : struct DropRelationCallbackState
     313             : {
     314             :     /* These fields are set by RemoveRelations: */
     315             :     char        expected_relkind;
     316             :     LOCKMODE    heap_lockmode;
     317             :     /* These fields are state to track which subsidiary locks are held: */
     318             :     Oid         heapOid;
     319             :     Oid         partParentOid;
     320             :     /* These fields are passed back by RangeVarCallbackForDropRelation: */
     321             :     char        actual_relkind;
     322             :     char        actual_relpersistence;
     323             : };
     324             : 
     325             : /* Alter table target-type flags for ATSimplePermissions */
     326             : #define     ATT_TABLE               0x0001
     327             : #define     ATT_VIEW                0x0002
     328             : #define     ATT_MATVIEW             0x0004
     329             : #define     ATT_INDEX               0x0008
     330             : #define     ATT_COMPOSITE_TYPE      0x0010
     331             : #define     ATT_FOREIGN_TABLE       0x0020
     332             : #define     ATT_PARTITIONED_INDEX   0x0040
     333             : #define     ATT_SEQUENCE            0x0080
     334             : 
     335             : /*
     336             :  * ForeignTruncateInfo
     337             :  *
     338             :  * Information related to truncation of foreign tables.  This is used for
     339             :  * the elements in a hash table. It uses the server OID as lookup key,
     340             :  * and includes a per-server list of all foreign tables involved in the
     341             :  * truncation.
     342             :  */
     343             : typedef struct ForeignTruncateInfo
     344             : {
     345             :     Oid         serverid;
     346             :     List       *rels;
     347             : } ForeignTruncateInfo;
     348             : 
     349             : /*
     350             :  * Partition tables are expected to be dropped when the parent partitioned
     351             :  * table gets dropped. Hence for partitioning we use AUTO dependency.
     352             :  * Otherwise, for regular inheritance use NORMAL dependency.
     353             :  */
     354             : #define child_dependency_type(child_is_partition)   \
     355             :     ((child_is_partition) ? DEPENDENCY_AUTO : DEPENDENCY_NORMAL)
     356             : 
     357             : static void truncate_check_rel(Oid relid, Form_pg_class reltuple);
     358             : static void truncate_check_perms(Oid relid, Form_pg_class reltuple);
     359             : static void truncate_check_activity(Relation rel);
     360             : static void RangeVarCallbackForTruncate(const RangeVar *relation,
     361             :                                         Oid relId, Oid oldRelId, void *arg);
     362             : static List *MergeAttributes(List *columns, const List *supers, char relpersistence,
     363             :                              bool is_partition, List **supconstr,
     364             :                              List **supnotnulls);
     365             : static List *MergeCheckConstraint(List *constraints, const char *name, Node *expr);
     366             : static void MergeChildAttribute(List *inh_columns, int exist_attno, int newcol_attno, const ColumnDef *newdef);
     367             : static ColumnDef *MergeInheritedAttribute(List *inh_columns, int exist_attno, const ColumnDef *newdef);
     368             : static void MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel, bool ispartition);
     369             : static void MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel);
     370             : static void StoreCatalogInheritance(Oid relationId, List *supers,
     371             :                                     bool child_is_partition);
     372             : static void StoreCatalogInheritance1(Oid relationId, Oid parentOid,
     373             :                                      int32 seqNumber, Relation inhRelation,
     374             :                                      bool child_is_partition);
     375             : static int  findAttrByName(const char *attributeName, const List *columns);
     376             : static void AlterIndexNamespaces(Relation classRel, Relation rel,
     377             :                                  Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved);
     378             : static void AlterSeqNamespaces(Relation classRel, Relation rel,
     379             :                                Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved,
     380             :                                LOCKMODE lockmode);
     381             : static ObjectAddress ATExecAlterConstraint(Relation rel, AlterTableCmd *cmd,
     382             :                                            bool recurse, bool recursing, LOCKMODE lockmode);
     383             : static bool ATExecAlterConstrRecurse(Constraint *cmdcon, Relation conrel, Relation tgrel,
     384             :                                      Relation rel, HeapTuple contuple, List **otherrelids,
     385             :                                      LOCKMODE lockmode);
     386             : static ObjectAddress ATExecValidateConstraint(List **wqueue,
     387             :                                               Relation rel, char *constrName,
     388             :                                               bool recurse, bool recursing, LOCKMODE lockmode);
     389             : static int  transformColumnNameList(Oid relId, List *colList,
     390             :                                     int16 *attnums, Oid *atttypids);
     391             : static int  transformFkeyGetPrimaryKey(Relation pkrel, Oid *indexOid,
     392             :                                        List **attnamelist,
     393             :                                        int16 *attnums, Oid *atttypids,
     394             :                                        Oid *opclasses, bool *pk_has_without_overlaps);
     395             : static Oid  transformFkeyCheckAttrs(Relation pkrel,
     396             :                                     int numattrs, int16 *attnums,
     397             :                                     bool with_period, Oid *opclasses,
     398             :                                     bool *pk_has_without_overlaps);
     399             : static void checkFkeyPermissions(Relation rel, int16 *attnums, int natts);
     400             : static CoercionPathType findFkeyCast(Oid targetTypeId, Oid sourceTypeId,
     401             :                                      Oid *funcid);
     402             : static void validateForeignKeyConstraint(char *conname,
     403             :                                          Relation rel, Relation pkrel,
     404             :                                          Oid pkindOid, Oid constraintOid, bool hasperiod);
     405             : static void ATController(AlterTableStmt *parsetree,
     406             :                          Relation rel, List *cmds, bool recurse, LOCKMODE lockmode,
     407             :                          AlterTableUtilityContext *context);
     408             : static void ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
     409             :                       bool recurse, bool recursing, LOCKMODE lockmode,
     410             :                       AlterTableUtilityContext *context);
     411             : static void ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode,
     412             :                               AlterTableUtilityContext *context);
     413             : static void ATExecCmd(List **wqueue, AlteredTableInfo *tab,
     414             :                       AlterTableCmd *cmd, LOCKMODE lockmode, AlterTablePass cur_pass,
     415             :                       AlterTableUtilityContext *context);
     416             : static AlterTableCmd *ATParseTransformCmd(List **wqueue, AlteredTableInfo *tab,
     417             :                                           Relation rel, AlterTableCmd *cmd,
     418             :                                           bool recurse, LOCKMODE lockmode,
     419             :                                           AlterTablePass cur_pass,
     420             :                                           AlterTableUtilityContext *context);
     421             : static void ATRewriteTables(AlterTableStmt *parsetree,
     422             :                             List **wqueue, LOCKMODE lockmode,
     423             :                             AlterTableUtilityContext *context);
     424             : static void ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode);
     425             : static AlteredTableInfo *ATGetQueueEntry(List **wqueue, Relation rel);
     426             : static void ATSimplePermissions(AlterTableType cmdtype, Relation rel, int allowed_targets);
     427             : static void ATSimpleRecursion(List **wqueue, Relation rel,
     428             :                               AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode,
     429             :                               AlterTableUtilityContext *context);
     430             : static void ATCheckPartitionsNotInUse(Relation rel, LOCKMODE lockmode);
     431             : static void ATTypedTableRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd,
     432             :                                   LOCKMODE lockmode,
     433             :                                   AlterTableUtilityContext *context);
     434             : static List *find_typed_table_dependencies(Oid typeOid, const char *typeName,
     435             :                                            DropBehavior behavior);
     436             : static void ATPrepAddColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
     437             :                             bool is_view, AlterTableCmd *cmd, LOCKMODE lockmode,
     438             :                             AlterTableUtilityContext *context);
     439             : static ObjectAddress ATExecAddColumn(List **wqueue, AlteredTableInfo *tab,
     440             :                                      Relation rel, AlterTableCmd **cmd,
     441             :                                      bool recurse, bool recursing,
     442             :                                      LOCKMODE lockmode, AlterTablePass cur_pass,
     443             :                                      AlterTableUtilityContext *context);
     444             : static bool check_for_column_name_collision(Relation rel, const char *colname,
     445             :                                             bool if_not_exists);
     446             : static void add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid);
     447             : static void add_column_collation_dependency(Oid relid, int32 attnum, Oid collid);
     448             : static ObjectAddress ATExecDropNotNull(Relation rel, const char *colName, bool recurse,
     449             :                                        LOCKMODE lockmode);
     450             : static bool set_attnotnull(List **wqueue, Relation rel,
     451             :                            AttrNumber attnum, bool recurse, LOCKMODE lockmode);
     452             : static ObjectAddress ATExecSetNotNull(List **wqueue, Relation rel,
     453             :                                       char *constrname, char *colName,
     454             :                                       bool recurse, bool recursing,
     455             :                                       List **readyRels, LOCKMODE lockmode);
     456             : static ObjectAddress ATExecSetAttNotNull(List **wqueue, Relation rel,
     457             :                                          const char *colName, LOCKMODE lockmode);
     458             : static bool NotNullImpliedByRelConstraints(Relation rel, Form_pg_attribute attr);
     459             : static bool ConstraintImpliedByRelConstraint(Relation scanrel,
     460             :                                              List *testConstraint, List *provenConstraint);
     461             : static ObjectAddress ATExecColumnDefault(Relation rel, const char *colName,
     462             :                                          Node *newDefault, LOCKMODE lockmode);
     463             : static ObjectAddress ATExecCookedColumnDefault(Relation rel, AttrNumber attnum,
     464             :                                                Node *newDefault);
     465             : static ObjectAddress ATExecAddIdentity(Relation rel, const char *colName,
     466             :                                        Node *def, LOCKMODE lockmode, bool recurse, bool recursing);
     467             : static ObjectAddress ATExecSetIdentity(Relation rel, const char *colName,
     468             :                                        Node *def, LOCKMODE lockmode, bool recurse, bool recursing);
     469             : static ObjectAddress ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode,
     470             :                                         bool recurse, bool recursing);
     471             : static ObjectAddress ATExecSetExpression(AlteredTableInfo *tab, Relation rel, const char *colName,
     472             :                                          Node *newExpr, LOCKMODE lockmode);
     473             : static void ATPrepDropExpression(Relation rel, AlterTableCmd *cmd, bool recurse, bool recursing, LOCKMODE lockmode);
     474             : static ObjectAddress ATExecDropExpression(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode);
     475             : static ObjectAddress ATExecSetStatistics(Relation rel, const char *colName, int16 colNum,
     476             :                                          Node *newValue, LOCKMODE lockmode);
     477             : static ObjectAddress ATExecSetOptions(Relation rel, const char *colName,
     478             :                                       Node *options, bool isReset, LOCKMODE lockmode);
     479             : static ObjectAddress ATExecSetStorage(Relation rel, const char *colName,
     480             :                                       Node *newValue, LOCKMODE lockmode);
     481             : static void ATPrepDropColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
     482             :                              AlterTableCmd *cmd, LOCKMODE lockmode,
     483             :                              AlterTableUtilityContext *context);
     484             : static ObjectAddress ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
     485             :                                       DropBehavior behavior,
     486             :                                       bool recurse, bool recursing,
     487             :                                       bool missing_ok, LOCKMODE lockmode,
     488             :                                       ObjectAddresses *addrs);
     489             : static void ATPrepAddPrimaryKey(List **wqueue, Relation rel, AlterTableCmd *cmd,
     490             :                                 LOCKMODE lockmode, AlterTableUtilityContext *context);
     491             : static ObjectAddress ATExecAddIndex(AlteredTableInfo *tab, Relation rel,
     492             :                                     IndexStmt *stmt, bool is_rebuild, LOCKMODE lockmode);
     493             : static ObjectAddress ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
     494             :                                          CreateStatsStmt *stmt, bool is_rebuild, LOCKMODE lockmode);
     495             : static ObjectAddress ATExecAddConstraint(List **wqueue,
     496             :                                          AlteredTableInfo *tab, Relation rel,
     497             :                                          Constraint *newConstraint, bool recurse, bool is_readd,
     498             :                                          LOCKMODE lockmode);
     499             : static char *ChooseForeignKeyConstraintNameAddition(List *colnames);
     500             : static ObjectAddress ATExecAddIndexConstraint(AlteredTableInfo *tab, Relation rel,
     501             :                                               IndexStmt *stmt, LOCKMODE lockmode);
     502             : static ObjectAddress ATAddCheckNNConstraint(List **wqueue,
     503             :                                             AlteredTableInfo *tab, Relation rel,
     504             :                                             Constraint *constr,
     505             :                                             bool recurse, bool recursing, bool is_readd,
     506             :                                             LOCKMODE lockmode);
     507             : static ObjectAddress ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab,
     508             :                                                Relation rel, Constraint *fkconstraint,
     509             :                                                bool recurse, bool recursing,
     510             :                                                LOCKMODE lockmode);
     511             : static ObjectAddress addFkRecurseReferenced(List **wqueue, Constraint *fkconstraint,
     512             :                                             Relation rel, Relation pkrel, Oid indexOid, Oid parentConstr,
     513             :                                             int numfks, int16 *pkattnum, int16 *fkattnum,
     514             :                                             Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators,
     515             :                                             int numfkdelsetcols, int16 *fkdelsetcols,
     516             :                                             bool old_check_ok,
     517             :                                             Oid parentDelTrigger, Oid parentUpdTrigger,
     518             :                                             bool with_period);
     519             : static void validateFkOnDeleteSetColumns(int numfks, const int16 *fkattnums,
     520             :                                          int numfksetcols, const int16 *fksetcolsattnums,
     521             :                                          List *fksetcols);
     522             : static void addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint,
     523             :                                     Relation rel, Relation pkrel, Oid indexOid, Oid parentConstr,
     524             :                                     int numfks, int16 *pkattnum, int16 *fkattnum,
     525             :                                     Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators,
     526             :                                     int numfkdelsetcols, int16 *fkdelsetcols,
     527             :                                     bool old_check_ok, LOCKMODE lockmode,
     528             :                                     Oid parentInsTrigger, Oid parentUpdTrigger,
     529             :                                     bool with_period);
     530             : 
     531             : static void CloneForeignKeyConstraints(List **wqueue, Relation parentRel,
     532             :                                        Relation partitionRel);
     533             : static void CloneFkReferenced(Relation parentRel, Relation partitionRel);
     534             : static void CloneFkReferencing(List **wqueue, Relation parentRel,
     535             :                                Relation partRel);
     536             : static void createForeignKeyCheckTriggers(Oid myRelOid, Oid refRelOid,
     537             :                                           Constraint *fkconstraint, Oid constraintOid,
     538             :                                           Oid indexOid,
     539             :                                           Oid parentInsTrigger, Oid parentUpdTrigger,
     540             :                                           Oid *insertTrigOid, Oid *updateTrigOid);
     541             : static void createForeignKeyActionTriggers(Relation rel, Oid refRelOid,
     542             :                                            Constraint *fkconstraint, Oid constraintOid,
     543             :                                            Oid indexOid,
     544             :                                            Oid parentDelTrigger, Oid parentUpdTrigger,
     545             :                                            Oid *deleteTrigOid, Oid *updateTrigOid);
     546             : static bool tryAttachPartitionForeignKey(ForeignKeyCacheInfo *fk,
     547             :                                          Oid partRelid,
     548             :                                          Oid parentConstrOid, int numfks,
     549             :                                          AttrNumber *mapped_conkey, AttrNumber *confkey,
     550             :                                          Oid *conpfeqop,
     551             :                                          Oid parentInsTrigger,
     552             :                                          Oid parentUpdTrigger,
     553             :                                          Relation trigrel);
     554             : static void GetForeignKeyActionTriggers(Relation trigrel,
     555             :                                         Oid conoid, Oid confrelid, Oid conrelid,
     556             :                                         Oid *deleteTriggerOid,
     557             :                                         Oid *updateTriggerOid);
     558             : static void GetForeignKeyCheckTriggers(Relation trigrel,
     559             :                                        Oid conoid, Oid confrelid, Oid conrelid,
     560             :                                        Oid *insertTriggerOid,
     561             :                                        Oid *updateTriggerOid);
     562             : static void ATExecDropConstraint(Relation rel, const char *constrName,
     563             :                                  DropBehavior behavior, bool recurse,
     564             :                                  bool missing_ok, LOCKMODE lockmode);
     565             : static ObjectAddress dropconstraint_internal(Relation rel,
     566             :                                              HeapTuple constraintTup, DropBehavior behavior,
     567             :                                              bool recurse, bool recursing,
     568             :                                              bool missing_ok, List **readyRels,
     569             :                                              LOCKMODE lockmode);
     570             : static void ATPrepAlterColumnType(List **wqueue,
     571             :                                   AlteredTableInfo *tab, Relation rel,
     572             :                                   bool recurse, bool recursing,
     573             :                                   AlterTableCmd *cmd, LOCKMODE lockmode,
     574             :                                   AlterTableUtilityContext *context);
     575             : static bool ATColumnChangeRequiresRewrite(Node *expr, AttrNumber varattno);
     576             : static ObjectAddress ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
     577             :                                            AlterTableCmd *cmd, LOCKMODE lockmode);
     578             : static void RememberAllDependentForRebuilding(AlteredTableInfo *tab, AlterTableType subtype,
     579             :                                               Relation rel, AttrNumber attnum, const char *colName);
     580             : static void RememberConstraintForRebuilding(Oid conoid, AlteredTableInfo *tab);
     581             : static void RememberIndexForRebuilding(Oid indoid, AlteredTableInfo *tab);
     582             : static void RememberStatisticsForRebuilding(Oid stxoid, AlteredTableInfo *tab);
     583             : static void ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab,
     584             :                                    LOCKMODE lockmode);
     585             : static void ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId,
     586             :                                  char *cmd, List **wqueue, LOCKMODE lockmode,
     587             :                                  bool rewrite);
     588             : static void RebuildConstraintComment(AlteredTableInfo *tab, AlterTablePass pass,
     589             :                                      Oid objid, Relation rel, List *domname,
     590             :                                      const char *conname);
     591             : static void TryReuseIndex(Oid oldId, IndexStmt *stmt);
     592             : static void TryReuseForeignKey(Oid oldId, Constraint *con);
     593             : static ObjectAddress ATExecAlterColumnGenericOptions(Relation rel, const char *colName,
     594             :                                                      List *options, LOCKMODE lockmode);
     595             : static void change_owner_fix_column_acls(Oid relationOid,
     596             :                                          Oid oldOwnerId, Oid newOwnerId);
     597             : static void change_owner_recurse_to_sequences(Oid relationOid,
     598             :                                               Oid newOwnerId, LOCKMODE lockmode);
     599             : static ObjectAddress ATExecClusterOn(Relation rel, const char *indexName,
     600             :                                      LOCKMODE lockmode);
     601             : static void ATExecDropCluster(Relation rel, LOCKMODE lockmode);
     602             : static void ATPrepSetAccessMethod(AlteredTableInfo *tab, Relation rel, const char *amname);
     603             : static void ATExecSetAccessMethodNoStorage(Relation rel, Oid newAccessMethod);
     604             : static bool ATPrepChangePersistence(Relation rel, bool toLogged);
     605             : static void ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel,
     606             :                                 const char *tablespacename, LOCKMODE lockmode);
     607             : static void ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode);
     608             : static void ATExecSetTableSpaceNoStorage(Relation rel, Oid newTableSpace);
     609             : static void ATExecSetRelOptions(Relation rel, List *defList,
     610             :                                 AlterTableType operation,
     611             :                                 LOCKMODE lockmode);
     612             : static void ATExecEnableDisableTrigger(Relation rel, const char *trigname,
     613             :                                        char fires_when, bool skip_system, bool recurse,
     614             :                                        LOCKMODE lockmode);
     615             : static void ATExecEnableDisableRule(Relation rel, const char *rulename,
     616             :                                     char fires_when, LOCKMODE lockmode);
     617             : static void ATPrepAddInherit(Relation child_rel);
     618             : static ObjectAddress ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode);
     619             : static ObjectAddress ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode);
     620             : static void drop_parent_dependency(Oid relid, Oid refclassid, Oid refobjid,
     621             :                                    DependencyType deptype);
     622             : static ObjectAddress ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode);
     623             : static void ATExecDropOf(Relation rel, LOCKMODE lockmode);
     624             : static void ATExecReplicaIdentity(Relation rel, ReplicaIdentityStmt *stmt, LOCKMODE lockmode);
     625             : static void ATExecGenericOptions(Relation rel, List *options);
     626             : static void ATExecSetRowSecurity(Relation rel, bool rls);
     627             : static void ATExecForceNoForceRowSecurity(Relation rel, bool force_rls);
     628             : static ObjectAddress ATExecSetCompression(Relation rel,
     629             :                                           const char *column, Node *newValue, LOCKMODE lockmode);
     630             : 
     631             : static void index_copy_data(Relation rel, RelFileLocator newrlocator);
     632             : static const char *storage_name(char c);
     633             : 
     634             : static void RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid,
     635             :                                             Oid oldRelOid, void *arg);
     636             : static void RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid,
     637             :                                              Oid oldrelid, void *arg);
     638             : static PartitionSpec *transformPartitionSpec(Relation rel, PartitionSpec *partspec);
     639             : static void ComputePartitionAttrs(ParseState *pstate, Relation rel, List *partParams, AttrNumber *partattrs,
     640             :                                   List **partexprs, Oid *partopclass, Oid *partcollation,
     641             :                                   PartitionStrategy strategy);
     642             : static void CreateInheritance(Relation child_rel, Relation parent_rel, bool ispartition);
     643             : static void RemoveInheritance(Relation child_rel, Relation parent_rel,
     644             :                               bool expect_detached);
     645             : static void ATInheritAdjustNotNulls(Relation parent_rel, Relation child_rel,
     646             :                                     int inhcount);
     647             : static ObjectAddress ATExecAttachPartition(List **wqueue, Relation rel,
     648             :                                            PartitionCmd *cmd,
     649             :                                            AlterTableUtilityContext *context);
     650             : static void AttachPartitionEnsureIndexes(List **wqueue, Relation rel, Relation attachrel);
     651             : static void QueuePartitionConstraintValidation(List **wqueue, Relation scanrel,
     652             :                                                List *partConstraint,
     653             :                                                bool validate_default);
     654             : static void CloneRowTriggersToPartition(Relation parent, Relation partition);
     655             : static void DetachAddConstraintIfNeeded(List **wqueue, Relation partRel);
     656             : static void DropClonedTriggersFromPartition(Oid partitionId);
     657             : static ObjectAddress ATExecDetachPartition(List **wqueue, AlteredTableInfo *tab,
     658             :                                            Relation rel, RangeVar *name,
     659             :                                            bool concurrent);
     660             : static void DetachPartitionFinalize(Relation rel, Relation partRel,
     661             :                                     bool concurrent, Oid defaultPartOid);
     662             : static ObjectAddress ATExecDetachPartitionFinalize(Relation rel, RangeVar *name);
     663             : static ObjectAddress ATExecAttachPartitionIdx(List **wqueue, Relation parentIdx,
     664             :                                               RangeVar *name);
     665             : static void validatePartitionedIndex(Relation partedIdx, Relation partedTbl);
     666             : static void refuseDupeIndexAttach(Relation parentIdx, Relation partIdx,
     667             :                                   Relation partitionTbl);
     668             : static void verifyPartitionIndexNotNull(IndexInfo *iinfo, Relation partIdx);
     669             : static List *GetParentedForeignKeyRefs(Relation partition);
     670             : static void ATDetachCheckNoForeignKeyRefs(Relation partition);
     671             : static char GetAttributeCompression(Oid atttypid, const char *compression);
     672             : static char GetAttributeStorage(Oid atttypid, const char *storagemode);
     673             : 
     674             : static void ATExecSplitPartition(List **wqueue, AlteredTableInfo *tab,
     675             :                                  Relation rel, PartitionCmd *cmd,
     676             :                                  AlterTableUtilityContext *context);
     677             : static void ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel,
     678             :                                   PartitionCmd *cmd, AlterTableUtilityContext *context);
     679             : 
     680             : /* ----------------------------------------------------------------
     681             :  *      DefineRelation
     682             :  *              Creates a new relation.
     683             :  *
     684             :  * stmt carries parsetree information from an ordinary CREATE TABLE statement.
     685             :  * The other arguments are used to extend the behavior for other cases:
     686             :  * relkind: relkind to assign to the new relation
     687             :  * ownerId: if not InvalidOid, use this as the new relation's owner.
     688             :  * typaddress: if not null, it's set to the pg_type entry's address.
     689             :  * queryString: for error reporting
     690             :  *
     691             :  * Note that permissions checks are done against current user regardless of
     692             :  * ownerId.  A nonzero ownerId is used when someone is creating a relation
     693             :  * "on behalf of" someone else, so we still want to see that the current user
     694             :  * has permissions to do it.
     695             :  *
     696             :  * If successful, returns the address of the new relation.
     697             :  * ----------------------------------------------------------------
     698             :  */
     699             : ObjectAddress
     700       53208 : DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
     701             :                ObjectAddress *typaddress, const char *queryString)
     702             : {
     703             :     char        relname[NAMEDATALEN];
     704             :     Oid         namespaceId;
     705             :     Oid         relationId;
     706             :     Oid         tablespaceId;
     707             :     Relation    rel;
     708             :     TupleDesc   descriptor;
     709             :     List       *inheritOids;
     710             :     List       *old_constraints;
     711             :     List       *old_notnulls;
     712             :     List       *rawDefaults;
     713             :     List       *cookedDefaults;
     714             :     List       *nncols;
     715             :     Datum       reloptions;
     716             :     ListCell   *listptr;
     717             :     AttrNumber  attnum;
     718             :     bool        partitioned;
     719             :     static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
     720             :     Oid         ofTypeId;
     721             :     ObjectAddress address;
     722             :     LOCKMODE    parentLockmode;
     723       53208 :     Oid         accessMethodId = InvalidOid;
     724             : 
     725             :     /*
     726             :      * Truncate relname to appropriate length (probably a waste of time, as
     727             :      * parser should have done this already).
     728             :      */
     729       53208 :     strlcpy(relname, stmt->relation->relname, NAMEDATALEN);
     730             : 
     731             :     /*
     732             :      * Check consistency of arguments
     733             :      */
     734       53208 :     if (stmt->oncommit != ONCOMMIT_NOOP
     735         178 :         && stmt->relation->relpersistence != RELPERSISTENCE_TEMP)
     736          12 :         ereport(ERROR,
     737             :                 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
     738             :                  errmsg("ON COMMIT can only be used on temporary tables")));
     739             : 
     740       53196 :     if (stmt->partspec != NULL)
     741             :     {
     742        4882 :         if (relkind != RELKIND_RELATION)
     743           0 :             elog(ERROR, "unexpected relkind: %d", (int) relkind);
     744             : 
     745        4882 :         relkind = RELKIND_PARTITIONED_TABLE;
     746        4882 :         partitioned = true;
     747             :     }
     748             :     else
     749       48314 :         partitioned = false;
     750             : 
     751             :     /*
     752             :      * Look up the namespace in which we are supposed to create the relation,
     753             :      * check we have permission to create there, lock it against concurrent
     754             :      * drop, and mark stmt->relation as RELPERSISTENCE_TEMP if a temporary
     755             :      * namespace is selected.
     756             :      */
     757             :     namespaceId =
     758       53196 :         RangeVarGetAndCheckCreationNamespace(stmt->relation, NoLock, NULL);
     759             : 
     760             :     /*
     761             :      * Security check: disallow creating temp tables from security-restricted
     762             :      * code.  This is needed because calling code might not expect untrusted
     763             :      * tables to appear in pg_temp at the front of its search path.
     764             :      */
     765       53196 :     if (stmt->relation->relpersistence == RELPERSISTENCE_TEMP
     766        2960 :         && InSecurityRestrictedOperation())
     767           0 :         ereport(ERROR,
     768             :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
     769             :                  errmsg("cannot create temporary table within security-restricted operation")));
     770             : 
     771             :     /*
     772             :      * Determine the lockmode to use when scanning parents.  A self-exclusive
     773             :      * lock is needed here.
     774             :      *
     775             :      * For regular inheritance, if two backends attempt to add children to the
     776             :      * same parent simultaneously, and that parent has no pre-existing
     777             :      * children, then both will attempt to update the parent's relhassubclass
     778             :      * field, leading to a "tuple concurrently updated" error.  Also, this
     779             :      * interlocks against a concurrent ANALYZE on the parent table, which
     780             :      * might otherwise be attempting to clear the parent's relhassubclass
     781             :      * field, if its previous children were recently dropped.
     782             :      *
     783             :      * If the child table is a partition, then we instead grab an exclusive
     784             :      * lock on the parent because its partition descriptor will be changed by
     785             :      * addition of the new partition.
     786             :      */
     787       53196 :     parentLockmode = (stmt->partbound != NULL ? AccessExclusiveLock :
     788             :                       ShareUpdateExclusiveLock);
     789             : 
     790             :     /* Determine the list of OIDs of the parents. */
     791       53196 :     inheritOids = NIL;
     792       63566 :     foreach(listptr, stmt->inhRelations)
     793             :     {
     794       10370 :         RangeVar   *rv = (RangeVar *) lfirst(listptr);
     795             :         Oid         parentOid;
     796             : 
     797       10370 :         parentOid = RangeVarGetRelid(rv, parentLockmode, false);
     798             : 
     799             :         /*
     800             :          * Reject duplications in the list of parents.
     801             :          */
     802       10370 :         if (list_member_oid(inheritOids, parentOid))
     803           0 :             ereport(ERROR,
     804             :                     (errcode(ERRCODE_DUPLICATE_TABLE),
     805             :                      errmsg("relation \"%s\" would be inherited from more than once",
     806             :                             get_rel_name(parentOid))));
     807             : 
     808       10370 :         inheritOids = lappend_oid(inheritOids, parentOid);
     809             :     }
     810             : 
     811             :     /*
     812             :      * Select tablespace to use: an explicitly indicated one, or (in the case
     813             :      * of a partitioned table) the parent's, if it has one.
     814             :      */
     815       53196 :     if (stmt->tablespacename)
     816             :     {
     817         110 :         tablespaceId = get_tablespace_oid(stmt->tablespacename, false);
     818             : 
     819         104 :         if (partitioned && tablespaceId == MyDatabaseTableSpace)
     820           6 :             ereport(ERROR,
     821             :                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     822             :                      errmsg("cannot specify default tablespace for partitioned relations")));
     823             :     }
     824       53086 :     else if (stmt->partbound)
     825             :     {
     826             :         Assert(list_length(inheritOids) == 1);
     827        8268 :         tablespaceId = get_rel_tablespace(linitial_oid(inheritOids));
     828             :     }
     829             :     else
     830       44818 :         tablespaceId = InvalidOid;
     831             : 
     832             :     /* still nothing? use the default */
     833       53184 :     if (!OidIsValid(tablespaceId))
     834       53064 :         tablespaceId = GetDefaultTablespace(stmt->relation->relpersistence,
     835             :                                             partitioned);
     836             : 
     837             :     /* Check permissions except when using database's default */
     838       53178 :     if (OidIsValid(tablespaceId) && tablespaceId != MyDatabaseTableSpace)
     839             :     {
     840             :         AclResult   aclresult;
     841             : 
     842         138 :         aclresult = object_aclcheck(TableSpaceRelationId, tablespaceId, GetUserId(),
     843             :                                     ACL_CREATE);
     844         138 :         if (aclresult != ACLCHECK_OK)
     845           6 :             aclcheck_error(aclresult, OBJECT_TABLESPACE,
     846           6 :                            get_tablespace_name(tablespaceId));
     847             :     }
     848             : 
     849             :     /* In all cases disallow placing user relations in pg_global */
     850       53172 :     if (tablespaceId == GLOBALTABLESPACE_OID)
     851          18 :         ereport(ERROR,
     852             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     853             :                  errmsg("only shared relations can be placed in pg_global tablespace")));
     854             : 
     855             :     /* Identify user ID that will own the table */
     856       53154 :     if (!OidIsValid(ownerId))
     857       52924 :         ownerId = GetUserId();
     858             : 
     859             :     /*
     860             :      * Parse and validate reloptions, if any.
     861             :      */
     862       53154 :     reloptions = transformRelOptions((Datum) 0, stmt->options, NULL, validnsps,
     863             :                                      true, false);
     864             : 
     865       53136 :     switch (relkind)
     866             :     {
     867       13370 :         case RELKIND_VIEW:
     868       13370 :             (void) view_reloptions(reloptions, true);
     869       13352 :             break;
     870        4864 :         case RELKIND_PARTITIONED_TABLE:
     871        4864 :             (void) partitioned_table_reloptions(reloptions, true);
     872        4858 :             break;
     873       34902 :         default:
     874       34902 :             (void) heap_reloptions(relkind, reloptions, true);
     875             :     }
     876             : 
     877       53016 :     if (stmt->ofTypename)
     878             :     {
     879             :         AclResult   aclresult;
     880             : 
     881          86 :         ofTypeId = typenameTypeId(NULL, stmt->ofTypename);
     882             : 
     883          86 :         aclresult = object_aclcheck(TypeRelationId, ofTypeId, GetUserId(), ACL_USAGE);
     884          86 :         if (aclresult != ACLCHECK_OK)
     885           6 :             aclcheck_error_type(aclresult, ofTypeId);
     886             :     }
     887             :     else
     888       52930 :         ofTypeId = InvalidOid;
     889             : 
     890             :     /*
     891             :      * Look up inheritance ancestors and generate relation schema, including
     892             :      * inherited attributes.  (Note that stmt->tableElts is destructively
     893             :      * modified by MergeAttributes.)
     894             :      */
     895       52842 :     stmt->tableElts =
     896       53010 :         MergeAttributes(stmt->tableElts, inheritOids,
     897       53010 :                         stmt->relation->relpersistence,
     898       53010 :                         stmt->partbound != NULL,
     899             :                         &old_constraints, &old_notnulls);
     900             : 
     901             :     /*
     902             :      * Create a tuple descriptor from the relation schema.  Note that this
     903             :      * deals with column names, types, and in-descriptor NOT NULL flags, but
     904             :      * not default values, NOT NULL or CHECK constraints; we handle those
     905             :      * below.
     906             :      */
     907       52842 :     descriptor = BuildDescForRelation(stmt->tableElts);
     908             : 
     909             :     /*
     910             :      * Find columns with default values and prepare for insertion of the
     911             :      * defaults.  Pre-cooked (that is, inherited) defaults go into a list of
     912             :      * CookedConstraint structs that we'll pass to heap_create_with_catalog,
     913             :      * while raw defaults go into a list of RawColumnDefault structs that will
     914             :      * be processed by AddRelationNewConstraints.  (We can't deal with raw
     915             :      * expressions until we can do transformExpr.)
     916             :      *
     917             :      * We can set the atthasdef flags now in the tuple descriptor; this just
     918             :      * saves StoreAttrDefault from having to do an immediate update of the
     919             :      * pg_attribute rows.
     920             :      */
     921       52794 :     rawDefaults = NIL;
     922       52794 :     cookedDefaults = NIL;
     923       52794 :     attnum = 0;
     924             : 
     925      267308 :     foreach(listptr, stmt->tableElts)
     926             :     {
     927      214514 :         ColumnDef  *colDef = lfirst(listptr);
     928             :         Form_pg_attribute attr;
     929             : 
     930      214514 :         attnum++;
     931      214514 :         attr = TupleDescAttr(descriptor, attnum - 1);
     932             : 
     933      214514 :         if (colDef->raw_default != NULL)
     934             :         {
     935             :             RawColumnDefault *rawEnt;
     936             : 
     937             :             Assert(colDef->cooked_default == NULL);
     938             : 
     939        2400 :             rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
     940        2400 :             rawEnt->attnum = attnum;
     941        2400 :             rawEnt->raw_default = colDef->raw_default;
     942        2400 :             rawEnt->missingMode = false;
     943        2400 :             rawEnt->generated = colDef->generated;
     944        2400 :             rawDefaults = lappend(rawDefaults, rawEnt);
     945        2400 :             attr->atthasdef = true;
     946             :         }
     947      212114 :         else if (colDef->cooked_default != NULL)
     948             :         {
     949             :             CookedConstraint *cooked;
     950             : 
     951         442 :             cooked = (CookedConstraint *) palloc(sizeof(CookedConstraint));
     952         442 :             cooked->contype = CONSTR_DEFAULT;
     953         442 :             cooked->conoid = InvalidOid; /* until created */
     954         442 :             cooked->name = NULL;
     955         442 :             cooked->attnum = attnum;
     956         442 :             cooked->expr = colDef->cooked_default;
     957         442 :             cooked->skip_validation = false;
     958         442 :             cooked->is_local = true; /* not used for defaults */
     959         442 :             cooked->inhcount = 0;    /* ditto */
     960         442 :             cooked->is_no_inherit = false;
     961         442 :             cookedDefaults = lappend(cookedDefaults, cooked);
     962         442 :             attr->atthasdef = true;
     963             :         }
     964             :     }
     965             : 
     966             :     /*
     967             :      * For relations with table AM and partitioned tables, select access
     968             :      * method to use: an explicitly indicated one, or (in the case of a
     969             :      * partitioned table) the parent's, if it has one.
     970             :      */
     971       52794 :     if (stmt->accessMethod != NULL)
     972             :     {
     973             :         Assert(RELKIND_HAS_TABLE_AM(relkind) || relkind == RELKIND_PARTITIONED_TABLE);
     974         116 :         accessMethodId = get_table_am_oid(stmt->accessMethod, false);
     975             :     }
     976       52678 :     else if (RELKIND_HAS_TABLE_AM(relkind) || relkind == RELKIND_PARTITIONED_TABLE)
     977             :     {
     978       36540 :         if (stmt->partbound)
     979             :         {
     980             :             Assert(list_length(inheritOids) == 1);
     981        8110 :             accessMethodId = get_rel_relam(linitial_oid(inheritOids));
     982             :         }
     983             : 
     984       36540 :         if (RELKIND_HAS_TABLE_AM(relkind) && !OidIsValid(accessMethodId))
     985       31662 :             accessMethodId = get_table_am_oid(default_table_access_method, false);
     986             :     }
     987             : 
     988             :     /*
     989             :      * Create the relation.  Inherited defaults and constraints are passed in
     990             :      * for immediate handling --- since they don't need parsing, they can be
     991             :      * stored immediately.
     992             :      */
     993       52776 :     relationId = heap_create_with_catalog(relname,
     994             :                                           namespaceId,
     995             :                                           tablespaceId,
     996             :                                           InvalidOid,
     997             :                                           InvalidOid,
     998             :                                           ofTypeId,
     999             :                                           ownerId,
    1000             :                                           accessMethodId,
    1001             :                                           descriptor,
    1002             :                                           list_concat(cookedDefaults,
    1003             :                                                       old_constraints),
    1004             :                                           relkind,
    1005       52776 :                                           stmt->relation->relpersistence,
    1006             :                                           false,
    1007             :                                           false,
    1008             :                                           stmt->oncommit,
    1009             :                                           reloptions,
    1010             :                                           true,
    1011             :                                           allowSystemTableMods,
    1012             :                                           false,
    1013             :                                           InvalidOid,
    1014             :                                           typaddress);
    1015             : 
    1016             :     /*
    1017             :      * We must bump the command counter to make the newly-created relation
    1018             :      * tuple visible for opening.
    1019             :      */
    1020       52752 :     CommandCounterIncrement();
    1021             : 
    1022             :     /*
    1023             :      * Open the new relation and acquire exclusive lock on it.  This isn't
    1024             :      * really necessary for locking out other backends (since they can't see
    1025             :      * the new rel anyway until we commit), but it keeps the lock manager from
    1026             :      * complaining about deadlock risks.
    1027             :      */
    1028       52752 :     rel = relation_open(relationId, AccessExclusiveLock);
    1029             : 
    1030             :     /*
    1031             :      * Now add any newly specified column default and generation expressions
    1032             :      * to the new relation.  These are passed to us in the form of raw
    1033             :      * parsetrees; we need to transform them to executable expression trees
    1034             :      * before they can be added. The most convenient way to do that is to
    1035             :      * apply the parser's transformExpr routine, but transformExpr doesn't
    1036             :      * work unless we have a pre-existing relation. So, the transformation has
    1037             :      * to be postponed to this final step of CREATE TABLE.
    1038             :      *
    1039             :      * This needs to be before processing the partitioning clauses because
    1040             :      * those could refer to generated columns.
    1041             :      */
    1042       52752 :     if (rawDefaults)
    1043        2036 :         AddRelationNewConstraints(rel, rawDefaults, NIL,
    1044             :                                   true, true, false, queryString);
    1045             : 
    1046             :     /*
    1047             :      * Make column generation expressions visible for use by partitioning.
    1048             :      */
    1049       52650 :     CommandCounterIncrement();
    1050             : 
    1051             :     /* Process and store partition bound, if any. */
    1052       52650 :     if (stmt->partbound)
    1053             :     {
    1054             :         PartitionBoundSpec *bound;
    1055             :         ParseState *pstate;
    1056        8214 :         Oid         parentId = linitial_oid(inheritOids),
    1057             :                     defaultPartOid;
    1058             :         Relation    parent,
    1059        8214 :                     defaultRel = NULL;
    1060             :         ParseNamespaceItem *nsitem;
    1061             : 
    1062             :         /* Already have strong enough lock on the parent */
    1063        8214 :         parent = table_open(parentId, NoLock);
    1064             : 
    1065             :         /*
    1066             :          * We are going to try to validate the partition bound specification
    1067             :          * against the partition key of parentRel, so it better have one.
    1068             :          */
    1069        8214 :         if (parent->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
    1070          18 :             ereport(ERROR,
    1071             :                     (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
    1072             :                      errmsg("\"%s\" is not partitioned",
    1073             :                             RelationGetRelationName(parent))));
    1074             : 
    1075             :         /*
    1076             :          * The partition constraint of the default partition depends on the
    1077             :          * partition bounds of every other partition. It is possible that
    1078             :          * another backend might be about to execute a query on the default
    1079             :          * partition table, and that the query relies on previously cached
    1080             :          * default partition constraints. We must therefore take a table lock
    1081             :          * strong enough to prevent all queries on the default partition from
    1082             :          * proceeding until we commit and send out a shared-cache-inval notice
    1083             :          * that will make them update their index lists.
    1084             :          *
    1085             :          * Order of locking: The relation being added won't be visible to
    1086             :          * other backends until it is committed, hence here in
    1087             :          * DefineRelation() the order of locking the default partition and the
    1088             :          * relation being added does not matter. But at all other places we
    1089             :          * need to lock the default relation before we lock the relation being
    1090             :          * added or removed i.e. we should take the lock in same order at all
    1091             :          * the places such that lock parent, lock default partition and then
    1092             :          * lock the partition so as to avoid a deadlock.
    1093             :          */
    1094             :         defaultPartOid =
    1095        8196 :             get_default_oid_from_partdesc(RelationGetPartitionDesc(parent,
    1096             :                                                                    true));
    1097        8196 :         if (OidIsValid(defaultPartOid))
    1098         378 :             defaultRel = table_open(defaultPartOid, AccessExclusiveLock);
    1099             : 
    1100             :         /* Transform the bound values */
    1101        8196 :         pstate = make_parsestate(NULL);
    1102        8196 :         pstate->p_sourcetext = queryString;
    1103             : 
    1104             :         /*
    1105             :          * Add an nsitem containing this relation, so that transformExpr
    1106             :          * called on partition bound expressions is able to report errors
    1107             :          * using a proper context.
    1108             :          */
    1109        8196 :         nsitem = addRangeTableEntryForRelation(pstate, rel, AccessShareLock,
    1110             :                                                NULL, false, false);
    1111        8196 :         addNSItemToQuery(pstate, nsitem, false, true, true);
    1112             : 
    1113        8196 :         bound = transformPartitionBound(pstate, parent, stmt->partbound);
    1114             : 
    1115             :         /*
    1116             :          * Check first that the new partition's bound is valid and does not
    1117             :          * overlap with any of existing partitions of the parent.
    1118             :          */
    1119        7992 :         check_new_partition_bound(relname, parent, bound, pstate);
    1120             : 
    1121             :         /*
    1122             :          * If the default partition exists, its partition constraints will
    1123             :          * change after the addition of this new partition such that it won't
    1124             :          * allow any row that qualifies for this new partition. So, check that
    1125             :          * the existing data in the default partition satisfies the constraint
    1126             :          * as it will exist after adding this partition.
    1127             :          */
    1128        7878 :         if (OidIsValid(defaultPartOid))
    1129             :         {
    1130         348 :             check_default_partition_contents(parent, defaultRel, bound);
    1131             :             /* Keep the lock until commit. */
    1132         330 :             table_close(defaultRel, NoLock);
    1133             :         }
    1134             : 
    1135             :         /* Update the pg_class entry. */
    1136        7860 :         StorePartitionBound(rel, parent, bound);
    1137             : 
    1138        7860 :         table_close(parent, NoLock);
    1139             :     }
    1140             : 
    1141             :     /* Store inheritance information for new rel. */
    1142       52296 :     StoreCatalogInheritance(relationId, inheritOids, stmt->partbound != NULL);
    1143             : 
    1144             :     /*
    1145             :      * Process the partitioning specification (if any) and store the partition
    1146             :      * key information into the catalog.
    1147             :      */
    1148       52296 :     if (partitioned)
    1149             :     {
    1150             :         ParseState *pstate;
    1151             :         int         partnatts;
    1152             :         AttrNumber  partattrs[PARTITION_MAX_KEYS];
    1153             :         Oid         partopclass[PARTITION_MAX_KEYS];
    1154             :         Oid         partcollation[PARTITION_MAX_KEYS];
    1155        4858 :         List       *partexprs = NIL;
    1156             : 
    1157        4858 :         pstate = make_parsestate(NULL);
    1158        4858 :         pstate->p_sourcetext = queryString;
    1159             : 
    1160        4858 :         partnatts = list_length(stmt->partspec->partParams);
    1161             : 
    1162             :         /* Protect fixed-size arrays here and in executor */
    1163        4858 :         if (partnatts > PARTITION_MAX_KEYS)
    1164           0 :             ereport(ERROR,
    1165             :                     (errcode(ERRCODE_TOO_MANY_COLUMNS),
    1166             :                      errmsg("cannot partition using more than %d columns",
    1167             :                             PARTITION_MAX_KEYS)));
    1168             : 
    1169             :         /*
    1170             :          * We need to transform the raw parsetrees corresponding to partition
    1171             :          * expressions into executable expression trees.  Like column defaults
    1172             :          * and CHECK constraints, we could not have done the transformation
    1173             :          * earlier.
    1174             :          */
    1175        4858 :         stmt->partspec = transformPartitionSpec(rel, stmt->partspec);
    1176             : 
    1177        4828 :         ComputePartitionAttrs(pstate, rel, stmt->partspec->partParams,
    1178             :                               partattrs, &partexprs, partopclass,
    1179        4828 :                               partcollation, stmt->partspec->strategy);
    1180             : 
    1181        4744 :         StorePartitionKey(rel, stmt->partspec->strategy, partnatts, partattrs,
    1182             :                           partexprs,
    1183             :                           partopclass, partcollation);
    1184             : 
    1185             :         /* make it all visible */
    1186        4744 :         CommandCounterIncrement();
    1187             :     }
    1188             : 
    1189             :     /*
    1190             :      * If we're creating a partition, create now all the indexes, triggers,
    1191             :      * FKs defined in the parent.
    1192             :      *
    1193             :      * We can't do it earlier, because DefineIndex wants to know the partition
    1194             :      * key which we just stored.
    1195             :      */
    1196       52182 :     if (stmt->partbound)
    1197             :     {
    1198        7854 :         Oid         parentId = linitial_oid(inheritOids);
    1199             :         Relation    parent;
    1200             :         List       *idxlist;
    1201             :         ListCell   *cell;
    1202             : 
    1203             :         /* Already have strong enough lock on the parent */
    1204        7854 :         parent = table_open(parentId, NoLock);
    1205        7854 :         idxlist = RelationGetIndexList(parent);
    1206             : 
    1207             :         /*
    1208             :          * For each index in the parent table, create one in the partition
    1209             :          */
    1210        9276 :         foreach(cell, idxlist)
    1211             :         {
    1212        1440 :             Relation    idxRel = index_open(lfirst_oid(cell), AccessShareLock);
    1213             :             AttrMap    *attmap;
    1214             :             IndexStmt  *idxstmt;
    1215             :             Oid         constraintOid;
    1216             : 
    1217        1440 :             if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
    1218             :             {
    1219          36 :                 if (idxRel->rd_index->indisunique)
    1220          12 :                     ereport(ERROR,
    1221             :                             (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    1222             :                              errmsg("cannot create foreign partition of partitioned table \"%s\"",
    1223             :                                     RelationGetRelationName(parent)),
    1224             :                              errdetail("Table \"%s\" contains indexes that are unique.",
    1225             :                                        RelationGetRelationName(parent))));
    1226             :                 else
    1227             :                 {
    1228          24 :                     index_close(idxRel, AccessShareLock);
    1229          24 :                     continue;
    1230             :                 }
    1231             :             }
    1232             : 
    1233        1404 :             attmap = build_attrmap_by_name(RelationGetDescr(rel),
    1234             :                                            RelationGetDescr(parent),
    1235             :                                            false);
    1236             :             idxstmt =
    1237        1404 :                 generateClonedIndexStmt(NULL, idxRel,
    1238             :                                         attmap, &constraintOid);
    1239        1404 :             DefineIndex(RelationGetRelid(rel),
    1240             :                         idxstmt,
    1241             :                         InvalidOid,
    1242             :                         RelationGetRelid(idxRel),
    1243             :                         constraintOid,
    1244             :                         -1,
    1245             :                         false, false, false, false, false);
    1246             : 
    1247        1398 :             index_close(idxRel, AccessShareLock);
    1248             :         }
    1249             : 
    1250        7836 :         list_free(idxlist);
    1251             : 
    1252             :         /*
    1253             :          * If there are any row-level triggers, clone them to the new
    1254             :          * partition.
    1255             :          */
    1256        7836 :         if (parent->trigdesc != NULL)
    1257         426 :             CloneRowTriggersToPartition(parent, rel);
    1258             : 
    1259             :         /*
    1260             :          * And foreign keys too.  Note that because we're freshly creating the
    1261             :          * table, there is no need to verify these new constraints.
    1262             :          */
    1263        7836 :         CloneForeignKeyConstraints(NULL, parent, rel);
    1264             : 
    1265        7836 :         table_close(parent, NoLock);
    1266             :     }
    1267             : 
    1268             :     /*
    1269             :      * Now add any newly specified CHECK constraints to the new relation. Same
    1270             :      * as for defaults above, but these need to come after partitioning is set
    1271             :      * up.
    1272             :      */
    1273       52164 :     if (stmt->constraints)
    1274         634 :         AddRelationNewConstraints(rel, NIL, stmt->constraints,
    1275             :                                   true, true, false, queryString);
    1276             : 
    1277             :     /*
    1278             :      * Finally, merge the not-null constraints that are declared directly with
    1279             :      * those that come from parent relations (making sure to count inheritance
    1280             :      * appropriately for each), create them, and set the attnotnull flag on
    1281             :      * columns that don't yet have it.
    1282             :      */
    1283       52146 :     nncols = AddRelationNotNullConstraints(rel, stmt->nnconstraints,
    1284             :                                            old_notnulls);
    1285       59660 :     foreach(listptr, nncols)
    1286        7526 :         set_attnotnull(NULL, rel, lfirst_int(listptr), false, NoLock);
    1287             : 
    1288       52134 :     ObjectAddressSet(address, RelationRelationId, relationId);
    1289             : 
    1290             :     /*
    1291             :      * Clean up.  We keep lock on new relation (although it shouldn't be
    1292             :      * visible to anyone else anyway, until commit).
    1293             :      */
    1294       52134 :     relation_close(rel, NoLock);
    1295             : 
    1296       52134 :     return address;
    1297             : }
    1298             : 
    1299             : /*
    1300             :  * BuildDescForRelation
    1301             :  *
    1302             :  * Given a list of ColumnDef nodes, build a TupleDesc.
    1303             :  *
    1304             :  * Note: tdtypeid will need to be filled in later on.
    1305             :  */
    1306             : TupleDesc
    1307       55520 : BuildDescForRelation(const List *columns)
    1308             : {
    1309             :     int         natts;
    1310             :     AttrNumber  attnum;
    1311             :     ListCell   *l;
    1312             :     TupleDesc   desc;
    1313             :     bool        has_not_null;
    1314             :     char       *attname;
    1315             :     Oid         atttypid;
    1316             :     int32       atttypmod;
    1317             :     Oid         attcollation;
    1318             :     int         attdim;
    1319             : 
    1320             :     /*
    1321             :      * allocate a new tuple descriptor
    1322             :      */
    1323       55520 :     natts = list_length(columns);
    1324       55520 :     desc = CreateTemplateTupleDesc(natts);
    1325       55520 :     has_not_null = false;
    1326             : 
    1327       55520 :     attnum = 0;
    1328             : 
    1329      272976 :     foreach(l, columns)
    1330             :     {
    1331      217516 :         ColumnDef  *entry = lfirst(l);
    1332             :         AclResult   aclresult;
    1333             :         Form_pg_attribute att;
    1334             : 
    1335             :         /*
    1336             :          * for each entry in the list, get the name and type information from
    1337             :          * the list and have TupleDescInitEntry fill in the attribute
    1338             :          * information we need.
    1339             :          */
    1340      217516 :         attnum++;
    1341             : 
    1342      217516 :         attname = entry->colname;
    1343      217516 :         typenameTypeIdAndMod(NULL, entry->typeName, &atttypid, &atttypmod);
    1344             : 
    1345      217516 :         aclresult = object_aclcheck(TypeRelationId, atttypid, GetUserId(), ACL_USAGE);
    1346      217516 :         if (aclresult != ACLCHECK_OK)
    1347          42 :             aclcheck_error_type(aclresult, atttypid);
    1348             : 
    1349      217474 :         attcollation = GetColumnDefCollation(NULL, entry, atttypid);
    1350      217474 :         attdim = list_length(entry->typeName->arrayBounds);
    1351      217474 :         if (attdim > PG_INT16_MAX)
    1352           0 :             ereport(ERROR,
    1353             :                     errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
    1354             :                     errmsg("too many array dimensions"));
    1355             : 
    1356      217474 :         if (entry->typeName->setof)
    1357           0 :             ereport(ERROR,
    1358             :                     (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    1359             :                      errmsg("column \"%s\" cannot be declared SETOF",
    1360             :                             attname)));
    1361             : 
    1362      217474 :         TupleDescInitEntry(desc, attnum, attname,
    1363             :                            atttypid, atttypmod, attdim);
    1364      217474 :         att = TupleDescAttr(desc, attnum - 1);
    1365             : 
    1366             :         /* Override TupleDescInitEntry's settings as requested */
    1367      217474 :         TupleDescInitEntryCollation(desc, attnum, attcollation);
    1368             : 
    1369             :         /* Fill in additional stuff not handled by TupleDescInitEntry */
    1370      217474 :         att->attnotnull = entry->is_not_null;
    1371      217474 :         has_not_null |= entry->is_not_null;
    1372      217474 :         att->attislocal = entry->is_local;
    1373      217474 :         att->attinhcount = entry->inhcount;
    1374      217474 :         att->attidentity = entry->identity;
    1375      217474 :         att->attgenerated = entry->generated;
    1376      217474 :         att->attcompression = GetAttributeCompression(att->atttypid, entry->compression);
    1377      217462 :         if (entry->storage)
    1378       22700 :             att->attstorage = entry->storage;
    1379      194762 :         else if (entry->storage_name)
    1380          20 :             att->attstorage = GetAttributeStorage(att->atttypid, entry->storage_name);
    1381             :     }
    1382             : 
    1383       55460 :     if (has_not_null)
    1384             :     {
    1385       12128 :         TupleConstr *constr = (TupleConstr *) palloc0(sizeof(TupleConstr));
    1386             : 
    1387       12128 :         constr->has_not_null = true;
    1388       12128 :         constr->has_generated_stored = false;
    1389       12128 :         constr->defval = NULL;
    1390       12128 :         constr->missing = NULL;
    1391       12128 :         constr->num_defval = 0;
    1392       12128 :         constr->check = NULL;
    1393       12128 :         constr->num_check = 0;
    1394       12128 :         desc->constr = constr;
    1395             :     }
    1396             :     else
    1397             :     {
    1398       43332 :         desc->constr = NULL;
    1399             :     }
    1400             : 
    1401       55460 :     return desc;
    1402             : }
    1403             : 
    1404             : /*
    1405             :  * Emit the right error or warning message for a "DROP" command issued on a
    1406             :  * non-existent relation
    1407             :  */
    1408             : static void
    1409        1080 : DropErrorMsgNonExistent(RangeVar *rel, char rightkind, bool missing_ok)
    1410             : {
    1411             :     const struct dropmsgstrings *rentry;
    1412             : 
    1413        1200 :     if (rel->schemaname != NULL &&
    1414         120 :         !OidIsValid(LookupNamespaceNoError(rel->schemaname)))
    1415             :     {
    1416          42 :         if (!missing_ok)
    1417             :         {
    1418           0 :             ereport(ERROR,
    1419             :                     (errcode(ERRCODE_UNDEFINED_SCHEMA),
    1420             :                      errmsg("schema \"%s\" does not exist", rel->schemaname)));
    1421             :         }
    1422             :         else
    1423             :         {
    1424          42 :             ereport(NOTICE,
    1425             :                     (errmsg("schema \"%s\" does not exist, skipping",
    1426             :                             rel->schemaname)));
    1427             :         }
    1428          42 :         return;
    1429             :     }
    1430             : 
    1431        1358 :     for (rentry = dropmsgstringarray; rentry->kind != '\0'; rentry++)
    1432             :     {
    1433        1358 :         if (rentry->kind == rightkind)
    1434             :         {
    1435        1038 :             if (!missing_ok)
    1436             :             {
    1437         132 :                 ereport(ERROR,
    1438             :                         (errcode(rentry->nonexistent_code),
    1439             :                          errmsg(rentry->nonexistent_msg, rel->relname)));
    1440             :             }
    1441             :             else
    1442             :             {
    1443         906 :                 ereport(NOTICE, (errmsg(rentry->skipping_msg, rel->relname)));
    1444         906 :                 break;
    1445             :             }
    1446             :         }
    1447             :     }
    1448             : 
    1449             :     Assert(rentry->kind != '\0');    /* Should be impossible */
    1450             : }
    1451             : 
    1452             : /*
    1453             :  * Emit the right error message for a "DROP" command issued on a
    1454             :  * relation of the wrong type
    1455             :  */
    1456             : static void
    1457           0 : DropErrorMsgWrongType(const char *relname, char wrongkind, char rightkind)
    1458             : {
    1459             :     const struct dropmsgstrings *rentry;
    1460             :     const struct dropmsgstrings *wentry;
    1461             : 
    1462           0 :     for (rentry = dropmsgstringarray; rentry->kind != '\0'; rentry++)
    1463           0 :         if (rentry->kind == rightkind)
    1464           0 :             break;
    1465             :     Assert(rentry->kind != '\0');
    1466             : 
    1467           0 :     for (wentry = dropmsgstringarray; wentry->kind != '\0'; wentry++)
    1468           0 :         if (wentry->kind == wrongkind)
    1469           0 :             break;
    1470             :     /* wrongkind could be something we don't have in our table... */
    1471             : 
    1472           0 :     ereport(ERROR,
    1473             :             (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    1474             :              errmsg(rentry->nota_msg, relname),
    1475             :              (wentry->kind != '\0') ? errhint("%s", _(wentry->drophint_msg)) : 0));
    1476             : }
    1477             : 
    1478             : /*
    1479             :  * RemoveRelations
    1480             :  *      Implements DROP TABLE, DROP INDEX, DROP SEQUENCE, DROP VIEW,
    1481             :  *      DROP MATERIALIZED VIEW, DROP FOREIGN TABLE
    1482             :  */
    1483             : void
    1484       16204 : RemoveRelations(DropStmt *drop)
    1485             : {
    1486             :     ObjectAddresses *objects;
    1487             :     char        relkind;
    1488             :     ListCell   *cell;
    1489       16204 :     int         flags = 0;
    1490       16204 :     LOCKMODE    lockmode = AccessExclusiveLock;
    1491             : 
    1492             :     /* DROP CONCURRENTLY uses a weaker lock, and has some restrictions */
    1493       16204 :     if (drop->concurrent)
    1494             :     {
    1495             :         /*
    1496             :          * Note that for temporary relations this lock may get upgraded later
    1497             :          * on, but as no other session can access a temporary relation, this
    1498             :          * is actually fine.
    1499             :          */
    1500         122 :         lockmode = ShareUpdateExclusiveLock;
    1501             :         Assert(drop->removeType == OBJECT_INDEX);
    1502         122 :         if (list_length(drop->objects) != 1)
    1503           6 :             ereport(ERROR,
    1504             :                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    1505             :                      errmsg("DROP INDEX CONCURRENTLY does not support dropping multiple objects")));
    1506         116 :         if (drop->behavior == DROP_CASCADE)
    1507           0 :             ereport(ERROR,
    1508             :                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    1509             :                      errmsg("DROP INDEX CONCURRENTLY does not support CASCADE")));
    1510             :     }
    1511             : 
    1512             :     /*
    1513             :      * First we identify all the relations, then we delete them in a single
    1514             :      * performMultipleDeletions() call.  This is to avoid unwanted DROP
    1515             :      * RESTRICT errors if one of the relations depends on another.
    1516             :      */
    1517             : 
    1518             :     /* Determine required relkind */
    1519       16198 :     switch (drop->removeType)
    1520             :     {
    1521       14110 :         case OBJECT_TABLE:
    1522       14110 :             relkind = RELKIND_RELATION;
    1523       14110 :             break;
    1524             : 
    1525         768 :         case OBJECT_INDEX:
    1526         768 :             relkind = RELKIND_INDEX;
    1527         768 :             break;
    1528             : 
    1529         172 :         case OBJECT_SEQUENCE:
    1530         172 :             relkind = RELKIND_SEQUENCE;
    1531         172 :             break;
    1532             : 
    1533         880 :         case OBJECT_VIEW:
    1534         880 :             relkind = RELKIND_VIEW;
    1535         880 :             break;
    1536             : 
    1537         120 :         case OBJECT_MATVIEW:
    1538         120 :             relkind = RELKIND_MATVIEW;
    1539         120 :             break;
    1540             : 
    1541         148 :         case OBJECT_FOREIGN_TABLE:
    1542         148 :             relkind = RELKIND_FOREIGN_TABLE;
    1543         148 :             break;
    1544             : 
    1545           0 :         default:
    1546           0 :             elog(ERROR, "unrecognized drop object type: %d",
    1547             :                  (int) drop->removeType);
    1548             :             relkind = 0;        /* keep compiler quiet */
    1549             :             break;
    1550             :     }
    1551             : 
    1552             :     /* Lock and validate each relation; build a list of object addresses */
    1553       16198 :     objects = new_object_addresses();
    1554             : 
    1555       35920 :     foreach(cell, drop->objects)
    1556             :     {
    1557       19880 :         RangeVar   *rel = makeRangeVarFromNameList((List *) lfirst(cell));
    1558             :         Oid         relOid;
    1559             :         ObjectAddress obj;
    1560             :         struct DropRelationCallbackState state;
    1561             : 
    1562             :         /*
    1563             :          * These next few steps are a great deal like relation_openrv, but we
    1564             :          * don't bother building a relcache entry since we don't need it.
    1565             :          *
    1566             :          * Check for shared-cache-inval messages before trying to access the
    1567             :          * relation.  This is needed to cover the case where the name
    1568             :          * identifies a rel that has been dropped and recreated since the
    1569             :          * start of our transaction: if we don't flush the old syscache entry,
    1570             :          * then we'll latch onto that entry and suffer an error later.
    1571             :          */
    1572       19880 :         AcceptInvalidationMessages();
    1573             : 
    1574             :         /* Look up the appropriate relation using namespace search. */
    1575       19880 :         state.expected_relkind = relkind;
    1576       39760 :         state.heap_lockmode = drop->concurrent ?
    1577       19880 :             ShareUpdateExclusiveLock : AccessExclusiveLock;
    1578             :         /* We must initialize these fields to show that no locks are held: */
    1579       19880 :         state.heapOid = InvalidOid;
    1580       19880 :         state.partParentOid = InvalidOid;
    1581             : 
    1582       19880 :         relOid = RangeVarGetRelidExtended(rel, lockmode, RVR_MISSING_OK,
    1583             :                                           RangeVarCallbackForDropRelation,
    1584             :                                           (void *) &state);
    1585             : 
    1586             :         /* Not there? */
    1587       19860 :         if (!OidIsValid(relOid))
    1588             :         {
    1589        1080 :             DropErrorMsgNonExistent(rel, relkind, drop->missing_ok);
    1590         948 :             continue;
    1591             :         }
    1592             : 
    1593             :         /*
    1594             :          * Decide if concurrent mode needs to be used here or not.  The
    1595             :          * callback retrieved the rel's persistence for us.
    1596             :          */
    1597       18780 :         if (drop->concurrent &&
    1598         110 :             state.actual_relpersistence != RELPERSISTENCE_TEMP)
    1599             :         {
    1600             :             Assert(list_length(drop->objects) == 1 &&
    1601             :                    drop->removeType == OBJECT_INDEX);
    1602          92 :             flags |= PERFORM_DELETION_CONCURRENTLY;
    1603             :         }
    1604             : 
    1605             :         /*
    1606             :          * Concurrent index drop cannot be used with partitioned indexes,
    1607             :          * either.
    1608             :          */
    1609       18780 :         if ((flags & PERFORM_DELETION_CONCURRENTLY) != 0 &&
    1610          92 :             state.actual_relkind == RELKIND_PARTITIONED_INDEX)
    1611           6 :             ereport(ERROR,
    1612             :                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    1613             :                      errmsg("cannot drop partitioned index \"%s\" concurrently",
    1614             :                             rel->relname)));
    1615             : 
    1616             :         /*
    1617             :          * If we're told to drop a partitioned index, we must acquire lock on
    1618             :          * all the children of its parent partitioned table before proceeding.
    1619             :          * Otherwise we'd try to lock the child index partitions before their
    1620             :          * tables, leading to potential deadlock against other sessions that
    1621             :          * will lock those objects in the other order.
    1622             :          */
    1623       18774 :         if (state.actual_relkind == RELKIND_PARTITIONED_INDEX)
    1624          70 :             (void) find_all_inheritors(state.heapOid,
    1625             :                                        state.heap_lockmode,
    1626             :                                        NULL);
    1627             : 
    1628             :         /* OK, we're ready to delete this one */
    1629       18774 :         obj.classId = RelationRelationId;
    1630       18774 :         obj.objectId = relOid;
    1631       18774 :         obj.objectSubId = 0;
    1632             : 
    1633       18774 :         add_exact_object_address(&obj, objects);
    1634             :     }
    1635             : 
    1636       16040 :     performMultipleDeletions(objects, drop->behavior, flags);
    1637             : 
    1638       15904 :     free_object_addresses(objects);
    1639       15904 : }
    1640             : 
    1641             : /*
    1642             :  * Before acquiring a table lock, check whether we have sufficient rights.
    1643             :  * In the case of DROP INDEX, also try to lock the table before the index.
    1644             :  * Also, if the table to be dropped is a partition, we try to lock the parent
    1645             :  * first.
    1646             :  */
    1647             : static void
    1648       20032 : RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid, Oid oldRelOid,
    1649             :                                 void *arg)
    1650             : {
    1651             :     HeapTuple   tuple;
    1652             :     struct DropRelationCallbackState *state;
    1653             :     char        expected_relkind;
    1654             :     bool        is_partition;
    1655             :     Form_pg_class classform;
    1656             :     LOCKMODE    heap_lockmode;
    1657       20032 :     bool        invalid_system_index = false;
    1658             : 
    1659       20032 :     state = (struct DropRelationCallbackState *) arg;
    1660       20032 :     heap_lockmode = state->heap_lockmode;
    1661             : 
    1662             :     /*
    1663             :      * If we previously locked some other index's heap, and the name we're
    1664             :      * looking up no longer refers to that relation, release the now-useless
    1665             :      * lock.
    1666             :      */
    1667       20032 :     if (relOid != oldRelOid && OidIsValid(state->heapOid))
    1668             :     {
    1669           0 :         UnlockRelationOid(state->heapOid, heap_lockmode);
    1670           0 :         state->heapOid = InvalidOid;
    1671             :     }
    1672             : 
    1673             :     /*
    1674             :      * Similarly, if we previously locked some other partition's heap, and the
    1675             :      * name we're looking up no longer refers to that relation, release the
    1676             :      * now-useless lock.
    1677             :      */
    1678       20032 :     if (relOid != oldRelOid && OidIsValid(state->partParentOid))
    1679             :     {
    1680           0 :         UnlockRelationOid(state->partParentOid, AccessExclusiveLock);
    1681           0 :         state->partParentOid = InvalidOid;
    1682             :     }
    1683             : 
    1684             :     /* Didn't find a relation, so no need for locking or permission checks. */
    1685       20032 :     if (!OidIsValid(relOid))
    1686        1082 :         return;
    1687             : 
    1688       18950 :     tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid));
    1689       18950 :     if (!HeapTupleIsValid(tuple))
    1690           0 :         return;                 /* concurrently dropped, so nothing to do */
    1691       18950 :     classform = (Form_pg_class) GETSTRUCT(tuple);
    1692       18950 :     is_partition = classform->relispartition;
    1693             : 
    1694             :     /* Pass back some data to save lookups in RemoveRelations */
    1695       18950 :     state->actual_relkind = classform->relkind;
    1696       18950 :     state->actual_relpersistence = classform->relpersistence;
    1697             : 
    1698             :     /*
    1699             :      * Both RELKIND_RELATION and RELKIND_PARTITIONED_TABLE are OBJECT_TABLE,
    1700             :      * but RemoveRelations() can only pass one relkind for a given relation.
    1701             :      * It chooses RELKIND_RELATION for both regular and partitioned tables.
    1702             :      * That means we must be careful before giving the wrong type error when
    1703             :      * the relation is RELKIND_PARTITIONED_TABLE.  An equivalent problem
    1704             :      * exists with indexes.
    1705             :      */
    1706       18950 :     if (classform->relkind == RELKIND_PARTITIONED_TABLE)
    1707        2964 :         expected_relkind = RELKIND_RELATION;
    1708       15986 :     else if (classform->relkind == RELKIND_PARTITIONED_INDEX)
    1709          78 :         expected_relkind = RELKIND_INDEX;
    1710             :     else
    1711       15908 :         expected_relkind = classform->relkind;
    1712             : 
    1713       18950 :     if (state->expected_relkind != expected_relkind)
    1714           0 :         DropErrorMsgWrongType(rel->relname, classform->relkind,
    1715           0 :                               state->expected_relkind);
    1716             : 
    1717             :     /* Allow DROP to either table owner or schema owner */
    1718       18950 :     if (!object_ownercheck(RelationRelationId, relOid, GetUserId()) &&
    1719          18 :         !object_ownercheck(NamespaceRelationId, classform->relnamespace, GetUserId()))
    1720          18 :         aclcheck_error(ACLCHECK_NOT_OWNER,
    1721          18 :                        get_relkind_objtype(classform->relkind),
    1722          18 :                        rel->relname);
    1723             : 
    1724             :     /*
    1725             :      * Check the case of a system index that might have been invalidated by a
    1726             :      * failed concurrent process and allow its drop. For the time being, this
    1727             :      * only concerns indexes of toast relations that became invalid during a
    1728             :      * REINDEX CONCURRENTLY process.
    1729             :      */
    1730       18932 :     if (IsSystemClass(relOid, classform) && classform->relkind == RELKIND_INDEX)
    1731             :     {
    1732             :         HeapTuple   locTuple;
    1733             :         Form_pg_index indexform;
    1734             :         bool        indisvalid;
    1735             : 
    1736           0 :         locTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(relOid));
    1737           0 :         if (!HeapTupleIsValid(locTuple))
    1738             :         {
    1739           0 :             ReleaseSysCache(tuple);
    1740           0 :             return;
    1741             :         }
    1742             : 
    1743           0 :         indexform = (Form_pg_index) GETSTRUCT(locTuple);
    1744           0 :         indisvalid = indexform->indisvalid;
    1745           0 :         ReleaseSysCache(locTuple);
    1746             : 
    1747             :         /* Mark object as being an invalid index of system catalogs */
    1748           0 :         if (!indisvalid)
    1749           0 :             invalid_system_index = true;
    1750             :     }
    1751             : 
    1752             :     /* In the case of an invalid index, it is fine to bypass this check */
    1753       18932 :     if (!invalid_system_index && !allowSystemTableMods && IsSystemClass(relOid, classform))
    1754           2 :         ereport(ERROR,
    1755             :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
    1756             :                  errmsg("permission denied: \"%s\" is a system catalog",
    1757             :                         rel->relname)));
    1758             : 
    1759       18930 :     ReleaseSysCache(tuple);
    1760             : 
    1761             :     /*
    1762             :      * In DROP INDEX, attempt to acquire lock on the parent table before
    1763             :      * locking the index.  index_drop() will need this anyway, and since
    1764             :      * regular queries lock tables before their indexes, we risk deadlock if
    1765             :      * we do it the other way around.  No error if we don't find a pg_index
    1766             :      * entry, though --- the relation may have been dropped.  Note that this
    1767             :      * code will execute for either plain or partitioned indexes.
    1768             :      */
    1769       18930 :     if (expected_relkind == RELKIND_INDEX &&
    1770             :         relOid != oldRelOid)
    1771             :     {
    1772         756 :         state->heapOid = IndexGetRelation(relOid, true);
    1773         756 :         if (OidIsValid(state->heapOid))
    1774         756 :             LockRelationOid(state->heapOid, heap_lockmode);
    1775             :     }
    1776             : 
    1777             :     /*
    1778             :      * Similarly, if the relation is a partition, we must acquire lock on its
    1779             :      * parent before locking the partition.  That's because queries lock the
    1780             :      * parent before its partitions, so we risk deadlock if we do it the other
    1781             :      * way around.
    1782             :      */
    1783       18930 :     if (is_partition && relOid != oldRelOid)
    1784             :     {
    1785         594 :         state->partParentOid = get_partition_parent(relOid, true);
    1786         594 :         if (OidIsValid(state->partParentOid))
    1787         594 :             LockRelationOid(state->partParentOid, AccessExclusiveLock);
    1788             :     }
    1789             : }
    1790             : 
    1791             : /*
    1792             :  * ExecuteTruncate
    1793             :  *      Executes a TRUNCATE command.
    1794             :  *
    1795             :  * This is a multi-relation truncate.  We first open and grab exclusive
    1796             :  * lock on all relations involved, checking permissions and otherwise
    1797             :  * verifying that the relation is OK for truncation.  Note that if relations
    1798             :  * are foreign tables, at this stage, we have not yet checked that their
    1799             :  * foreign data in external data sources are OK for truncation.  These are
    1800             :  * checked when foreign data are actually truncated later.  In CASCADE mode,
    1801             :  * relations having FK references to the targeted relations are automatically
    1802             :  * added to the group; in RESTRICT mode, we check that all FK references are
    1803             :  * internal to the group that's being truncated.  Finally all the relations
    1804             :  * are truncated and reindexed.
    1805             :  */
    1806             : void
    1807        1444 : ExecuteTruncate(TruncateStmt *stmt)
    1808             : {
    1809        1444 :     List       *rels = NIL;
    1810        1444 :     List       *relids = NIL;
    1811        1444 :     List       *relids_logged = NIL;
    1812             :     ListCell   *cell;
    1813             : 
    1814             :     /*
    1815             :      * Open, exclusive-lock, and check all the explicitly-specified relations
    1816             :      */
    1817        3114 :     foreach(cell, stmt->relations)
    1818             :     {
    1819        1718 :         RangeVar   *rv = lfirst(cell);
    1820             :         Relation    rel;
    1821        1718 :         bool        recurse = rv->inh;
    1822             :         Oid         myrelid;
    1823        1718 :         LOCKMODE    lockmode = AccessExclusiveLock;
    1824             : 
    1825        1718 :         myrelid = RangeVarGetRelidExtended(rv, lockmode,
    1826             :                                            0, RangeVarCallbackForTruncate,
    1827             :                                            NULL);
    1828             : 
    1829             :         /* don't throw error for "TRUNCATE foo, foo" */
    1830        1682 :         if (list_member_oid(relids, myrelid))
    1831           2 :             continue;
    1832             : 
    1833             :         /* open the relation, we already hold a lock on it */
    1834        1680 :         rel = table_open(myrelid, NoLock);
    1835             : 
    1836             :         /*
    1837             :          * RangeVarGetRelidExtended() has done most checks with its callback,
    1838             :          * but other checks with the now-opened Relation remain.
    1839             :          */
    1840        1680 :         truncate_check_activity(rel);
    1841             : 
    1842        1680 :         rels = lappend(rels, rel);
    1843        1680 :         relids = lappend_oid(relids, myrelid);
    1844             : 
    1845             :         /* Log this relation only if needed for logical decoding */
    1846        1680 :         if (RelationIsLogicallyLogged(rel))
    1847          64 :             relids_logged = lappend_oid(relids_logged, myrelid);
    1848             : 
    1849        1680 :         if (recurse)
    1850             :         {
    1851             :             ListCell   *child;
    1852             :             List       *children;
    1853             : 
    1854        1618 :             children = find_all_inheritors(myrelid, lockmode, NULL);
    1855             : 
    1856        4968 :             foreach(child, children)
    1857             :             {
    1858        3350 :                 Oid         childrelid = lfirst_oid(child);
    1859             : 
    1860        3350 :                 if (list_member_oid(relids, childrelid))
    1861        1618 :                     continue;
    1862             : 
    1863             :                 /* find_all_inheritors already got lock */
    1864        1732 :                 rel = table_open(childrelid, NoLock);
    1865             : 
    1866             :                 /*
    1867             :                  * It is possible that the parent table has children that are
    1868             :                  * temp tables of other backends.  We cannot safely access
    1869             :                  * such tables (because of buffering issues), and the best
    1870             :                  * thing to do is to silently ignore them.  Note that this
    1871             :                  * check is the same as one of the checks done in
    1872             :                  * truncate_check_activity() called below, still it is kept
    1873             :                  * here for simplicity.
    1874             :                  */
    1875        1732 :                 if (RELATION_IS_OTHER_TEMP(rel))
    1876             :                 {
    1877           8 :                     table_close(rel, lockmode);
    1878           8 :                     continue;
    1879             :                 }
    1880             : 
    1881             :                 /*
    1882             :                  * Inherited TRUNCATE commands perform access permission
    1883             :                  * checks on the parent table only. So we skip checking the
    1884             :                  * children's permissions and don't call
    1885             :                  * truncate_check_perms() here.
    1886             :                  */
    1887        1724 :                 truncate_check_rel(RelationGetRelid(rel), rel->rd_rel);
    1888        1724 :                 truncate_check_activity(rel);
    1889             : 
    1890        1724 :                 rels = lappend(rels, rel);
    1891        1724 :                 relids = lappend_oid(relids, childrelid);
    1892             : 
    1893             :                 /* Log this relation only if needed for logical decoding */
    1894        1724 :                 if (RelationIsLogicallyLogged(rel))
    1895          22 :                     relids_logged = lappend_oid(relids_logged, childrelid);
    1896             :             }
    1897             :         }
    1898          62 :         else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
    1899          12 :             ereport(ERROR,
    1900             :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    1901             :                      errmsg("cannot truncate only a partitioned table"),
    1902             :                      errhint("Do not specify the ONLY keyword, or use TRUNCATE ONLY on the partitions directly.")));
    1903             :     }
    1904             : 
    1905        1396 :     ExecuteTruncateGuts(rels, relids, relids_logged,
    1906        1396 :                         stmt->behavior, stmt->restart_seqs, false);
    1907             : 
    1908             :     /* And close the rels */
    1909        4552 :     foreach(cell, rels)
    1910             :     {
    1911        3238 :         Relation    rel = (Relation) lfirst(cell);
    1912             : 
    1913        3238 :         table_close(rel, NoLock);
    1914             :     }
    1915        1314 : }
    1916             : 
    1917             : /*
    1918             :  * ExecuteTruncateGuts
    1919             :  *
    1920             :  * Internal implementation of TRUNCATE.  This is called by the actual TRUNCATE
    1921             :  * command (see above) as well as replication subscribers that execute a
    1922             :  * replicated TRUNCATE action.
    1923             :  *
    1924             :  * explicit_rels is the list of Relations to truncate that the command
    1925             :  * specified.  relids is the list of Oids corresponding to explicit_rels.
    1926             :  * relids_logged is the list of Oids (a subset of relids) that require
    1927             :  * WAL-logging.  This is all a bit redundant, but the existing callers have
    1928             :  * this information handy in this form.
    1929             :  */
    1930             : void
    1931        1434 : ExecuteTruncateGuts(List *explicit_rels,
    1932             :                     List *relids,
    1933             :                     List *relids_logged,
    1934             :                     DropBehavior behavior, bool restart_seqs,
    1935             :                     bool run_as_table_owner)
    1936             : {
    1937             :     List       *rels;
    1938        1434 :     List       *seq_relids = NIL;
    1939        1434 :     HTAB       *ft_htab = NULL;
    1940             :     EState     *estate;
    1941             :     ResultRelInfo *resultRelInfos;
    1942             :     ResultRelInfo *resultRelInfo;
    1943             :     SubTransactionId mySubid;
    1944             :     ListCell   *cell;
    1945             :     Oid        *logrelids;
    1946             : 
    1947             :     /*
    1948             :      * Check the explicitly-specified relations.
    1949             :      *
    1950             :      * In CASCADE mode, suck in all referencing relations as well.  This
    1951             :      * requires multiple iterations to find indirectly-dependent relations. At
    1952             :      * each phase, we need to exclusive-lock new rels before looking for their
    1953             :      * dependencies, else we might miss something.  Also, we check each rel as
    1954             :      * soon as we open it, to avoid a faux pas such as holding lock for a long
    1955             :      * time on a rel we have no permissions for.
    1956             :      */
    1957        1434 :     rels = list_copy(explicit_rels);
    1958        1434 :     if (behavior == DROP_CASCADE)
    1959             :     {
    1960             :         for (;;)
    1961          40 :         {
    1962             :             List       *newrelids;
    1963             : 
    1964          80 :             newrelids = heap_truncate_find_FKs(relids);
    1965          80 :             if (newrelids == NIL)
    1966          40 :                 break;          /* nothing else to add */
    1967             : 
    1968         134 :             foreach(cell, newrelids)
    1969             :             {
    1970          94 :                 Oid         relid = lfirst_oid(cell);
    1971             :                 Relation    rel;
    1972             : 
    1973          94 :                 rel = table_open(relid, AccessExclusiveLock);
    1974          94 :                 ereport(NOTICE,
    1975             :                         (errmsg("truncate cascades to table \"%s\"",
    1976             :                                 RelationGetRelationName(rel))));
    1977          94 :                 truncate_check_rel(relid, rel->rd_rel);
    1978          94 :                 truncate_check_perms(relid, rel->rd_rel);
    1979          94 :                 truncate_check_activity(rel);
    1980          94 :                 rels = lappend(rels, rel);
    1981          94 :                 relids = lappend_oid(relids, relid);
    1982             : 
    1983             :                 /* Log this relation only if needed for logical decoding */
    1984          94 :                 if (RelationIsLogicallyLogged(rel))
    1985           0 :                     relids_logged = lappend_oid(relids_logged, relid);
    1986             :             }
    1987             :         }
    1988             :     }
    1989             : 
    1990             :     /*
    1991             :      * Check foreign key references.  In CASCADE mode, this should be
    1992             :      * unnecessary since we just pulled in all the references; but as a
    1993             :      * cross-check, do it anyway if in an Assert-enabled build.
    1994             :      */
    1995             : #ifdef USE_ASSERT_CHECKING
    1996             :     heap_truncate_check_FKs(rels, false);
    1997             : #else
    1998        1434 :     if (behavior == DROP_RESTRICT)
    1999        1394 :         heap_truncate_check_FKs(rels, false);
    2000             : #endif
    2001             : 
    2002             :     /*
    2003             :      * If we are asked to restart sequences, find all the sequences, lock them
    2004             :      * (we need AccessExclusiveLock for ResetSequence), and check permissions.
    2005             :      * We want to do this early since it's pointless to do all the truncation
    2006             :      * work only to fail on sequence permissions.
    2007             :      */
    2008        1360 :     if (restart_seqs)
    2009             :     {
    2010          52 :         foreach(cell, rels)
    2011             :         {
    2012          26 :             Relation    rel = (Relation) lfirst(cell);
    2013          26 :             List       *seqlist = getOwnedSequences(RelationGetRelid(rel));
    2014             :             ListCell   *seqcell;
    2015             : 
    2016          62 :             foreach(seqcell, seqlist)
    2017             :             {
    2018          36 :                 Oid         seq_relid = lfirst_oid(seqcell);
    2019             :                 Relation    seq_rel;
    2020             : 
    2021          36 :                 seq_rel = relation_open(seq_relid, AccessExclusiveLock);
    2022             : 
    2023             :                 /* This check must match AlterSequence! */
    2024          36 :                 if (!object_ownercheck(RelationRelationId, seq_relid, GetUserId()))
    2025           0 :                     aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_SEQUENCE,
    2026           0 :                                    RelationGetRelationName(seq_rel));
    2027             : 
    2028          36 :                 seq_relids = lappend_oid(seq_relids, seq_relid);
    2029             : 
    2030          36 :                 relation_close(seq_rel, NoLock);
    2031             :             }
    2032             :         }
    2033             :     }
    2034             : 
    2035             :     /* Prepare to catch AFTER triggers. */
    2036        1360 :     AfterTriggerBeginQuery();
    2037             : 
    2038             :     /*
    2039             :      * To fire triggers, we'll need an EState as well as a ResultRelInfo for
    2040             :      * each relation.  We don't need to call ExecOpenIndices, though.
    2041             :      *
    2042             :      * We put the ResultRelInfos in the es_opened_result_relations list, even
    2043             :      * though we don't have a range table and don't populate the
    2044             :      * es_result_relations array.  That's a bit bogus, but it's enough to make
    2045             :      * ExecGetTriggerResultRel() find them.
    2046             :      */
    2047        1360 :     estate = CreateExecutorState();
    2048             :     resultRelInfos = (ResultRelInfo *)
    2049        1360 :         palloc(list_length(rels) * sizeof(ResultRelInfo));
    2050        1360 :     resultRelInfo = resultRelInfos;
    2051        4770 :     foreach(cell, rels)
    2052             :     {
    2053        3410 :         Relation    rel = (Relation) lfirst(cell);
    2054             : 
    2055        3410 :         InitResultRelInfo(resultRelInfo,
    2056             :                           rel,
    2057             :                           0,    /* dummy rangetable index */
    2058             :                           NULL,
    2059             :                           0);
    2060        3410 :         estate->es_opened_result_relations =
    2061        3410 :             lappend(estate->es_opened_result_relations, resultRelInfo);
    2062        3410 :         resultRelInfo++;
    2063             :     }
    2064             : 
    2065             :     /*
    2066             :      * Process all BEFORE STATEMENT TRUNCATE triggers before we begin
    2067             :      * truncating (this is because one of them might throw an error). Also, if
    2068             :      * we were to allow them to prevent statement execution, that would need
    2069             :      * to be handled here.
    2070             :      */
    2071        1360 :     resultRelInfo = resultRelInfos;
    2072        4770 :     foreach(cell, rels)
    2073             :     {
    2074             :         UserContext ucxt;
    2075             : 
    2076        3410 :         if (run_as_table_owner)
    2077          70 :             SwitchToUntrustedUser(resultRelInfo->ri_RelationDesc->rd_rel->relowner,
    2078             :                                   &ucxt);
    2079        3410 :         ExecBSTruncateTriggers(estate, resultRelInfo);
    2080        3410 :         if (run_as_table_owner)
    2081          70 :             RestoreUserContext(&ucxt);
    2082        3410 :         resultRelInfo++;
    2083             :     }
    2084             : 
    2085             :     /*
    2086             :      * OK, truncate each table.
    2087             :      */
    2088        1360 :     mySubid = GetCurrentSubTransactionId();
    2089             : 
    2090        4770 :     foreach(cell, rels)
    2091             :     {
    2092        3410 :         Relation    rel = (Relation) lfirst(cell);
    2093             : 
    2094             :         /* Skip partitioned tables as there is nothing to do */
    2095        3410 :         if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
    2096         692 :             continue;
    2097             : 
    2098             :         /*
    2099             :          * Build the lists of foreign tables belonging to each foreign server
    2100             :          * and pass each list to the foreign data wrapper's callback function,
    2101             :          * so that each server can truncate its all foreign tables in bulk.
    2102             :          * Each list is saved as a single entry in a hash table that uses the
    2103             :          * server OID as lookup key.
    2104             :          */
    2105        2718 :         if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
    2106             :         {
    2107          34 :             Oid         serverid = GetForeignServerIdByRelId(RelationGetRelid(rel));
    2108             :             bool        found;
    2109             :             ForeignTruncateInfo *ft_info;
    2110             : 
    2111             :             /* First time through, initialize hashtable for foreign tables */
    2112          34 :             if (!ft_htab)
    2113             :             {
    2114             :                 HASHCTL     hctl;
    2115             : 
    2116          30 :                 memset(&hctl, 0, sizeof(HASHCTL));
    2117          30 :                 hctl.keysize = sizeof(Oid);
    2118          30 :                 hctl.entrysize = sizeof(ForeignTruncateInfo);
    2119          30 :                 hctl.hcxt = CurrentMemoryContext;
    2120             : 
    2121          30 :                 ft_htab = hash_create("TRUNCATE for Foreign Tables",
    2122             :                                       32,   /* start small and extend */
    2123             :                                       &hctl,
    2124             :                                       HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);
    2125             :             }
    2126             : 
    2127             :             /* Find or create cached entry for the foreign table */
    2128          34 :             ft_info = hash_search(ft_htab, &serverid, HASH_ENTER, &found);
    2129          34 :             if (!found)
    2130          30 :                 ft_info->rels = NIL;
    2131             : 
    2132             :             /*
    2133             :              * Save the foreign table in the entry of the server that the
    2134             :              * foreign table belongs to.
    2135             :              */
    2136          34 :             ft_info->rels = lappend(ft_info->rels, rel);
    2137          34 :             continue;
    2138             :         }
    2139             : 
    2140             :         /*
    2141             :          * Normally, we need a transaction-safe truncation here.  However, if
    2142             :          * the table was either created in the current (sub)transaction or has
    2143             :          * a new relfilenumber in the current (sub)transaction, then we can
    2144             :          * just truncate it in-place, because a rollback would cause the whole
    2145             :          * table or the current physical file to be thrown away anyway.
    2146             :          */
    2147        2684 :         if (rel->rd_createSubid == mySubid ||
    2148        2658 :             rel->rd_newRelfilelocatorSubid == mySubid)
    2149             :         {
    2150             :             /* Immediate, non-rollbackable truncation is OK */
    2151          90 :             heap_truncate_one_rel(rel);
    2152             :         }
    2153             :         else
    2154             :         {
    2155             :             Oid         heap_relid;
    2156             :             Oid         toast_relid;
    2157        2594 :             ReindexParams reindex_params = {0};
    2158             : 
    2159             :             /*
    2160             :              * This effectively deletes all rows in the table, and may be done
    2161             :              * in a serializable transaction.  In that case we must record a
    2162             :              * rw-conflict in to this transaction from each transaction
    2163             :              * holding a predicate lock on the table.
    2164             :              */
    2165        2594 :             CheckTableForSerializableConflictIn(rel);
    2166             : 
    2167             :             /*
    2168             :              * Need the full transaction-safe pushups.
    2169             :              *
    2170             :              * Create a new empty storage file for the relation, and assign it
    2171             :              * as the relfilenumber value. The old storage file is scheduled
    2172             :              * for deletion at commit.
    2173             :              */
    2174        2594 :             RelationSetNewRelfilenumber(rel, rel->rd_rel->relpersistence);
    2175             : 
    2176        2594 :             heap_relid = RelationGetRelid(rel);
    2177             : 
    2178             :             /*
    2179             :              * The same for the toast table, if any.
    2180             :              */
    2181        2594 :             toast_relid = rel->rd_rel->reltoastrelid;
    2182        2594 :             if (OidIsValid(toast_relid))
    2183             :             {
    2184        1596 :                 Relation    toastrel = relation_open(toast_relid,
    2185             :                                                      AccessExclusiveLock);
    2186             : 
    2187        1596 :                 RelationSetNewRelfilenumber(toastrel,
    2188        1596 :                                             toastrel->rd_rel->relpersistence);
    2189        1596 :                 table_close(toastrel, NoLock);
    2190             :             }
    2191             : 
    2192             :             /*
    2193             :              * Reconstruct the indexes to match, and we're done.
    2194             :              */
    2195        2594 :             reindex_relation(NULL, heap_relid, REINDEX_REL_PROCESS_TOAST,
    2196             :                              &reindex_params);
    2197             :         }
    2198             : 
    2199        2684 :         pgstat_count_truncate(rel);
    2200             :     }
    2201             : 
    2202             :     /* Now go through the hash table, and truncate foreign tables */
    2203        1360 :     if (ft_htab)
    2204             :     {
    2205             :         ForeignTruncateInfo *ft_info;
    2206             :         HASH_SEQ_STATUS seq;
    2207             : 
    2208          30 :         hash_seq_init(&seq, ft_htab);
    2209             : 
    2210          30 :         PG_TRY();
    2211             :         {
    2212          52 :             while ((ft_info = hash_seq_search(&seq)) != NULL)
    2213             :             {
    2214          30 :                 FdwRoutine *routine = GetFdwRoutineByServerId(ft_info->serverid);
    2215             : 
    2216             :                 /* truncate_check_rel() has checked that already */
    2217             :                 Assert(routine->ExecForeignTruncate != NULL);
    2218             : 
    2219          30 :                 routine->ExecForeignTruncate(ft_info->rels,
    2220             :                                              behavior,
    2221             :                                              restart_seqs);
    2222             :             }
    2223             :         }
    2224           8 :         PG_FINALLY();
    2225             :         {
    2226          30 :             hash_destroy(ft_htab);
    2227             :         }
    2228          30 :         PG_END_TRY();
    2229             :     }
    2230             : 
    2231             :     /*
    2232             :      * Restart owned sequences if we were asked to.
    2233             :      */
    2234        1388 :     foreach(cell, seq_relids)
    2235             :     {
    2236          36 :         Oid         seq_relid = lfirst_oid(cell);
    2237             : 
    2238          36 :         ResetSequence(seq_relid);
    2239             :     }
    2240             : 
    2241             :     /*
    2242             :      * Write a WAL record to allow this set of actions to be logically
    2243             :      * decoded.
    2244             :      *
    2245             :      * Assemble an array of relids so we can write a single WAL record for the
    2246             :      * whole action.
    2247             :      */
    2248        1352 :     if (relids_logged != NIL)
    2249             :     {
    2250             :         xl_heap_truncate xlrec;
    2251          50 :         int         i = 0;
    2252             : 
    2253             :         /* should only get here if wal_level >= logical */
    2254             :         Assert(XLogLogicalInfoActive());
    2255             : 
    2256          50 :         logrelids = palloc(list_length(relids_logged) * sizeof(Oid));
    2257         136 :         foreach(cell, relids_logged)
    2258          86 :             logrelids[i++] = lfirst_oid(cell);
    2259             : 
    2260          50 :         xlrec.dbId = MyDatabaseId;
    2261          50 :         xlrec.nrelids = list_length(relids_logged);
    2262          50 :         xlrec.flags = 0;
    2263          50 :         if (behavior == DROP_CASCADE)
    2264           2 :             xlrec.flags |= XLH_TRUNCATE_CASCADE;
    2265          50 :         if (restart_seqs)
    2266           4 :             xlrec.flags |= XLH_TRUNCATE_RESTART_SEQS;
    2267             : 
    2268          50 :         XLogBeginInsert();
    2269          50 :         XLogRegisterData((char *) &xlrec, SizeOfHeapTruncate);
    2270          50 :         XLogRegisterData((char *) logrelids, list_length(relids_logged) * sizeof(Oid));
    2271             : 
    2272          50 :         XLogSetRecordFlags(XLOG_INCLUDE_ORIGIN);
    2273             : 
    2274          50 :         (void) XLogInsert(RM_HEAP_ID, XLOG_HEAP_TRUNCATE);
    2275             :     }
    2276             : 
    2277             :     /*
    2278             :      * Process all AFTER STATEMENT TRUNCATE triggers.
    2279             :      */
    2280        1352 :     resultRelInfo = resultRelInfos;
    2281        4754 :     foreach(cell, rels)
    2282             :     {
    2283             :         UserContext ucxt;
    2284             : 
    2285        3402 :         if (run_as_table_owner)
    2286          70 :             SwitchToUntrustedUser(resultRelInfo->ri_RelationDesc->rd_rel->relowner,
    2287             :                                   &ucxt);
    2288        3402 :         ExecASTruncateTriggers(estate, resultRelInfo);
    2289        3402 :         if (run_as_table_owner)
    2290          70 :             RestoreUserContext(&ucxt);
    2291        3402 :         resultRelInfo++;
    2292             :     }
    2293             : 
    2294             :     /* Handle queued AFTER triggers */
    2295        1352 :     AfterTriggerEndQuery(estate);
    2296             : 
    2297             :     /* We can clean up the EState now */
    2298        1352 :     FreeExecutorState(estate);
    2299             : 
    2300             :     /*
    2301             :      * Close any rels opened by CASCADE (can't do this while EState still
    2302             :      * holds refs)
    2303             :      */
    2304        1352 :     rels = list_difference_ptr(rels, explicit_rels);
    2305        1446 :     foreach(cell, rels)
    2306             :     {
    2307          94 :         Relation    rel = (Relation) lfirst(cell);
    2308             : 
    2309          94 :         table_close(rel, NoLock);
    2310             :     }
    2311        1352 : }
    2312             : 
    2313             : /*
    2314             :  * Check that a given relation is safe to truncate.  Subroutine for
    2315             :  * ExecuteTruncate() and RangeVarCallbackForTruncate().
    2316             :  */
    2317             : static void
    2318        3612 : truncate_check_rel(Oid relid, Form_pg_class reltuple)
    2319             : {
    2320        3612 :     char       *relname = NameStr(reltuple->relname);
    2321             : 
    2322             :     /*
    2323             :      * Only allow truncate on regular tables, foreign tables using foreign
    2324             :      * data wrappers supporting TRUNCATE and partitioned tables (although, the
    2325             :      * latter are only being included here for the following checks; no
    2326             :      * physical truncation will occur in their case.).
    2327             :      */
    2328        3612 :     if (reltuple->relkind == RELKIND_FOREIGN_TABLE)
    2329             :     {
    2330          36 :         Oid         serverid = GetForeignServerIdByRelId(relid);
    2331          36 :         FdwRoutine *fdwroutine = GetFdwRoutineByServerId(serverid);
    2332             : 
    2333          36 :         if (!fdwroutine->ExecForeignTruncate)
    2334           2 :             ereport(ERROR,
    2335             :                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    2336             :                      errmsg("cannot truncate foreign table \"%s\"",
    2337             :                             relname)));
    2338             :     }
    2339        3576 :     else if (reltuple->relkind != RELKIND_RELATION &&
    2340         706 :              reltuple->relkind != RELKIND_PARTITIONED_TABLE)
    2341           0 :         ereport(ERROR,
    2342             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    2343             :                  errmsg("\"%s\" is not a table", relname)));
    2344             : 
    2345             :     /*
    2346             :      * Most system catalogs can't be truncated at all, or at least not unless
    2347             :      * allow_system_table_mods=on. As an exception, however, we allow
    2348             :      * pg_largeobject to be truncated as part of pg_upgrade, because we need
    2349             :      * to change its relfilenode to match the old cluster, and allowing a
    2350             :      * TRUNCATE command to be executed is the easiest way of doing that.
    2351             :      */
    2352        3610 :     if (!allowSystemTableMods && IsSystemClass(relid, reltuple)
    2353          22 :         && (!IsBinaryUpgrade || relid != LargeObjectRelationId))
    2354           2 :         ereport(ERROR,
    2355             :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
    2356             :                  errmsg("permission denied: \"%s\" is a system catalog",
    2357             :                         relname)));
    2358             : 
    2359        3608 :     InvokeObjectTruncateHook(relid);
    2360        3608 : }
    2361             : 
    2362             : /*
    2363             :  * Check that current user has the permission to truncate given relation.
    2364             :  */
    2365             : static void
    2366        1884 : truncate_check_perms(Oid relid, Form_pg_class reltuple)
    2367             : {
    2368        1884 :     char       *relname = NameStr(reltuple->relname);
    2369             :     AclResult   aclresult;
    2370             : 
    2371             :     /* Permissions checks */
    2372        1884 :     aclresult = pg_class_aclcheck(relid, GetUserId(), ACL_TRUNCATE);
    2373        1884 :     if (aclresult != ACLCHECK_OK)
    2374          32 :         aclcheck_error(aclresult, get_relkind_objtype(reltuple->relkind),
    2375             :                        relname);
    2376        1852 : }
    2377             : 
    2378             : /*
    2379             :  * Set of extra sanity checks to check if a given relation is safe to
    2380             :  * truncate.  This is split with truncate_check_rel() as
    2381             :  * RangeVarCallbackForTruncate() cannot open a Relation yet.
    2382             :  */
    2383             : static void
    2384        3498 : truncate_check_activity(Relation rel)
    2385             : {
    2386             :     /*
    2387             :      * Don't allow truncate on temp tables of other backends ... their local
    2388             :      * buffer manager is not going to cope.
    2389             :      */
    2390        3498 :     if (RELATION_IS_OTHER_TEMP(rel))
    2391           0 :         ereport(ERROR,
    2392             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    2393             :                  errmsg("cannot truncate temporary tables of other sessions")));
    2394             : 
    2395             :     /*
    2396             :      * Also check for active uses of the relation in the current transaction,
    2397             :      * including open scans and pending AFTER trigger events.
    2398             :      */
    2399        3498 :     CheckTableNotInUse(rel, "TRUNCATE");
    2400        3498 : }
    2401             : 
    2402             : /*
    2403             :  * storage_name
    2404             :  *    returns the name corresponding to a typstorage/attstorage enum value
    2405             :  */
    2406             : static const char *
    2407          24 : storage_name(char c)
    2408             : {
    2409          24 :     switch (c)
    2410             :     {
    2411           0 :         case TYPSTORAGE_PLAIN:
    2412           0 :             return "PLAIN";
    2413           0 :         case TYPSTORAGE_EXTERNAL:
    2414           0 :             return "EXTERNAL";
    2415          12 :         case TYPSTORAGE_EXTENDED:
    2416          12 :             return "EXTENDED";
    2417          12 :         case TYPSTORAGE_MAIN:
    2418          12 :             return "MAIN";
    2419           0 :         default:
    2420           0 :             return "???";
    2421             :     }
    2422             : }
    2423             : 
    2424             : /*----------
    2425             :  * MergeAttributes
    2426             :  *      Returns new schema given initial schema and superclasses.
    2427             :  *
    2428             :  * Input arguments:
    2429             :  * 'columns' is the column/attribute definition for the table. (It's a list
    2430             :  *      of ColumnDef's.) It is destructively changed.
    2431             :  * 'supers' is a list of OIDs of parent relations, already locked by caller.
    2432             :  * 'relpersistence' is the persistence type of the table.
    2433             :  * 'is_partition' tells if the table is a partition.
    2434             :  *
    2435             :  * Output arguments:
    2436             :  * 'supconstr' receives a list of constraints belonging to the parents,
    2437             :  *      updated as necessary to be valid for the child.
    2438             :  * 'supnotnulls' receives a list of CookedConstraints that corresponds to
    2439             :  *      constraints coming from inheritance parents.
    2440             :  *
    2441             :  * Return value:
    2442             :  * Completed schema list.
    2443             :  *
    2444             :  * Notes:
    2445             :  *    The order in which the attributes are inherited is very important.
    2446             :  *    Intuitively, the inherited attributes should come first. If a table
    2447             :  *    inherits from multiple parents, the order of those attributes are
    2448             :  *    according to the order of the parents specified in CREATE TABLE.
    2449             :  *
    2450             :  *    Here's an example:
    2451             :  *
    2452             :  *      create table person (name text, age int4, location point);
    2453             :  *      create table emp (salary int4, manager text) inherits(person);
    2454             :  *      create table student (gpa float8) inherits (person);
    2455             :  *      create table stud_emp (percent int4) inherits (emp, student);
    2456             :  *
    2457             :  *    The order of the attributes of stud_emp is:
    2458             :  *
    2459             :  *                          person {1:name, 2:age, 3:location}
    2460             :  *                          /    \
    2461             :  *             {6:gpa}  student   emp {4:salary, 5:manager}
    2462             :  *                          \    /
    2463             :  *                         stud_emp {7:percent}
    2464             :  *
    2465             :  *     If the same attribute name appears multiple times, then it appears
    2466             :  *     in the result table in the proper location for its first appearance.
    2467             :  *
    2468             :  *     Constraints (including not-null constraints) for the child table
    2469             :  *     are the union of all relevant constraints, from both the child schema
    2470             :  *     and parent tables.  In addition, in legacy inheritance, each column that
    2471             :  *     appears in a primary key in any of the parents also gets a NOT NULL
    2472             :  *     constraint (partitioning doesn't need this, because the PK itself gets
    2473             :  *     inherited.)
    2474             :  *
    2475             :  *     The default value for a child column is defined as:
    2476             :  *      (1) If the child schema specifies a default, that value is used.
    2477             :  *      (2) If neither the child nor any parent specifies a default, then
    2478             :  *          the column will not have a default.
    2479             :  *      (3) If conflicting defaults are inherited from different parents
    2480             :  *          (and not overridden by the child), an error is raised.
    2481             :  *      (4) Otherwise the inherited default is used.
    2482             :  *
    2483             :  *      Note that the default-value infrastructure is used for generated
    2484             :  *      columns' expressions too, so most of the preceding paragraph applies
    2485             :  *      to generation expressions too.  We insist that a child column be
    2486             :  *      generated if and only if its parent(s) are, but it need not have
    2487             :  *      the same generation expression.
    2488             :  *----------
    2489             :  */
    2490             : static List *
    2491       53010 : MergeAttributes(List *columns, const List *supers, char relpersistence,
    2492             :                 bool is_partition, List **supconstr, List **supnotnulls)
    2493             : {
    2494       53010 :     List       *inh_columns = NIL;
    2495       53010 :     List       *constraints = NIL;
    2496       53010 :     List       *nnconstraints = NIL;
    2497       53010 :     bool        have_bogus_defaults = false;
    2498             :     int         child_attno;
    2499             :     static Node bogus_marker = {0}; /* marks conflicting defaults */
    2500       53010 :     List       *saved_columns = NIL;
    2501             :     ListCell   *lc;
    2502             : 
    2503             :     /*
    2504             :      * Check for and reject tables with too many columns. We perform this
    2505             :      * check relatively early for two reasons: (a) we don't run the risk of
    2506             :      * overflowing an AttrNumber in subsequent code (b) an O(n^2) algorithm is
    2507             :      * okay if we're processing <= 1600 columns, but could take minutes to
    2508             :      * execute if the user attempts to create a table with hundreds of
    2509             :      * thousands of columns.
    2510             :      *
    2511             :      * Note that we also need to check that we do not exceed this figure after
    2512             :      * including columns from inherited relations.
    2513             :      */
    2514       53010 :     if (list_length(columns) > MaxHeapAttributeNumber)
    2515           0 :         ereport(ERROR,
    2516             :                 (errcode(ERRCODE_TOO_MANY_COLUMNS),
    2517             :                  errmsg("tables can have at most %d columns",
    2518             :                         MaxHeapAttributeNumber)));
    2519             : 
    2520             :     /*
    2521             :      * Check for duplicate names in the explicit list of attributes.
    2522             :      *
    2523             :      * Although we might consider merging such entries in the same way that we
    2524             :      * handle name conflicts for inherited attributes, it seems to make more
    2525             :      * sense to assume such conflicts are errors.
    2526             :      *
    2527             :      * We don't use foreach() here because we have two nested loops over the
    2528             :      * columns list, with possible element deletions in the inner one.  If we
    2529             :      * used foreach_delete_current() it could only fix up the state of one of
    2530             :      * the loops, so it seems cleaner to use looping over list indexes for
    2531             :      * both loops.  Note that any deletion will happen beyond where the outer
    2532             :      * loop is, so its index never needs adjustment.
    2533             :      */
    2534      246980 :     for (int coldefpos = 0; coldefpos < list_length(columns); coldefpos++)
    2535             :     {
    2536      193994 :         ColumnDef  *coldef = list_nth_node(ColumnDef, columns, coldefpos);
    2537             : 
    2538      193994 :         if (!is_partition && coldef->typeName == NULL)
    2539             :         {
    2540             :             /*
    2541             :              * Typed table column option that does not belong to a column from
    2542             :              * the type.  This works because the columns from the type come
    2543             :              * first in the list.  (We omit this check for partition column
    2544             :              * lists; those are processed separately below.)
    2545             :              */
    2546           6 :             ereport(ERROR,
    2547             :                     (errcode(ERRCODE_UNDEFINED_COLUMN),
    2548             :                      errmsg("column \"%s\" does not exist",
    2549             :                             coldef->colname)));
    2550             :         }
    2551             : 
    2552             :         /* restpos scans all entries beyond coldef; incr is in loop body */
    2553     6160764 :         for (int restpos = coldefpos + 1; restpos < list_length(columns);)
    2554             :         {
    2555     5966794 :             ColumnDef  *restdef = list_nth_node(ColumnDef, columns, restpos);
    2556             : 
    2557     5966794 :             if (strcmp(coldef->colname, restdef->colname) == 0)
    2558             :             {
    2559          50 :                 if (coldef->is_from_type)
    2560             :                 {
    2561             :                     /*
    2562             :                      * merge the column options into the column from the type
    2563             :                      */
    2564          32 :                     coldef->is_not_null = restdef->is_not_null;
    2565          32 :                     coldef->raw_default = restdef->raw_default;
    2566          32 :                     coldef->cooked_default = restdef->cooked_default;
    2567          32 :                     coldef->constraints = restdef->constraints;
    2568          32 :                     coldef->is_from_type = false;
    2569          32 :                     columns = list_delete_nth_cell(columns, restpos);
    2570             :                 }
    2571             :                 else
    2572          18 :                     ereport(ERROR,
    2573             :                             (errcode(ERRCODE_DUPLICATE_COLUMN),
    2574             :                              errmsg("column \"%s\" specified more than once",
    2575             :                                     coldef->colname)));
    2576             :             }
    2577             :             else
    2578     5966744 :                 restpos++;
    2579             :         }
    2580             :     }
    2581             : 
    2582             :     /*
    2583             :      * In case of a partition, there are no new column definitions, only dummy
    2584             :      * ColumnDefs created for column constraints.  Set them aside for now and
    2585             :      * process them at the end.
    2586             :      */
    2587       52986 :     if (is_partition)
    2588             :     {
    2589        8256 :         saved_columns = columns;
    2590        8256 :         columns = NIL;
    2591             :     }
    2592             : 
    2593             :     /*
    2594             :      * Scan the parents left-to-right, and merge their attributes to form a
    2595             :      * list of inherited columns (inh_columns).
    2596             :      */
    2597       52986 :     child_attno = 0;
    2598       63260 :     foreach(lc, supers)
    2599             :     {
    2600       10346 :         Oid         parent = lfirst_oid(lc);
    2601             :         Relation    relation;
    2602             :         TupleDesc   tupleDesc;
    2603             :         TupleConstr *constr;
    2604             :         AttrMap    *newattmap;
    2605             :         List       *inherited_defaults;
    2606             :         List       *cols_with_defaults;
    2607             :         List       *nnconstrs;
    2608             :         ListCell   *lc1;
    2609             :         ListCell   *lc2;
    2610             :         Bitmapset  *pkattrs;
    2611       10346 :         Bitmapset  *nncols = NULL;
    2612             : 
    2613             :         /* caller already got lock */
    2614       10346 :         relation = table_open(parent, NoLock);
    2615             : 
    2616             :         /*
    2617             :          * Check for active uses of the parent partitioned table in the
    2618             :          * current transaction, such as being used in some manner by an
    2619             :          * enclosing command.
    2620             :          */
    2621       10346 :         if (is_partition)
    2622        8256 :             CheckTableNotInUse(relation, "CREATE TABLE .. PARTITION OF");
    2623             : 
    2624             :         /*
    2625             :          * We do not allow partitioned tables and partitions to participate in
    2626             :          * regular inheritance.
    2627             :          */
    2628       10340 :         if (relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && !is_partition)
    2629           6 :             ereport(ERROR,
    2630             :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    2631             :                      errmsg("cannot inherit from partitioned table \"%s\"",
    2632             :                             RelationGetRelationName(relation))));
    2633       10334 :         if (relation->rd_rel->relispartition && !is_partition)
    2634           6 :             ereport(ERROR,
    2635             :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    2636             :                      errmsg("cannot inherit from partition \"%s\"",
    2637             :                             RelationGetRelationName(relation))));
    2638             : 
    2639       10328 :         if (relation->rd_rel->relkind != RELKIND_RELATION &&
    2640        8252 :             relation->rd_rel->relkind != RELKIND_FOREIGN_TABLE &&
    2641        8232 :             relation->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
    2642           0 :             ereport(ERROR,
    2643             :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    2644             :                      errmsg("inherited relation \"%s\" is not a table or foreign table",
    2645             :                             RelationGetRelationName(relation))));
    2646             : 
    2647             :         /*
    2648             :          * If the parent is permanent, so must be all of its partitions.  Note
    2649             :          * that inheritance allows that case.
    2650             :          */
    2651       10328 :         if (is_partition &&
    2652        8250 :             relation->rd_rel->relpersistence != RELPERSISTENCE_TEMP &&
    2653             :             relpersistence == RELPERSISTENCE_TEMP)
    2654           6 :             ereport(ERROR,
    2655             :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    2656             :                      errmsg("cannot create a temporary relation as partition of permanent relation \"%s\"",
    2657             :                             RelationGetRelationName(relation))));
    2658             : 
    2659             :         /* Permanent rels cannot inherit from temporary ones */
    2660       10322 :         if (relpersistence != RELPERSISTENCE_TEMP &&
    2661        9980 :             relation->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
    2662          24 :             ereport(ERROR,
    2663             :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    2664             :                      errmsg(!is_partition
    2665             :                             ? "cannot inherit from temporary relation \"%s\""
    2666             :                             : "cannot create a permanent relation as partition of temporary relation \"%s\"",
    2667             :                             RelationGetRelationName(relation))));
    2668             : 
    2669             :         /* If existing rel is temp, it must belong to this session */
    2670       10298 :         if (relation->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
    2671         294 :             !relation->rd_islocaltemp)
    2672           0 :             ereport(ERROR,
    2673             :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    2674             :                      errmsg(!is_partition
    2675             :                             ? "cannot inherit from temporary relation of another session"
    2676             :                             : "cannot create as partition of temporary relation of another session")));
    2677             : 
    2678             :         /*
    2679             :          * We should have an UNDER permission flag for this, but for now,
    2680             :          * demand that creator of a child table own the parent.
    2681             :          */
    2682       10298 :         if (!object_ownercheck(RelationRelationId, RelationGetRelid(relation), GetUserId()))
    2683           0 :             aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(relation->rd_rel->relkind),
    2684           0 :                            RelationGetRelationName(relation));
    2685             : 
    2686       10298 :         tupleDesc = RelationGetDescr(relation);
    2687       10298 :         constr = tupleDesc->constr;
    2688             : 
    2689             :         /*
    2690             :          * newattmap->attnums[] will contain the child-table attribute numbers
    2691             :          * for the attributes of this parent table.  (They are not the same
    2692             :          * for parents after the first one, nor if we have dropped columns.)
    2693             :          */
    2694       10298 :         newattmap = make_attrmap(tupleDesc->natts);
    2695             : 
    2696             :         /* We can't process inherited defaults until newattmap is complete. */
    2697       10298 :         inherited_defaults = cols_with_defaults = NIL;
    2698             : 
    2699             :         /*
    2700             :          * All columns that are part of the parent's primary key need to be
    2701             :          * NOT NULL; if partition just the attnotnull bit, otherwise a full
    2702             :          * constraint (if they don't have one already).  Also, we request
    2703             :          * attnotnull on columns that have a not-null constraint that's not
    2704             :          * marked NO INHERIT.
    2705             :          */
    2706       10298 :         pkattrs = RelationGetIndexAttrBitmap(relation,
    2707             :                                              INDEX_ATTR_BITMAP_PRIMARY_KEY);
    2708       10298 :         nnconstrs = RelationGetNotNullConstraints(RelationGetRelid(relation), true);
    2709       11146 :         foreach(lc1, nnconstrs)
    2710         848 :             nncols = bms_add_member(nncols,
    2711         848 :                                     ((CookedConstraint *) lfirst(lc1))->attnum);
    2712             : 
    2713       32124 :         for (AttrNumber parent_attno = 1; parent_attno <= tupleDesc->natts;
    2714       21826 :              parent_attno++)
    2715             :         {
    2716       21850 :             Form_pg_attribute attribute = TupleDescAttr(tupleDesc,
    2717             :                                                         parent_attno - 1);
    2718       21850 :             char       *attributeName = NameStr(attribute->attname);
    2719             :             int         exist_attno;
    2720             :             ColumnDef  *newdef;
    2721             :             ColumnDef  *mergeddef;
    2722             : 
    2723             :             /*
    2724             :              * Ignore dropped columns in the parent.
    2725             :              */
    2726       21850 :             if (attribute->attisdropped)
    2727         192 :                 continue;       /* leave newattmap->attnums entry as zero */
    2728             : 
    2729             :             /*
    2730             :              * Create new column definition
    2731             :              */
    2732       21658 :             newdef = makeColumnDef(attributeName, attribute->atttypid,
    2733             :                                    attribute->atttypmod, attribute->attcollation);
    2734       21658 :             newdef->storage = attribute->attstorage;
    2735       21658 :             newdef->generated = attribute->attgenerated;
    2736       21658 :             if (CompressionMethodIsValid(attribute->attcompression))
    2737          24 :                 newdef->compression =
    2738          24 :                     pstrdup(GetCompressionMethodName(attribute->attcompression));
    2739             : 
    2740             :             /*
    2741             :              * Regular inheritance children are independent enough not to
    2742             :              * inherit identity columns.  But partitions are integral part of
    2743             :              * a partitioned table and inherit identity column.
    2744             :              */
    2745       21658 :             if (is_partition)
    2746       17738 :                 newdef->identity = attribute->attidentity;
    2747             : 
    2748             :             /*
    2749             :              * Does it match some previously considered column from another
    2750             :              * parent?
    2751             :              */
    2752       21658 :             exist_attno = findAttrByName(attributeName, inh_columns);
    2753       21658 :             if (exist_attno > 0)
    2754             :             {
    2755             :                 /*
    2756             :                  * Yes, try to merge the two column definitions.
    2757             :                  */
    2758         296 :                 mergeddef = MergeInheritedAttribute(inh_columns, exist_attno, newdef);
    2759             : 
    2760         272 :                 newattmap->attnums[parent_attno - 1] = exist_attno;
    2761             : 
    2762             :                 /*
    2763             :                  * Partitions have only one parent, so conflict should never
    2764             :                  * occur.
    2765             :                  */
    2766             :                 Assert(!is_partition);
    2767             :             }
    2768             :             else
    2769             :             {
    2770             :                 /*
    2771             :                  * No, create a new inherited column
    2772             :                  */
    2773       21362 :                 newdef->inhcount = 1;
    2774       21362 :                 newdef->is_local = false;
    2775       21362 :                 inh_columns = lappend(inh_columns, newdef);
    2776             : 
    2777       21362 :                 newattmap->attnums[parent_attno - 1] = ++child_attno;
    2778             : 
    2779       21362 :                 mergeddef = newdef;
    2780             :             }
    2781             : 
    2782             :             /*
    2783             :              * mark attnotnull if parent has it and it's not NO INHERIT
    2784             :              */
    2785       42420 :             if (bms_is_member(parent_attno, nncols) ||
    2786       20786 :                 bms_is_member(parent_attno - FirstLowInvalidHeapAttributeNumber,
    2787             :                               pkattrs))
    2788        2134 :                 mergeddef->is_not_null = true;
    2789             : 
    2790             :             /*
    2791             :              * In regular inheritance, columns in the parent's primary key get
    2792             :              * an extra not-null constraint.  Partitioning doesn't need this,
    2793             :              * because the PK itself is going to be cloned to the partition.
    2794             :              */
    2795       25530 :             if (!is_partition &&
    2796        3896 :                 bms_is_member(parent_attno -
    2797             :                               FirstLowInvalidHeapAttributeNumber,
    2798             :                               pkattrs))
    2799             :             {
    2800             :                 CookedConstraint *nn;
    2801             : 
    2802         256 :                 nn = palloc(sizeof(CookedConstraint));
    2803         256 :                 nn->contype = CONSTR_NOTNULL;
    2804         256 :                 nn->conoid = InvalidOid;
    2805         256 :                 nn->name = NULL;
    2806         256 :                 nn->attnum = newattmap->attnums[parent_attno - 1];
    2807         256 :                 nn->expr = NULL;
    2808         256 :                 nn->skip_validation = false;
    2809         256 :                 nn->is_local = false;
    2810         256 :                 nn->inhcount = 1;
    2811         256 :                 nn->is_no_inherit = false;
    2812             : 
    2813         256 :                 nnconstraints = lappend(nnconstraints, nn);
    2814             :             }
    2815             : 
    2816             :             /*
    2817             :              * Locate default/generation expression if any
    2818             :              */
    2819       21634 :             if (attribute->atthasdef)
    2820             :             {
    2821             :                 Node       *this_default;
    2822             : 
    2823         650 :                 this_default = TupleDescGetDefault(tupleDesc, parent_attno);
    2824         650 :                 if (this_default == NULL)
    2825           0 :                     elog(ERROR, "default expression not found for attribute %d of relation \"%s\"",
    2826             :                          parent_attno, RelationGetRelationName(relation));
    2827             : 
    2828             :                 /*
    2829             :                  * If it's a GENERATED default, it might contain Vars that
    2830             :                  * need to be mapped to the inherited column(s)' new numbers.
    2831             :                  * We can't do that till newattmap is ready, so just remember
    2832             :                  * all the inherited default expressions for the moment.
    2833             :                  */
    2834         650 :                 inherited_defaults = lappend(inherited_defaults, this_default);
    2835         650 :                 cols_with_defaults = lappend(cols_with_defaults, mergeddef);
    2836             :             }
    2837             :         }
    2838             : 
    2839             :         /*
    2840             :          * Now process any inherited default expressions, adjusting attnos
    2841             :          * using the completed newattmap map.
    2842             :          */
    2843       10924 :         forboth(lc1, inherited_defaults, lc2, cols_with_defaults)
    2844             :         {
    2845         650 :             Node       *this_default = (Node *) lfirst(lc1);
    2846         650 :             ColumnDef  *def = (ColumnDef *) lfirst(lc2);
    2847             :             bool        found_whole_row;
    2848             : 
    2849             :             /* Adjust Vars to match new table's column numbering */
    2850         650 :             this_default = map_variable_attnos(this_default,
    2851             :                                                1, 0,
    2852             :                                                newattmap,
    2853             :                                                InvalidOid, &found_whole_row);
    2854             : 
    2855             :             /*
    2856             :              * For the moment we have to reject whole-row variables.  We could
    2857             :              * convert them, if we knew the new table's rowtype OID, but that
    2858             :              * hasn't been assigned yet.  (A variable could only appear in a
    2859             :              * generation expression, so the error message is correct.)
    2860             :              */
    2861         650 :             if (found_whole_row)
    2862           0 :                 ereport(ERROR,
    2863             :                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    2864             :                          errmsg("cannot convert whole-row table reference"),
    2865             :                          errdetail("Generation expression for column \"%s\" contains a whole-row reference to table \"%s\".",
    2866             :                                    def->colname,
    2867             :                                    RelationGetRelationName(relation))));
    2868             : 
    2869             :             /*
    2870             :              * If we already had a default from some prior parent, check to
    2871             :              * see if they are the same.  If so, no problem; if not, mark the
    2872             :              * column as having a bogus default.  Below, we will complain if
    2873             :              * the bogus default isn't overridden by the child columns.
    2874             :              */
    2875             :             Assert(def->raw_default == NULL);
    2876         650 :             if (def->cooked_default == NULL)
    2877         620 :                 def->cooked_default = this_default;
    2878          30 :             else if (!equal(def->cooked_default, this_default))
    2879             :             {
    2880          24 :                 def->cooked_default = &bogus_marker;
    2881          24 :                 have_bogus_defaults = true;
    2882             :             }
    2883             :         }
    2884             : 
    2885             :         /*
    2886             :          * Now copy the CHECK constraints of this parent, adjusting attnos
    2887             :          * using the completed newattmap map.  Identically named constraints
    2888             :          * are merged if possible, else we throw error.
    2889             :          */
    2890       10274 :         if (constr && constr->num_check > 0)
    2891             :         {
    2892         304 :             ConstrCheck *check = constr->check;
    2893             : 
    2894         638 :             for (int i = 0; i < constr->num_check; i++)
    2895             :             {
    2896         334 :                 char       *name = check[i].ccname;
    2897             :                 Node       *expr;
    2898             :                 bool        found_whole_row;
    2899             : 
    2900             :                 /* ignore if the constraint is non-inheritable */
    2901         334 :                 if (check[i].ccnoinherit)
    2902          48 :                     continue;
    2903             : 
    2904             :                 /* Adjust Vars to match new table's column numbering */
    2905         286 :                 expr = map_variable_attnos(stringToNode(check[i].ccbin),
    2906             :                                            1, 0,
    2907             :                                            newattmap,
    2908             :                                            InvalidOid, &found_whole_row);
    2909             : 
    2910             :                 /*
    2911             :                  * For the moment we have to reject whole-row variables. We
    2912             :                  * could convert them, if we knew the new table's rowtype OID,
    2913             :                  * but that hasn't been assigned yet.
    2914             :                  */
    2915         286 :                 if (found_whole_row)
    2916           0 :                     ereport(ERROR,
    2917             :                             (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    2918             :                              errmsg("cannot convert whole-row table reference"),
    2919             :                              errdetail("Constraint \"%s\" contains a whole-row reference to table \"%s\".",
    2920             :                                        name,
    2921             :                                        RelationGetRelationName(relation))));
    2922             : 
    2923         286 :                 constraints = MergeCheckConstraint(constraints, name, expr);
    2924             :             }
    2925             :         }
    2926             : 
    2927             :         /*
    2928             :          * Also copy the not-null constraints from this parent.  The
    2929             :          * attnotnull markings were already installed above.
    2930             :          */
    2931       11122 :         foreach(lc1, nnconstrs)
    2932             :         {
    2933         848 :             CookedConstraint *nn = lfirst(lc1);
    2934             : 
    2935             :             Assert(nn->contype == CONSTR_NOTNULL);
    2936             : 
    2937         848 :             nn->attnum = newattmap->attnums[nn->attnum - 1];
    2938         848 :             nn->is_local = false;
    2939         848 :             nn->inhcount = 1;
    2940             : 
    2941         848 :             nnconstraints = lappend(nnconstraints, nn);
    2942             :         }
    2943             : 
    2944       10274 :         free_attrmap(newattmap);
    2945             : 
    2946             :         /*
    2947             :          * Close the parent rel, but keep our lock on it until xact commit.
    2948             :          * That will prevent someone else from deleting or ALTERing the parent
    2949             :          * before the child is committed.
    2950             :          */
    2951       10274 :         table_close(relation, NoLock);
    2952             :     }
    2953             : 
    2954             :     /*
    2955             :      * If we had no inherited attributes, the result columns are just the
    2956             :      * explicitly declared columns.  Otherwise, we need to merge the declared
    2957             :      * columns into the inherited column list.  Although, we never have any
    2958             :      * explicitly declared columns if the table is a partition.
    2959             :      */
    2960       52914 :     if (inh_columns != NIL)
    2961             :     {
    2962        9936 :         int         newcol_attno = 0;
    2963             : 
    2964       10726 :         foreach(lc, columns)
    2965             :         {
    2966         838 :             ColumnDef  *newdef = lfirst_node(ColumnDef, lc);
    2967         838 :             char       *attributeName = newdef->colname;
    2968             :             int         exist_attno;
    2969             : 
    2970             :             /*
    2971             :              * Partitions have only one parent and have no column definitions
    2972             :              * of their own, so conflict should never occur.
    2973             :              */
    2974             :             Assert(!is_partition);
    2975             : 
    2976         838 :             newcol_attno++;
    2977             : 
    2978             :             /*
    2979             :              * Does it match some inherited column?
    2980             :              */
    2981         838 :             exist_attno = findAttrByName(attributeName, inh_columns);
    2982         838 :             if (exist_attno > 0)
    2983             :             {
    2984             :                 /*
    2985             :                  * Yes, try to merge the two column definitions.
    2986             :                  */
    2987         280 :                 MergeChildAttribute(inh_columns, exist_attno, newcol_attno, newdef);
    2988             :             }
    2989             :             else
    2990             :             {
    2991             :                 /*
    2992             :                  * No, attach new column unchanged to result columns.
    2993             :                  */
    2994         558 :                 inh_columns = lappend(inh_columns, newdef);
    2995             :             }
    2996             :         }
    2997             : 
    2998        9888 :         columns = inh_columns;
    2999             : 
    3000             :         /*
    3001             :          * Check that we haven't exceeded the legal # of columns after merging
    3002             :          * in inherited columns.
    3003             :          */
    3004        9888 :         if (list_length(columns) > MaxHeapAttributeNumber)
    3005           0 :             ereport(ERROR,
    3006             :                     (errcode(ERRCODE_TOO_MANY_COLUMNS),
    3007             :                      errmsg("tables can have at most %d columns",
    3008             :                             MaxHeapAttributeNumber)));
    3009             :     }
    3010             : 
    3011             :     /*
    3012             :      * Now that we have the column definition list for a partition, we can
    3013             :      * check whether the columns referenced in the column constraint specs
    3014             :      * actually exist.  Also, merge column defaults.
    3015             :      */
    3016       52866 :     if (is_partition)
    3017             :     {
    3018        8422 :         foreach(lc, saved_columns)
    3019             :         {
    3020         208 :             ColumnDef  *restdef = lfirst(lc);
    3021         208 :             bool        found = false;
    3022             :             ListCell   *l;
    3023             : 
    3024         784 :             foreach(l, columns)
    3025             :             {
    3026         588 :                 ColumnDef  *coldef = lfirst(l);
    3027             : 
    3028         588 :                 if (strcmp(coldef->colname, restdef->colname) == 0)
    3029             :                 {
    3030         208 :                     found = true;
    3031             : 
    3032             :                     /*
    3033             :                      * Check for conflicts related to generated columns.
    3034             :                      *
    3035             :                      * Same rules as above: generated-ness has to match the
    3036             :                      * parent, but the contents of the generation expression
    3037             :                      * can be different.
    3038             :                      */
    3039         208 :                     if (coldef->generated)
    3040             :                     {
    3041         106 :                         if (restdef->raw_default && !restdef->generated)
    3042           6 :                             ereport(ERROR,
    3043             :                                     (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
    3044             :                                      errmsg("column \"%s\" inherits from generated column but specifies default",
    3045             :                                             restdef->colname)));
    3046         100 :                         if (restdef->identity)
    3047           0 :                             ereport(ERROR,
    3048             :                                     (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
    3049             :                                      errmsg("column \"%s\" inherits from generated column but specifies identity",
    3050             :                                             restdef->colname)));
    3051             :                     }
    3052             :                     else
    3053             :                     {
    3054         102 :                         if (restdef->generated)
    3055           6 :                             ereport(ERROR,
    3056             :                                     (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
    3057             :                                      errmsg("child column \"%s\" specifies generation expression",
    3058             :                                             restdef->colname),
    3059             :                                      errhint("A child table column cannot be generated unless its parent column is.")));
    3060             :                     }
    3061             : 
    3062             :                     /*
    3063             :                      * Override the parent's default value for this column
    3064             :                      * (coldef->cooked_default) with the partition's local
    3065             :                      * definition (restdef->raw_default), if there's one. It
    3066             :                      * should be physically impossible to get a cooked default
    3067             :                      * in the local definition or a raw default in the
    3068             :                      * inherited definition, but make sure they're nulls, for
    3069             :                      * future-proofing.
    3070             :                      */
    3071             :                     Assert(restdef->cooked_default == NULL);
    3072             :                     Assert(coldef->raw_default == NULL);
    3073         196 :                     if (restdef->raw_default)
    3074             :                     {
    3075         124 :                         coldef->raw_default = restdef->raw_default;
    3076         124 :                         coldef->cooked_default = NULL;
    3077             :                     }
    3078             :                 }
    3079             :             }
    3080             : 
    3081             :             /* complain for constraints on columns not in parent */
    3082         196 :             if (!found)
    3083           0 :                 ereport(ERROR,
    3084             :                         (errcode(ERRCODE_UNDEFINED_COLUMN),
    3085             :                          errmsg("column \"%s\" does not exist",
    3086             :                                 restdef->colname)));
    3087             :         }
    3088             :     }
    3089             : 
    3090             :     /*
    3091             :      * If we found any conflicting parent default values, check to make sure
    3092             :      * they were overridden by the child.
    3093             :      */
    3094       52854 :     if (have_bogus_defaults)
    3095             :     {
    3096          54 :         foreach(lc, columns)
    3097             :         {
    3098          42 :             ColumnDef  *def = lfirst(lc);
    3099             : 
    3100          42 :             if (def->cooked_default == &bogus_marker)
    3101             :             {
    3102          12 :                 if (def->generated)
    3103           6 :                     ereport(ERROR,
    3104             :                             (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
    3105             :                              errmsg("column \"%s\" inherits conflicting generation expressions",
    3106             :                                     def->colname),
    3107             :                              errhint("To resolve the conflict, specify a generation expression explicitly.")));
    3108             :                 else
    3109           6 :                     ereport(ERROR,
    3110             :                             (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
    3111             :                              errmsg("column \"%s\" inherits conflicting default values",
    3112             :                                     def->colname),
    3113             :                              errhint("To resolve the conflict, specify a default explicitly.")));
    3114             :             }
    3115             :         }
    3116             :     }
    3117             : 
    3118       52842 :     *supconstr = constraints;
    3119       52842 :     *supnotnulls = nnconstraints;
    3120             : 
    3121       52842 :     return columns;
    3122             : }
    3123             : 
    3124             : 
    3125             : /*
    3126             :  * MergeCheckConstraint
    3127             :  *      Try to merge an inherited CHECK constraint with previous ones
    3128             :  *
    3129             :  * If we inherit identically-named constraints from multiple parents, we must
    3130             :  * merge them, or throw an error if they don't have identical definitions.
    3131             :  *
    3132             :  * constraints is a list of CookedConstraint structs for previous constraints.
    3133             :  *
    3134             :  * If the new constraint matches an existing one, then the existing
    3135             :  * constraint's inheritance count is updated.  If there is a conflict (same
    3136             :  * name but different expression), throw an error.  If the constraint neither
    3137             :  * matches nor conflicts with an existing one, a new constraint is appended to
    3138             :  * the list.
    3139             :  */
    3140             : static List *
    3141         286 : MergeCheckConstraint(List *constraints, const char *name, Node *expr)
    3142             : {
    3143             :     ListCell   *lc;
    3144             :     CookedConstraint *newcon;
    3145             : 
    3146         316 :     foreach(lc, constraints)
    3147             :     {
    3148          72 :         CookedConstraint *ccon = (CookedConstraint *) lfirst(lc);
    3149             : 
    3150             :         Assert(ccon->contype == CONSTR_CHECK);
    3151             : 
    3152             :         /* Non-matching names never conflict */
    3153          72 :         if (strcmp(ccon->name, name) != 0)
    3154          30 :             continue;
    3155             : 
    3156          42 :         if (equal(expr, ccon->expr))
    3157             :         {
    3158             :             /* OK to merge constraint with existing */
    3159          42 :             ccon->inhcount++;
    3160          42 :             if (ccon->inhcount < 0)
    3161           0 :                 ereport(ERROR,
    3162             :                         errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
    3163             :                         errmsg("too many inheritance parents"));
    3164          42 :             return constraints;
    3165             :         }
    3166             : 
    3167           0 :         ereport(ERROR,
    3168             :                 (errcode(ERRCODE_DUPLICATE_OBJECT),
    3169             :                  errmsg("check constraint name \"%s\" appears multiple times but with different expressions",
    3170             :                         name)));
    3171             :     }
    3172             : 
    3173             :     /*
    3174             :      * Constraint couldn't be merged with an existing one and also didn't
    3175             :      * conflict with an existing one, so add it as a new one to the list.
    3176             :      */
    3177         244 :     newcon = palloc0_object(CookedConstraint);
    3178         244 :     newcon->contype = CONSTR_CHECK;
    3179         244 :     newcon->name = pstrdup(name);
    3180         244 :     newcon->expr = expr;
    3181         244 :     newcon->inhcount = 1;
    3182         244 :     return lappend(constraints, newcon);
    3183             : }
    3184             : 
    3185             : /*
    3186             :  * MergeChildAttribute
    3187             :  *      Merge given child attribute definition into given inherited attribute.
    3188             :  *
    3189             :  * Input arguments:
    3190             :  * 'inh_columns' is the list of inherited ColumnDefs.
    3191             :  * 'exist_attno' is the number of the inherited attribute in inh_columns
    3192             :  * 'newcol_attno' is the attribute number in child table's schema definition
    3193             :  * 'newdef' is the column/attribute definition from the child table.
    3194             :  *
    3195             :  * The ColumnDef in 'inh_columns' list is modified.  The child attribute's
    3196             :  * ColumnDef remains unchanged.
    3197             :  *
    3198             :  * Notes:
    3199             :  * - The attribute is merged according to the rules laid out in the prologue
    3200             :  *   of MergeAttributes().
    3201             :  * - If matching inherited attribute exists but the child attribute can not be
    3202             :  *   merged into it, the function throws respective errors.
    3203             :  * - A partition can not have its own column definitions. Hence this function
    3204             :  *   is applicable only to a regular inheritance child.
    3205             :  */
    3206             : static void
    3207         280 : MergeChildAttribute(List *inh_columns, int exist_attno, int newcol_attno, const ColumnDef *newdef)
    3208             : {
    3209         280 :     char       *attributeName = newdef->colname;
    3210             :     ColumnDef  *inhdef;
    3211             :     Oid         inhtypeid,
    3212             :                 newtypeid;
    3213             :     int32       inhtypmod,
    3214             :                 newtypmod;
    3215             :     Oid         inhcollid,
    3216             :                 newcollid;
    3217             : 
    3218         280 :     if (exist_attno == newcol_attno)
    3219         252 :         ereport(NOTICE,
    3220             :                 (errmsg("merging column \"%s\" with inherited definition",
    3221             :                         attributeName)));
    3222             :     else
    3223          28 :         ereport(NOTICE,
    3224             :                 (errmsg("moving and merging column \"%s\" with inherited definition", attributeName),
    3225             :                  errdetail("User-specified column moved to the position of the inherited column.")));
    3226             : 
    3227         280 :     inhdef = list_nth_node(ColumnDef, inh_columns, exist_attno - 1);
    3228             : 
    3229             :     /*
    3230             :      * Must have the same type and typmod
    3231             :      */
    3232         280 :     typenameTypeIdAndMod(NULL, inhdef->typeName, &inhtypeid, &inhtypmod);
    3233         280 :     typenameTypeIdAndMod(NULL, newdef->typeName, &newtypeid, &newtypmod);
    3234         280 :     if (inhtypeid != newtypeid || inhtypmod != newtypmod)
    3235          12 :         ereport(ERROR,
    3236             :                 (errcode(ERRCODE_DATATYPE_MISMATCH),
    3237             :                  errmsg("column \"%s\" has a type conflict",
    3238             :                         attributeName),
    3239             :                  errdetail("%s versus %s",
    3240             :                            format_type_with_typemod(inhtypeid, inhtypmod),
    3241             :                            format_type_with_typemod(newtypeid, newtypmod))));
    3242             : 
    3243             :     /*
    3244             :      * Must have the same collation
    3245             :      */
    3246         268 :     inhcollid = GetColumnDefCollation(NULL, inhdef, inhtypeid);
    3247         268 :     newcollid = GetColumnDefCollation(NULL, newdef, newtypeid);
    3248         268 :     if (inhcollid != newcollid)
    3249           6 :         ereport(ERROR,
    3250             :                 (errcode(ERRCODE_COLLATION_MISMATCH),
    3251             :                  errmsg("column \"%s\" has a collation conflict",
    3252             :                         attributeName),
    3253             :                  errdetail("\"%s\" versus \"%s\"",
    3254             :                            get_collation_name(inhcollid),
    3255             :                            get_collation_name(newcollid))));
    3256             : 
    3257             :     /*
    3258             :      * Identity is never inherited by a regular inheritance child. Pick
    3259             :      * child's identity definition if there's one.
    3260             :      */
    3261         262 :     inhdef->identity = newdef->identity;
    3262             : 
    3263             :     /*
    3264             :      * Copy storage parameter
    3265             :      */
    3266         262 :     if (inhdef->storage == 0)
    3267           0 :         inhdef->storage = newdef->storage;
    3268         262 :     else if (newdef->storage != 0 && inhdef->storage != newdef->storage)
    3269           6 :         ereport(ERROR,
    3270             :                 (errcode(ERRCODE_DATATYPE_MISMATCH),
    3271             :                  errmsg("column \"%s\" has a storage parameter conflict",
    3272             :                         attributeName),
    3273             :                  errdetail("%s versus %s",
    3274             :                            storage_name(inhdef->storage),
    3275             :                            storage_name(newdef->storage))));
    3276             : 
    3277             :     /*
    3278             :      * Copy compression parameter
    3279             :      */
    3280         256 :     if (inhdef->compression == NULL)
    3281         250 :         inhdef->compression = newdef->compression;
    3282           6 :     else if (newdef->compression != NULL)
    3283             :     {
    3284           6 :         if (strcmp(inhdef->compression, newdef->compression) != 0)
    3285           6 :             ereport(ERROR,
    3286             :                     (errcode(ERRCODE_DATATYPE_MISMATCH),
    3287             :                      errmsg("column \"%s\" has a compression method conflict",
    3288             :                             attributeName),
    3289             :                      errdetail("%s versus %s", inhdef->compression, newdef->compression)));
    3290             :     }
    3291             : 
    3292             :     /*
    3293             :      * Merge of not-null constraints = OR 'em together
    3294             :      */
    3295         250 :     inhdef->is_not_null |= newdef->is_not_null;
    3296             : 
    3297             :     /*
    3298             :      * Check for conflicts related to generated columns.
    3299             :      *
    3300             :      * If the parent column is generated, the child column will be made a
    3301             :      * generated column if it isn't already.  If it is a generated column,
    3302             :      * we'll take its generation expression in preference to the parent's.  We
    3303             :      * must check that the child column doesn't specify a default value or
    3304             :      * identity, which matches the rules for a single column in
    3305             :      * parse_utilcmd.c.
    3306             :      *
    3307             :      * Conversely, if the parent column is not generated, the child column
    3308             :      * can't be either.  (We used to allow that, but it results in being able
    3309             :      * to override the generation expression via UPDATEs through the parent.)
    3310             :      */
    3311         250 :     if (inhdef->generated)
    3312             :     {
    3313          26 :         if (newdef->raw_default && !newdef->generated)
    3314           6 :             ereport(ERROR,
    3315             :                     (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
    3316             :                      errmsg("column \"%s\" inherits from generated column but specifies default",
    3317             :                             inhdef->colname)));
    3318          20 :         if (newdef->identity)
    3319           6 :             ereport(ERROR,
    3320             :                     (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
    3321             :                      errmsg("column \"%s\" inherits from generated column but specifies identity",
    3322             :                             inhdef->colname)));
    3323             :     }
    3324             :     else
    3325             :     {
    3326         224 :         if (newdef->generated)
    3327           6 :             ereport(ERROR,
    3328             :                     (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
    3329             :                      errmsg("child column \"%s\" specifies generation expression",
    3330             :                             inhdef->colname),
    3331             :                      errhint("A child table column cannot be generated unless its parent column is.")));
    3332             :     }
    3333             : 
    3334             :     /*
    3335             :      * If new def has a default, override previous default
    3336             :      */
    3337         232 :     if (newdef->raw_default != NULL)
    3338             :     {
    3339          18 :         inhdef->raw_default = newdef->raw_default;
    3340          18 :         inhdef->cooked_default = newdef->cooked_default;
    3341             :     }
    3342             : 
    3343             :     /* Mark the column as locally defined */
    3344         232 :     inhdef->is_local = true;
    3345         232 : }
    3346             : 
    3347             : /*
    3348             :  * MergeInheritedAttribute
    3349             :  *      Merge given parent attribute definition into specified attribute
    3350             :  *      inherited from the previous parents.
    3351             :  *
    3352             :  * Input arguments:
    3353             :  * 'inh_columns' is the list of previously inherited ColumnDefs.
    3354             :  * 'exist_attno' is the number the existing matching attribute in inh_columns.
    3355             :  * 'newdef' is the new parent column/attribute definition to be merged.
    3356             :  *
    3357             :  * The matching ColumnDef in 'inh_columns' list is modified and returned.
    3358             :  *
    3359             :  * Notes:
    3360             :  * - The attribute is merged according to the rules laid out in the prologue
    3361             :  *   of MergeAttributes().
    3362             :  * - If matching inherited attribute exists but the new attribute can not be
    3363             :  *   merged into it, the function throws respective errors.
    3364             :  * - A partition inherits from only a single parent. Hence this function is
    3365             :  *   applicable only to a regular inheritance.
    3366             :  */
    3367             : static ColumnDef *
    3368         296 : MergeInheritedAttribute(List *inh_columns,
    3369             :                         int exist_attno,
    3370             :                         const ColumnDef *newdef)
    3371             : {
    3372         296 :     char       *attributeName = newdef->colname;
    3373             :     ColumnDef  *prevdef;
    3374             :     Oid         prevtypeid,
    3375             :                 newtypeid;
    3376             :     int32       prevtypmod,
    3377             :                 newtypmod;
    3378             :     Oid         prevcollid,
    3379             :                 newcollid;
    3380             : 
    3381         296 :     ereport(NOTICE,
    3382             :             (errmsg("merging multiple inherited definitions of column \"%s\"",
    3383             :                     attributeName)));
    3384         296 :     prevdef = list_nth_node(ColumnDef, inh_columns, exist_attno - 1);
    3385             : 
    3386             :     /*
    3387             :      * Must have the same type and typmod
    3388             :      */
    3389         296 :     typenameTypeIdAndMod(NULL, prevdef->typeName, &prevtypeid, &prevtypmod);
    3390         296 :     typenameTypeIdAndMod(NULL, newdef->typeName, &newtypeid, &newtypmod);
    3391         296 :     if (prevtypeid != newtypeid || prevtypmod != newtypmod)
    3392           0 :         ereport(ERROR,
    3393             :                 (errcode(ERRCODE_DATATYPE_MISMATCH),
    3394             :                  errmsg("inherited column \"%s\" has a type conflict",
    3395             :                         attributeName),
    3396             :                  errdetail("%s versus %s",
    3397             :                            format_type_with_typemod(prevtypeid, prevtypmod),
    3398             :                            format_type_with_typemod(newtypeid, newtypmod))));
    3399             : 
    3400             :     /*
    3401             :      * Must have the same collation
    3402             :      */
    3403         296 :     prevcollid = GetColumnDefCollation(NULL, prevdef, prevtypeid);
    3404         296 :     newcollid = GetColumnDefCollation(NULL, newdef, newtypeid);
    3405         296 :     if (prevcollid != newcollid)
    3406           0 :         ereport(ERROR,
    3407             :                 (errcode(ERRCODE_COLLATION_MISMATCH),
    3408             :                  errmsg("inherited column \"%s\" has a collation conflict",
    3409             :                         attributeName),
    3410             :                  errdetail("\"%s\" versus \"%s\"",
    3411             :                            get_collation_name(prevcollid),
    3412             :                            get_collation_name(newcollid))));
    3413             : 
    3414             :     /*
    3415             :      * Copy/check storage parameter
    3416             :      */
    3417         296 :     if (prevdef->storage == 0)
    3418           0 :         prevdef->storage = newdef->storage;
    3419         296 :     else if (prevdef->storage != newdef->storage)
    3420           6 :         ereport(ERROR,
    3421             :                 (errcode(ERRCODE_DATATYPE_MISMATCH),
    3422             :                  errmsg("inherited column \"%s\" has a storage parameter conflict",
    3423             :                         attributeName),
    3424             :                  errdetail("%s versus %s",
    3425             :                            storage_name(prevdef->storage),
    3426             :                            storage_name(newdef->storage))));
    3427             : 
    3428             :     /*
    3429             :      * Copy/check compression parameter
    3430             :      */
    3431         290 :     if (prevdef->compression == NULL)
    3432         284 :         prevdef->compression = newdef->compression;
    3433           6 :     else if (strcmp(prevdef->compression, newdef->compression) != 0)
    3434           6 :         ereport(ERROR,
    3435             :                 (errcode(ERRCODE_DATATYPE_MISMATCH),
    3436             :                  errmsg("column \"%s\" has a compression method conflict",
    3437             :                         attributeName),
    3438             :                  errdetail("%s versus %s", prevdef->compression, newdef->compression)));
    3439             : 
    3440             :     /*
    3441             :      * Check for GENERATED conflicts
    3442             :      */
    3443         284 :     if (prevdef->generated != newdef->generated)
    3444          12 :         ereport(ERROR,
    3445             :                 (errcode(ERRCODE_DATATYPE_MISMATCH),
    3446             :                  errmsg("inherited column \"%s\" has a generation conflict",
    3447             :                         attributeName)));
    3448             : 
    3449             :     /*
    3450             :      * Default and other constraints are handled by the caller.
    3451             :      */
    3452             : 
    3453         272 :     prevdef->inhcount++;
    3454         272 :     if (prevdef->inhcount < 0)
    3455           0 :         ereport(ERROR,
    3456             :                 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
    3457             :                 errmsg("too many inheritance parents"));
    3458             : 
    3459         272 :     return prevdef;
    3460             : }
    3461             : 
    3462             : /*
    3463             :  * StoreCatalogInheritance
    3464             :  *      Updates the system catalogs with proper inheritance information.
    3465             :  *
    3466             :  * supers is a list of the OIDs of the new relation's direct ancestors.
    3467             :  */
    3468             : static void
    3469       52296 : StoreCatalogInheritance(Oid relationId, List *supers,
    3470             :                         bool child_is_partition)
    3471             : {
    3472             :     Relation    relation;
    3473             :     int32       seqNumber;
    3474             :     ListCell   *entry;
    3475             : 
    3476             :     /*
    3477             :      * sanity checks
    3478             :      */
    3479             :     Assert(OidIsValid(relationId));
    3480             : 
    3481       52296 :     if (supers == NIL)
    3482       42750 :         return;
    3483             : 
    3484             :     /*
    3485             :      * Store INHERITS information in pg_inherits using direct ancestors only.
    3486             :      * Also enter dependencies on the direct ancestors, and make sure they are
    3487             :      * marked with relhassubclass = true.
    3488             :      *
    3489             :      * (Once upon a time, both direct and indirect ancestors were found here
    3490             :      * and then entered into pg_ipl.  Since that catalog doesn't exist
    3491             :      * anymore, there's no need to look for indirect ancestors.)
    3492             :      */
    3493        9546 :     relation = table_open(InheritsRelationId, RowExclusiveLock);
    3494             : 
    3495        9546 :     seqNumber = 1;
    3496       19358 :     foreach(entry, supers)
    3497             :     {
    3498        9812 :         Oid         parentOid = lfirst_oid(entry);
    3499             : 
    3500        9812 :         StoreCatalogInheritance1(relationId, parentOid, seqNumber, relation,
    3501             :                                  child_is_partition);
    3502        9812 :         seqNumber++;
    3503             :     }
    3504             : 
    3505        9546 :     table_close(relation, RowExclusiveLock);
    3506             : }
    3507             : 
    3508             : /*
    3509             :  * Make catalog entries showing relationId as being an inheritance child
    3510             :  * of parentOid.  inhRelation is the already-opened pg_inherits catalog.
    3511             :  */
    3512             : static void
    3513       12428 : StoreCatalogInheritance1(Oid relationId, Oid parentOid,
    3514             :                          int32 seqNumber, Relation inhRelation,
    3515             :                          bool child_is_partition)
    3516             : {
    3517             :     ObjectAddress childobject,
    3518             :                 parentobject;
    3519             : 
    3520             :     /* store the pg_inherits row */
    3521       12428 :     StoreSingleInheritance(relationId, parentOid, seqNumber);
    3522             : 
    3523             :     /*
    3524             :      * Store a dependency too
    3525             :      */
    3526       12428 :     parentobject.classId = RelationRelationId;
    3527       12428 :     parentobject.objectId = parentOid;
    3528       12428 :     parentobject.objectSubId = 0;
    3529       12428 :     childobject.classId = RelationRelationId;
    3530       12428 :     childobject.objectId = relationId;
    3531       12428 :     childobject.objectSubId = 0;
    3532             : 
    3533       12428 :     recordDependencyOn(&childobject, &parentobject,
    3534             :                        child_dependency_type(child_is_partition));
    3535             : 
    3536             :     /*
    3537             :      * Post creation hook of this inheritance. Since object_access_hook
    3538             :      * doesn't take multiple object identifiers, we relay oid of parent
    3539             :      * relation using auxiliary_id argument.
    3540             :      */
    3541       12428 :     InvokeObjectPostAlterHookArg(InheritsRelationId,
    3542             :                                  relationId, 0,
    3543             :                                  parentOid, false);
    3544             : 
    3545             :     /*
    3546             :      * Mark the parent as having subclasses.
    3547             :      */
    3548       12428 :     SetRelationHasSubclass(parentOid, true);
    3549       12428 : }
    3550             : 
    3551             : /*
    3552             :  * Look for an existing column entry with the given name.
    3553             :  *
    3554             :  * Returns the index (starting with 1) if attribute already exists in columns,
    3555             :  * 0 if it doesn't.
    3556             :  */
    3557             : static int
    3558       22496 : findAttrByName(const char *attributeName, const List *columns)
    3559             : {
    3560             :     ListCell   *lc;
    3561       22496 :     int         i = 1;
    3562             : 
    3563       42008 :     foreach(lc, columns)
    3564             :     {
    3565       20088 :         if (strcmp(attributeName, lfirst_node(ColumnDef, lc)->colname) == 0)
    3566         576 :             return i;
    3567             : 
    3568       19512 :         i++;
    3569             :     }
    3570       21920 :     return 0;
    3571             : }
    3572             : 
    3573             : 
    3574             : /*
    3575             :  * SetRelationHasSubclass
    3576             :  *      Set the value of the relation's relhassubclass field in pg_class.
    3577             :  *
    3578             :  * NOTE: caller must be holding an appropriate lock on the relation.
    3579             :  * ShareUpdateExclusiveLock is sufficient.
    3580             :  *
    3581             :  * NOTE: an important side-effect of this operation is that an SI invalidation
    3582             :  * message is sent out to all backends --- including me --- causing plans
    3583             :  * referencing the relation to be rebuilt with the new list of children.
    3584             :  * This must happen even if we find that no change is needed in the pg_class
    3585             :  * row.
    3586             :  */
    3587             : void
    3588       15738 : SetRelationHasSubclass(Oid relationId, bool relhassubclass)
    3589             : {
    3590             :     Relation    relationRelation;
    3591             :     HeapTuple   tuple;
    3592             :     Form_pg_class classtuple;
    3593             : 
    3594             :     /*
    3595             :      * Fetch a modifiable copy of the tuple, modify it, update pg_class.
    3596             :      */
    3597       15738 :     relationRelation = table_open(RelationRelationId, RowExclusiveLock);
    3598       15738 :     tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relationId));
    3599       15738 :     if (!HeapTupleIsValid(tuple))
    3600           0 :         elog(ERROR, "cache lookup failed for relation %u", relationId);
    3601       15738 :     classtuple = (Form_pg_class) GETSTRUCT(tuple);
    3602             : 
    3603       15738 :     if (classtuple->relhassubclass != relhassubclass)
    3604             :     {
    3605        7404 :         classtuple->relhassubclass = relhassubclass;
    3606        7404 :         CatalogTupleUpdate(relationRelation, &tuple->t_self, tuple);
    3607             :     }
    3608             :     else
    3609             :     {
    3610             :         /* no need to change tuple, but force relcache rebuild anyway */
    3611        8334 :         CacheInvalidateRelcacheByTuple(tuple);
    3612             :     }
    3613             : 
    3614       15738 :     heap_freetuple(tuple);
    3615       15738 :     table_close(relationRelation, RowExclusiveLock);
    3616       15738 : }
    3617             : 
    3618             : /*
    3619             :  * CheckRelationTableSpaceMove
    3620             :  *      Check if relation can be moved to new tablespace.
    3621             :  *
    3622             :  * NOTE: The caller must hold AccessExclusiveLock on the relation.
    3623             :  *
    3624             :  * Returns true if the relation can be moved to the new tablespace; raises
    3625             :  * an error if it is not possible to do the move; returns false if the move
    3626             :  * would have no effect.
    3627             :  */
    3628             : bool
    3629         226 : CheckRelationTableSpaceMove(Relation rel, Oid newTableSpaceId)
    3630             : {
    3631             :     Oid         oldTableSpaceId;
    3632             : 
    3633             :     /*
    3634             :      * No work if no change in tablespace.  Note that MyDatabaseTableSpace is
    3635             :      * stored as 0.
    3636             :      */
    3637         226 :     oldTableSpaceId = rel->rd_rel->reltablespace;
    3638         226 :     if (newTableSpaceId == oldTableSpaceId ||
    3639         218 :         (newTableSpaceId == MyDatabaseTableSpace && oldTableSpaceId == 0))
    3640          10 :         return false;
    3641             : 
    3642             :     /*
    3643             :      * We cannot support moving mapped relations into different tablespaces.
    3644             :      * (In particular this eliminates all shared catalogs.)
    3645             :      */
    3646         216 :     if (RelationIsMapped(rel))
    3647           0 :         ereport(ERROR,
    3648             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    3649             :                  errmsg("cannot move system relation \"%s\"",
    3650             :                         RelationGetRelationName(rel))));
    3651             : 
    3652             :     /* Cannot move a non-shared relation into pg_global */
    3653         216 :     if (newTableSpaceId == GLOBALTABLESPACE_OID)
    3654          12 :         ereport(ERROR,
    3655             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    3656             :                  errmsg("only shared relations can be placed in pg_global tablespace")));
    3657             : 
    3658             :     /*
    3659             :      * Do not allow moving temp tables of other backends ... their local
    3660             :      * buffer manager is not going to cope.
    3661             :      */
    3662         204 :     if (RELATION_IS_OTHER_TEMP(rel))
    3663           0 :         ereport(ERROR,
    3664             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    3665             :                  errmsg("cannot move temporary tables of other sessions")));
    3666             : 
    3667         204 :     return true;
    3668             : }
    3669             : 
    3670             : /*
    3671             :  * SetRelationTableSpace
    3672             :  *      Set new reltablespace and relfilenumber in pg_class entry.
    3673             :  *
    3674             :  * newTableSpaceId is the new tablespace for the relation, and
    3675             :  * newRelFilenumber its new filenumber.  If newRelFilenumber is
    3676             :  * InvalidRelFileNumber, this field is not updated.
    3677             :  *
    3678             :  * NOTE: The caller must hold AccessExclusiveLock on the relation.
    3679             :  *
    3680             :  * The caller of this routine had better check if a relation can be
    3681             :  * moved to this new tablespace by calling CheckRelationTableSpaceMove()
    3682             :  * first, and is responsible for making the change visible with
    3683             :  * CommandCounterIncrement().
    3684             :  */
    3685             : void
    3686         204 : SetRelationTableSpace(Relation rel,
    3687             :                       Oid newTableSpaceId,
    3688             :                       RelFileNumber newRelFilenumber)
    3689             : {
    3690             :     Relation    pg_class;
    3691             :     HeapTuple   tuple;
    3692             :     Form_pg_class rd_rel;
    3693         204 :     Oid         reloid = RelationGetRelid(rel);
    3694             : 
    3695             :     Assert(CheckRelationTableSpaceMove(rel, newTableSpaceId));
    3696             : 
    3697             :     /* Get a modifiable copy of the relation's pg_class row. */
    3698         204 :     pg_class = table_open(RelationRelationId, RowExclusiveLock);
    3699             : 
    3700         204 :     tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(reloid));
    3701         204 :     if (!HeapTupleIsValid(tuple))
    3702           0 :         elog(ERROR, "cache lookup failed for relation %u", reloid);
    3703         204 :     rd_rel = (Form_pg_class) GETSTRUCT(tuple);
    3704             : 
    3705             :     /* Update the pg_class row. */
    3706         408 :     rd_rel->reltablespace = (newTableSpaceId == MyDatabaseTableSpace) ?
    3707         204 :         InvalidOid : newTableSpaceId;
    3708         204 :     if (RelFileNumberIsValid(newRelFilenumber))
    3709         160 :         rd_rel->relfilenode = newRelFilenumber;
    3710         204 :     CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
    3711             : 
    3712             :     /*
    3713             :      * Record dependency on tablespace.  This is only required for relations
    3714             :      * that have no physical storage.
    3715             :      */
    3716         204 :     if (!RELKIND_HAS_STORAGE(rel->rd_rel->relkind))
    3717          30 :         changeDependencyOnTablespace(RelationRelationId, reloid,
    3718             :                                      rd_rel->reltablespace);
    3719             : 
    3720         204 :     heap_freetuple(tuple);
    3721         204 :     table_close(pg_class, RowExclusiveLock);
    3722         204 : }
    3723             : 
    3724             : /*
    3725             :  *      renameatt_check         - basic sanity checks before attribute rename
    3726             :  */
    3727             : static void
    3728         972 : renameatt_check(Oid myrelid, Form_pg_class classform, bool recursing)
    3729             : {
    3730         972 :     char        relkind = classform->relkind;
    3731             : 
    3732         972 :     if (classform->reloftype && !recursing)
    3733           6 :         ereport(ERROR,
    3734             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    3735             :                  errmsg("cannot rename column of typed table")));
    3736             : 
    3737             :     /*
    3738             :      * Renaming the columns of sequences or toast tables doesn't actually
    3739             :      * break anything from the system's point of view, since internal
    3740             :      * references are by attnum.  But it doesn't seem right to allow users to
    3741             :      * change names that are hardcoded into the system, hence the following
    3742             :      * restriction.
    3743             :      */
    3744         966 :     if (relkind != RELKIND_RELATION &&
    3745          86 :         relkind != RELKIND_VIEW &&
    3746          86 :         relkind != RELKIND_MATVIEW &&
    3747          38 :         relkind != RELKIND_COMPOSITE_TYPE &&
    3748          38 :         relkind != RELKIND_INDEX &&
    3749          38 :         relkind != RELKIND_PARTITIONED_INDEX &&
    3750           0 :         relkind != RELKIND_FOREIGN_TABLE &&
    3751             :         relkind != RELKIND_PARTITIONED_TABLE)
    3752           0 :         ereport(ERROR,
    3753             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    3754             :                  errmsg("cannot rename columns of relation \"%s\"",
    3755             :                         NameStr(classform->relname)),
    3756             :                  errdetail_relkind_not_supported(relkind)));
    3757             : 
    3758             :     /*
    3759             :      * permissions checking.  only the owner of a class can change its schema.
    3760             :      */
    3761         966 :     if (!object_ownercheck(RelationRelationId, myrelid, GetUserId()))
    3762           0 :         aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(myrelid)),
    3763           0 :                        NameStr(classform->relname));
    3764         966 :     if (!allowSystemTableMods && IsSystemClass(myrelid, classform))
    3765           2 :         ereport(ERROR,
    3766             :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
    3767             :                  errmsg("permission denied: \"%s\" is a system catalog",
    3768             :                         NameStr(classform->relname))));
    3769         964 : }
    3770             : 
    3771             : /*
    3772             :  *      renameatt_internal      - workhorse for renameatt
    3773             :  *
    3774             :  * Return value is the attribute number in the 'myrelid' relation.
    3775             :  */
    3776             : static AttrNumber
    3777         540 : renameatt_internal(Oid myrelid,
    3778             :                    const char *oldattname,
    3779             :                    const char *newattname,
    3780             :                    bool recurse,
    3781             :                    bool recursing,
    3782             :                    int expected_parents,
    3783             :                    DropBehavior behavior)
    3784             : {
    3785             :     Relation    targetrelation;
    3786             :     Relation    attrelation;
    3787             :     HeapTuple   atttup;
    3788             :     Form_pg_attribute attform;
    3789             :     AttrNumber  attnum;
    3790             : 
    3791             :     /*
    3792             :      * Grab an exclusive lock on the target table, which we will NOT release
    3793             :      * until end of transaction.
    3794             :      */
    3795         540 :     targetrelation = relation_open(myrelid, AccessExclusiveLock);
    3796         540 :     renameatt_check(myrelid, RelationGetForm(targetrelation), recursing);
    3797             : 
    3798             :     /*
    3799             :      * if the 'recurse' flag is set then we are supposed to rename this
    3800             :      * attribute in all classes that inherit from 'relname' (as well as in
    3801             :      * 'relname').
    3802             :      *
    3803             :      * any permissions or problems with duplicate attributes will cause the
    3804             :      * whole transaction to abort, which is what we want -- all or nothing.
    3805             :      */
    3806         540 :     if (recurse)
    3807             :     {
    3808             :         List       *child_oids,
    3809             :                    *child_numparents;
    3810             :         ListCell   *lo,
    3811             :                    *li;
    3812             : 
    3813             :         /*
    3814             :          * we need the number of parents for each child so that the recursive
    3815             :          * calls to renameatt() can determine whether there are any parents
    3816             :          * outside the inheritance hierarchy being processed.
    3817             :          */
    3818         236 :         child_oids = find_all_inheritors(myrelid, AccessExclusiveLock,
    3819             :                                          &child_numparents);
    3820             : 
    3821             :         /*
    3822             :          * find_all_inheritors does the recursive search of the inheritance
    3823             :          * hierarchy, so all we have to do is process all of the relids in the
    3824             :          * list that it returns.
    3825             :          */
    3826         710 :         forboth(lo, child_oids, li, child_numparents)
    3827             :         {
    3828         504 :             Oid         childrelid = lfirst_oid(lo);
    3829         504 :             int         numparents = lfirst_int(li);
    3830             : 
    3831         504 :             if (childrelid == myrelid)
    3832         236 :                 continue;
    3833             :             /* note we need not recurse again */
    3834         268 :             renameatt_internal(childrelid, oldattname, newattname, false, true, numparents, behavior);
    3835             :         }
    3836             :     }
    3837             :     else
    3838             :     {
    3839             :         /*
    3840             :          * If we are told not to recurse, there had better not be any child
    3841             :          * tables; else the rename would put them out of step.
    3842             :          *
    3843             :          * expected_parents will only be 0 if we are not already recursing.
    3844             :          */
    3845         340 :         if (expected_parents == 0 &&
    3846          36 :             find_inheritance_children(myrelid, NoLock) != NIL)
    3847          12 :             ereport(ERROR,
    3848             :                     (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    3849             :                      errmsg("inherited column \"%s\" must be renamed in child tables too",
    3850             :                             oldattname)));
    3851             :     }
    3852             : 
    3853             :     /* rename attributes in typed tables of composite type */
    3854         498 :     if (targetrelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
    3855             :     {
    3856             :         List       *child_oids;
    3857             :         ListCell   *lo;
    3858             : 
    3859          24 :         child_oids = find_typed_table_dependencies(targetrelation->rd_rel->reltype,
    3860          24 :                                                    RelationGetRelationName(targetrelation),
    3861             :                                                    behavior);
    3862             : 
    3863          24 :         foreach(lo, child_oids)
    3864           6 :             renameatt_internal(lfirst_oid(lo), oldattname, newattname, true, true, 0, behavior);
    3865             :     }
    3866             : 
    3867         492 :     attrelation = table_open(AttributeRelationId, RowExclusiveLock);
    3868             : 
    3869         492 :     atttup = SearchSysCacheCopyAttName(myrelid, oldattname);
    3870         492 :     if (!HeapTupleIsValid(atttup))
    3871          24 :         ereport(ERROR,
    3872             :                 (errcode(ERRCODE_UNDEFINED_COLUMN),
    3873             :                  errmsg("column \"%s\" does not exist",
    3874             :                         oldattname)));
    3875         468 :     attform = (Form_pg_attribute) GETSTRUCT(atttup);
    3876             : 
    3877         468 :     attnum = attform->attnum;
    3878         468 :     if (attnum <= 0)
    3879           0 :         ereport(ERROR,
    3880             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    3881             :                  errmsg("cannot rename system column \"%s\"",
    3882             :                         oldattname)));
    3883             : 
    3884             :     /*
    3885             :      * if the attribute is inherited, forbid the renaming.  if this is a
    3886             :      * top-level call to renameatt(), then expected_parents will be 0, so the
    3887             :      * effect of this code will be to prohibit the renaming if the attribute
    3888             :      * is inherited at all.  if this is a recursive call to renameatt(),
    3889             :      * expected_parents will be the number of parents the current relation has
    3890             :      * within the inheritance hierarchy being processed, so we'll prohibit the
    3891             :      * renaming only if there are additional parents from elsewhere.
    3892             :      */
    3893         468 :     if (attform->attinhcount > expected_parents)
    3894          30 :         ereport(ERROR,
    3895             :                 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    3896             :                  errmsg("cannot rename inherited column \"%s\"",
    3897             :                         oldattname)));
    3898             : 
    3899             :     /* new name should not already exist */
    3900         438 :     (void) check_for_column_name_collision(targetrelation, newattname, false);
    3901             : 
    3902             :     /* apply the update */
    3903         426 :     namestrcpy(&(attform->attname), newattname);
    3904             : 
    3905         426 :     CatalogTupleUpdate(attrelation, &atttup->t_self, atttup);
    3906             : 
    3907         426 :     InvokeObjectPostAlterHook(RelationRelationId, myrelid, attnum);
    3908             : 
    3909         426 :     heap_freetuple(atttup);
    3910             : 
    3911         426 :     table_close(attrelation, RowExclusiveLock);
    3912             : 
    3913         426 :     relation_close(targetrelation, NoLock); /* close rel but keep lock */
    3914             : 
    3915         426 :     return attnum;
    3916             : }
    3917             : 
    3918             : /*
    3919             :  * Perform permissions and integrity checks before acquiring a relation lock.
    3920             :  */
    3921             : static void
    3922         394 : RangeVarCallbackForRenameAttribute(const RangeVar *rv, Oid relid, Oid oldrelid,
    3923             :                                    void *arg)
    3924             : {
    3925             :     HeapTuple   tuple;
    3926             :     Form_pg_class form;
    3927             : 
    3928         394 :     tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
    3929         394 :     if (!HeapTupleIsValid(tuple))
    3930          40 :         return;                 /* concurrently dropped */
    3931         354 :     form = (Form_pg_class) GETSTRUCT(tuple);
    3932         354 :     renameatt_check(relid, form, false);
    3933         346 :     ReleaseSysCache(tuple);
    3934             : }
    3935             : 
    3936             : /*
    3937             :  *      renameatt       - changes the name of an attribute in a relation
    3938             :  *
    3939             :  * The returned ObjectAddress is that of the renamed column.
    3940             :  */
    3941             : ObjectAddress
    3942         304 : renameatt(RenameStmt *stmt)
    3943             : {
    3944             :     Oid         relid;
    3945             :     AttrNumber  attnum;
    3946             :     ObjectAddress address;
    3947             : 
    3948             :     /* lock level taken here should match renameatt_internal */
    3949         304 :     relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
    3950         304 :                                      stmt->missing_ok ? RVR_MISSING_OK : 0,
    3951             :                                      RangeVarCallbackForRenameAttribute,
    3952             :                                      NULL);
    3953             : 
    3954         290 :     if (!OidIsValid(relid))
    3955             :     {
    3956          24 :         ereport(NOTICE,
    3957             :                 (errmsg("relation \"%s\" does not exist, skipping",
    3958             :                         stmt->relation->relname)));
    3959          24 :         return InvalidObjectAddress;
    3960             :     }
    3961             : 
    3962             :     attnum =
    3963         266 :         renameatt_internal(relid,
    3964         266 :                            stmt->subname,    /* old att name */
    3965         266 :                            stmt->newname,    /* new att name */
    3966         266 :                            stmt->relation->inh, /* recursive? */
    3967             :                            false,   /* recursing? */
    3968             :                            0,   /* expected inhcount */
    3969             :                            stmt->behavior);
    3970             : 
    3971         182 :     ObjectAddressSubSet(address, RelationRelationId, relid, attnum);
    3972             : 
    3973         182 :     return address;
    3974             : }
    3975             : 
    3976             : /*
    3977             :  * same logic as renameatt_internal
    3978             :  */
    3979             : static ObjectAddress
    3980          84 : rename_constraint_internal(Oid myrelid,
    3981             :                            Oid mytypid,
    3982             :                            const char *oldconname,
    3983             :                            const char *newconname,
    3984             :                            bool recurse,
    3985             :                            bool recursing,
    3986             :                            int expected_parents)
    3987             : {
    3988          84 :     Relation    targetrelation = NULL;
    3989             :     Oid         constraintOid;
    3990             :     HeapTuple   tuple;
    3991             :     Form_pg_constraint con;
    3992             :     ObjectAddress address;
    3993             : 
    3994             :     Assert(!myrelid || !mytypid);
    3995             : 
    3996          84 :     if (mytypid)
    3997             :     {
    3998           6 :         constraintOid = get_domain_constraint_oid(mytypid, oldconname, false);
    3999             :     }
    4000             :     else
    4001             :     {
    4002          78 :         targetrelation = relation_open(myrelid, AccessExclusiveLock);
    4003             : 
    4004             :         /*
    4005             :          * don't tell it whether we're recursing; we allow changing typed
    4006             :          * tables here
    4007             :          */
    4008          78 :         renameatt_check(myrelid, RelationGetForm(targetrelation), false);
    4009             : 
    4010          78 :         constraintOid = get_relation_constraint_oid(myrelid, oldconname, false);
    4011             :     }
    4012             : 
    4013          84 :     tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constraintOid));
    4014          84 :     if (!HeapTupleIsValid(tuple))
    4015           0 :         elog(ERROR, "cache lookup failed for constraint %u",
    4016             :              constraintOid);
    4017          84 :     con = (Form_pg_constraint) GETSTRUCT(tuple);
    4018             : 
    4019          84 :     if (myrelid &&
    4020          78 :         (con->contype == CONSTRAINT_CHECK ||
    4021          18 :          con->contype == CONSTRAINT_NOTNULL) &&
    4022          60 :         !con->connoinherit)
    4023             :     {
    4024          48 :         if (recurse)
    4025             :         {
    4026             :             List       *child_oids,
    4027             :                        *child_numparents;
    4028             :             ListCell   *lo,
    4029             :                        *li;
    4030             : 
    4031          30 :             child_oids = find_all_inheritors(myrelid, AccessExclusiveLock,
    4032             :                                              &child_numparents);
    4033             : 
    4034          72 :             forboth(lo, child_oids, li, child_numparents)
    4035             :             {
    4036          42 :                 Oid         childrelid = lfirst_oid(lo);
    4037          42 :                 int         numparents = lfirst_int(li);
    4038             : 
    4039          42 :                 if (childrelid == myrelid)
    4040          30 :                     continue;
    4041             : 
    4042          12 :                 rename_constraint_internal(childrelid, InvalidOid, oldconname, newconname, false, true, numparents);
    4043             :             }
    4044             :         }
    4045             :         else
    4046             :         {
    4047          24 :             if (expected_parents == 0 &&
    4048           6 :                 find_inheritance_children(myrelid, NoLock) != NIL)
    4049           6 :                 ereport(ERROR,
    4050             :                         (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    4051             :                          errmsg("inherited constraint \"%s\" must be renamed in child tables too",
    4052             :                                 oldconname)));
    4053             :         }
    4054             : 
    4055          42 :         if (con->coninhcount > expected_parents)
    4056           6 :             ereport(ERROR,
    4057             :                     (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    4058             :                      errmsg("cannot rename inherited constraint \"%s\"",
    4059             :                             oldconname)));
    4060             :     }
    4061             : 
    4062          72 :     if (con->conindid
    4063          18 :         && (con->contype == CONSTRAINT_PRIMARY
    4064           6 :             || con->contype == CONSTRAINT_UNIQUE
    4065           0 :             || con->contype == CONSTRAINT_EXCLUSION))
    4066             :         /* rename the index; this renames the constraint as well */
    4067          18 :         RenameRelationInternal(con->conindid, newconname, false, true);
    4068             :     else
    4069          54 :         RenameConstraintById(constraintOid, newconname);
    4070             : 
    4071          72 :     ObjectAddressSet(address, ConstraintRelationId, constraintOid);
    4072             : 
    4073          72 :     ReleaseSysCache(tuple);
    4074             : 
    4075          72 :     if (targetrelation)
    4076             :     {
    4077             :         /*
    4078             :          * Invalidate relcache so as others can see the new constraint name.
    4079             :          */
    4080          66 :         CacheInvalidateRelcache(targetrelation);
    4081             : 
    4082          66 :         relation_close(targetrelation, NoLock); /* close rel but keep lock */
    4083             :     }
    4084             : 
    4085          72 :     return address;
    4086             : }
    4087             : 
    4088             : ObjectAddress
    4089          78 : RenameConstraint(RenameStmt *stmt)
    4090             : {
    4091          78 :     Oid         relid = InvalidOid;
    4092          78 :     Oid         typid = InvalidOid;
    4093             : 
    4094          78 :     if (stmt->renameType == OBJECT_DOMCONSTRAINT)
    4095             :     {
    4096             :         Relation    rel;
    4097             :         HeapTuple   tup;
    4098             : 
    4099           6 :         typid = typenameTypeId(NULL, makeTypeNameFromNameList(castNode(List, stmt->object)));
    4100           6 :         rel = table_open(TypeRelationId, RowExclusiveLock);
    4101           6 :         tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
    4102           6 :         if (!HeapTupleIsValid(tup))
    4103           0 :             elog(ERROR, "cache lookup failed for type %u", typid);
    4104           6 :         checkDomainOwner(tup);
    4105           6 :         ReleaseSysCache(tup);
    4106           6 :         table_close(rel, NoLock);
    4107             :     }
    4108             :     else
    4109             :     {
    4110             :         /* lock level taken here should match rename_constraint_internal */
    4111          72 :         relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
    4112          72 :                                          stmt->missing_ok ? RVR_MISSING_OK : 0,
    4113             :                                          RangeVarCallbackForRenameAttribute,
    4114             :                                          NULL);
    4115          72 :         if (!OidIsValid(relid))
    4116             :         {
    4117           6 :             ereport(NOTICE,
    4118             :                     (errmsg("relation \"%s\" does not exist, skipping",
    4119             :                             stmt->relation->relname)));
    4120           6 :             return InvalidObjectAddress;
    4121             :         }
    4122             :     }
    4123             : 
    4124             :     return
    4125          72 :         rename_constraint_internal(relid, typid,
    4126          72 :                                    stmt->subname,
    4127          72 :                                    stmt->newname,
    4128         138 :                                    (stmt->relation &&
    4129          66 :                                     stmt->relation->inh), /* recursive? */
    4130             :                                    false,   /* recursing? */
    4131             :                                    0 /* expected inhcount */ );
    4132             : }
    4133             : 
    4134             : /*
    4135             :  * Execute ALTER TABLE/INDEX/SEQUENCE/VIEW/MATERIALIZED VIEW/FOREIGN TABLE
    4136             :  * RENAME
    4137             :  */
    4138             : ObjectAddress
    4139         510 : RenameRelation(RenameStmt *stmt)
    4140             : {
    4141         510 :     bool        is_index_stmt = stmt->renameType == OBJECT_INDEX;
    4142             :     Oid         relid;
    4143             :     ObjectAddress address;
    4144             : 
    4145             :     /*
    4146             :      * Grab an exclusive lock on the target table, index, sequence, view,
    4147             :      * materialized view, or foreign table, which we will NOT release until
    4148             :      * end of transaction.
    4149             :      *
    4150             :      * Lock level used here should match RenameRelationInternal, to avoid lock
    4151             :      * escalation.  However, because ALTER INDEX can be used with any relation
    4152             :      * type, we mustn't believe without verification.
    4153             :      */
    4154             :     for (;;)
    4155          12 :     {
    4156             :         LOCKMODE    lockmode;
    4157             :         char        relkind;
    4158             :         bool        obj_is_index;
    4159             : 
    4160         522 :         lockmode = is_index_stmt ? ShareUpdateExclusiveLock : AccessExclusiveLock;
    4161             : 
    4162         522 :         relid = RangeVarGetRelidExtended(stmt->relation, lockmode,
    4163         522 :                                          stmt->missing_ok ? RVR_MISSING_OK : 0,
    4164             :                                          RangeVarCallbackForAlterRelation,
    4165             :                                          (void *) stmt);
    4166             : 
    4167         472 :         if (!OidIsValid(relid))
    4168             :         {
    4169          18 :             ereport(NOTICE,
    4170             :                     (errmsg("relation \"%s\" does not exist, skipping",
    4171             :                             stmt->relation->relname)));
    4172          18 :             return InvalidObjectAddress;
    4173             :         }
    4174             : 
    4175             :         /*
    4176             :          * We allow mismatched statement and object types (e.g., ALTER INDEX
    4177             :          * to rename a table), but we might've used the wrong lock level.  If
    4178             :          * that happens, retry with the correct lock level.  We don't bother
    4179             :          * if we already acquired AccessExclusiveLock with an index, however.
    4180             :          */
    4181         454 :         relkind = get_rel_relkind(relid);
    4182         454 :         obj_is_index = (relkind == RELKIND_INDEX ||
    4183             :                         relkind == RELKIND_PARTITIONED_INDEX);
    4184         454 :         if (obj_is_index || is_index_stmt == obj_is_index)
    4185             :             break;
    4186             : 
    4187          12 :         UnlockRelationOid(relid, lockmode);
    4188          12 :         is_index_stmt = obj_is_index;
    4189             :     }
    4190             : 
    4191             :     /* Do the work */
    4192         442 :     RenameRelationInternal(relid, stmt->newname, false, is_index_stmt);
    4193             : 
    4194         430 :     ObjectAddressSet(address, RelationRelationId, relid);
    4195             : 
    4196         430 :     return address;
    4197             : }
    4198             : 
    4199             : /*
    4200             :  *      RenameRelationInternal - change the name of a relation
    4201             :  */
    4202             : void
    4203        1330 : RenameRelationInternal(Oid myrelid, const char *newrelname, bool is_internal, bool is_index)
    4204             : {
    4205             :     Relation    targetrelation;
    4206             :     Relation    relrelation;    /* for RELATION relation */
    4207             :     HeapTuple   reltup;
    4208             :     Form_pg_class relform;
    4209             :     Oid         namespaceId;
    4210             : 
    4211             :     /*
    4212             :      * Grab a lock on the target relation, which we will NOT release until end
    4213             :      * of transaction.  We need at least a self-exclusive lock so that
    4214             :      * concurrent DDL doesn't overwrite the rename if they start updating
    4215             :      * while still seeing the old version.  The lock also guards against
    4216             :      * triggering relcache reloads in concurrent sessions, which might not
    4217             :      * handle this information changing under them.  For indexes, we can use a
    4218             :      * reduced lock level because RelationReloadIndexInfo() handles indexes
    4219             :      * specially.
    4220             :      */
    4221        1330 :     targetrelation = relation_open(myrelid, is_index ? ShareUpdateExclusiveLock : AccessExclusiveLock);
    4222        1330 :     namespaceId = RelationGetNamespace(targetrelation);
    4223             : 
    4224             :     /*
    4225             :      * Find relation's pg_class tuple, and make sure newrelname isn't in use.
    4226             :      */
    4227        1330 :     relrelation = table_open(RelationRelationId, RowExclusiveLock);
    4228             : 
    4229        1330 :     reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(myrelid));
    4230        1330 :     if (!HeapTupleIsValid(reltup))  /* shouldn't happen */
    4231           0 :         elog(ERROR, "cache lookup failed for relation %u", myrelid);
    4232        1330 :     relform = (Form_pg_class) GETSTRUCT(reltup);
    4233             : 
    4234        1330 :     if (get_relname_relid(newrelname, namespaceId) != InvalidOid)
    4235          12 :         ereport(ERROR,
    4236             :                 (errcode(ERRCODE_DUPLICATE_TABLE),
    4237             :                  errmsg("relation \"%s\" already exists",
    4238             :                         newrelname)));
    4239             : 
    4240             :     /*
    4241             :      * RenameRelation is careful not to believe the caller's idea of the
    4242             :      * relation kind being handled.  We don't have to worry about this, but
    4243             :      * let's not be totally oblivious to it.  We can process an index as
    4244             :      * not-an-index, but not the other way around.
    4245             :      */
    4246             :     Assert(!is_index ||
    4247             :            is_index == (targetrelation->rd_rel->relkind == RELKIND_INDEX ||
    4248             :                         targetrelation->rd_rel->relkind == RELKIND_PARTITIONED_INDEX));
    4249             : 
    4250             :     /*
    4251             :      * Update pg_class tuple with new relname.  (Scribbling on reltup is OK
    4252             :      * because it's a copy...)
    4253             :      */
    4254        1318 :     namestrcpy(&(relform->relname), newrelname);
    4255             : 
    4256        1318 :     CatalogTupleUpdate(relrelation, &reltup->t_self, reltup);
    4257             : 
    4258        1318 :     InvokeObjectPostAlterHookArg(RelationRelationId, myrelid, 0,
    4259             :                                  InvalidOid, is_internal);
    4260             : 
    4261        1318 :     heap_freetuple(reltup);
    4262        1318 :     table_close(relrelation, RowExclusiveLock);
    4263             : 
    4264             :     /*
    4265             :      * Also rename the associated type, if any.
    4266             :      */
    4267        1318 :     if (OidIsValid(targetrelation->rd_rel->reltype))
    4268         154 :         RenameTypeInternal(targetrelation->rd_rel->reltype,
    4269             :                            newrelname, namespaceId);
    4270             : 
    4271             :     /*
    4272             :      * Also rename the associated constraint, if any.
    4273             :      */
    4274        1318 :     if (targetrelation->rd_rel->relkind == RELKIND_INDEX ||
    4275         718 :         targetrelation->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
    4276             :     {
    4277         618 :         Oid         constraintId = get_index_constraint(myrelid);
    4278             : 
    4279         618 :         if (OidIsValid(constraintId))
    4280          36 :             RenameConstraintById(constraintId, newrelname);
    4281             :     }
    4282             : 
    4283             :     /*
    4284             :      * Close rel, but keep lock!
    4285             :      */
    4286        1318 :     relation_close(targetrelation, NoLock);
    4287        1318 : }
    4288             : 
    4289             : /*
    4290             :  *      ResetRelRewrite - reset relrewrite
    4291             :  */
    4292             : void
    4293         412 : ResetRelRewrite(Oid myrelid)
    4294             : {
    4295             :     Relation    relrelation;    /* for RELATION relation */
    4296             :     HeapTuple   reltup;
    4297             :     Form_pg_class relform;
    4298             : 
    4299             :     /*
    4300             :      * Find relation's pg_class tuple.
    4301             :      */
    4302         412 :     relrelation = table_open(RelationRelationId, RowExclusiveLock);
    4303             : 
    4304         412 :     reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(myrelid));
    4305         412 :     if (!HeapTupleIsValid(reltup))  /* shouldn't happen */
    4306           0 :         elog(ERROR, "cache lookup failed for relation %u", myrelid);
    4307         412 :     relform = (Form_pg_class) GETSTRUCT(reltup);
    4308             : 
    4309             :     /*
    4310             :      * Update pg_class tuple.
    4311             :      */
    4312         412 :     relform->relrewrite = InvalidOid;
    4313             : 
    4314         412 :     CatalogTupleUpdate(relrelation, &reltup->t_self, reltup);
    4315             : 
    4316         412 :     heap_freetuple(reltup);
    4317         412 :     table_close(relrelation, RowExclusiveLock);
    4318         412 : }
    4319             : 
    4320             : /*
    4321             :  * Disallow ALTER TABLE (and similar commands) when the current backend has
    4322             :  * any open reference to the target table besides the one just acquired by
    4323             :  * the calling command; this implies there's an open cursor or active plan.
    4324             :  * We need this check because our lock doesn't protect us against stomping
    4325             :  * on our own foot, only other people's feet!
    4326             :  *
    4327             :  * For ALTER TABLE, the only case known to cause serious trouble is ALTER
    4328             :  * COLUMN TYPE, and some changes are obviously pretty benign, so this could
    4329             :  * possibly be relaxed to only error out for certain types of alterations.
    4330             :  * But the use-case for allowing any of these things is not obvious, so we
    4331             :  * won't work hard at it for now.
    4332             :  *
    4333             :  * We also reject these commands if there are any pending AFTER trigger events
    4334             :  * for the rel.  This is certainly necessary for the rewriting variants of
    4335             :  * ALTER TABLE, because they don't preserve tuple TIDs and so the pending
    4336             :  * events would try to fetch the wrong tuples.  It might be overly cautious
    4337             :  * in other cases, but again it seems better to err on the side of paranoia.
    4338             :  *
    4339             :  * REINDEX calls this with "rel" referencing the index to be rebuilt; here
    4340             :  * we are worried about active indexscans on the index.  The trigger-event
    4341             :  * check can be skipped, since we are doing no damage to the parent table.
    4342             :  *
    4343             :  * The statement name (eg, "ALTER TABLE") is passed for use in error messages.
    4344             :  */
    4345             : void
    4346      137088 : CheckTableNotInUse(Relation rel, const char *stmt)
    4347             : {
    4348             :     int         expected_refcnt;
    4349             : 
    4350      137088 :     expected_refcnt = rel->rd_isnailed ? 2 : 1;
    4351      137088 :     if (rel->rd_refcnt != expected_refcnt)
    4352          24 :         ereport(ERROR,
    4353             :                 (errcode(ERRCODE_OBJECT_IN_USE),
    4354             :         /* translator: first %s is a SQL command, eg ALTER TABLE */
    4355             :                  errmsg("cannot %s \"%s\" because it is being used by active queries in this session",
    4356             :                         stmt, RelationGetRelationName(rel))));
    4357             : 
    4358      137064 :     if (rel->rd_rel->relkind != RELKIND_INDEX &&
    4359      214940 :         rel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX &&
    4360      106436 :         AfterTriggerPendingOnRel(RelationGetRelid(rel)))
    4361          18 :         ereport(ERROR,
    4362             :                 (errcode(ERRCODE_OBJECT_IN_USE),
    4363             :         /* translator: first %s is a SQL command, eg ALTER TABLE */
    4364             :                  errmsg("cannot %s \"%s\" because it has pending trigger events",
    4365             :                         stmt, RelationGetRelationName(rel))));
    4366      137046 : }
    4367             : 
    4368             : /*
    4369             :  * AlterTableLookupRelation
    4370             :  *      Look up, and lock, the OID for the relation named by an alter table
    4371             :  *      statement.
    4372             :  */
    4373             : Oid
    4374       34618 : AlterTableLookupRelation(AlterTableStmt *stmt, LOCKMODE lockmode)
    4375             : {
    4376       69152 :     return RangeVarGetRelidExtended(stmt->relation, lockmode,
    4377       34618 :                                     stmt->missing_ok ? RVR_MISSING_OK : 0,
    4378             :                                     RangeVarCallbackForAlterRelation,
    4379             :                                     (void *) stmt);
    4380             : }
    4381             : 
    4382             : /*
    4383             :  * AlterTable
    4384             :  *      Execute ALTER TABLE, which can be a list of subcommands
    4385             :  *
    4386             :  * ALTER TABLE is performed in three phases:
    4387             :  *      1. Examine subcommands and perform pre-transformation checking.
    4388             :  *      2. Validate and transform subcommands, and update system catalogs.
    4389             :  *      3. Scan table(s) to check new constraints, and optionally recopy
    4390             :  *         the data into new table(s).
    4391             :  * Phase 3 is not performed unless one or more of the subcommands requires
    4392             :  * it.  The intention of this design is to allow multiple independent
    4393             :  * updates of the table schema to be performed with only one pass over the
    4394             :  * data.
    4395             :  *
    4396             :  * ATPrepCmd performs phase 1.  A "work queue" entry is created for
    4397             :  * each table to be affected (there may be multiple affected tables if the
    4398             :  * commands traverse a table inheritance hierarchy).  Also we do preliminary
    4399             :  * validation of the subcommands.  Because earlier subcommands may change
    4400             :  * the catalog state seen by later commands, there are limits to what can
    4401             :  * be done in this phase.  Generally, this phase acquires table locks,
    4402             :  * checks permissions and relkind, and recurses to find child tables.
    4403             :  *
    4404             :  * ATRewriteCatalogs performs phase 2 for each affected table.
    4405             :  * Certain subcommands need to be performed before others to avoid
    4406             :  * unnecessary conflicts; for example, DROP COLUMN should come before
    4407             :  * ADD COLUMN.  Therefore phase 1 divides the subcommands into multiple
    4408             :  * lists, one for each logical "pass" of phase 2.
    4409             :  *
    4410             :  * ATRewriteTables performs phase 3 for those tables that need it.
    4411             :  *
    4412             :  * For most subcommand types, phases 2 and 3 do no explicit recursion,
    4413             :  * since phase 1 already does it.  However, for certain subcommand types
    4414             :  * it is only possible to determine how to recurse at phase 2 time; for
    4415             :  * those cases, phase 1 sets the cmd->recurse flag.
    4416             :  *
    4417             :  * Thanks to the magic of MVCC, an error anywhere along the way rolls back
    4418             :  * the whole operation; we don't have to do anything special to clean up.
    4419             :  *
    4420             :  * The caller must lock the relation, with an appropriate lock level
    4421             :  * for the subcommands requested, using AlterTableGetLockLevel(stmt->cmds)
    4422             :  * or higher. We pass the lock level down
    4423             :  * so that we can apply it recursively to inherited tables. Note that the
    4424             :  * lock level we want as we recurse might well be higher than required for
    4425             :  * that specific subcommand. So we pass down the overall lock requirement,
    4426             :  * rather than reassess it at lower levels.
    4427             :  *
    4428             :  * The caller also provides a "context" which is to be passed back to
    4429             :  * utility.c when we need to execute a subcommand such as CREATE INDEX.
    4430             :  * Some of the fields therein, such as the relid, are used here as well.
    4431             :  */
    4432             : void
    4433       34396 : AlterTable(AlterTableStmt *stmt, LOCKMODE lockmode,
    4434             :            AlterTableUtilityContext *context)
    4435             : {
    4436             :     Relation    rel;
    4437             : 
    4438             :     /* Caller is required to provide an adequate lock. */
    4439       34396 :     rel = relation_open(context->relid, NoLock);
    4440             : 
    4441       34396 :     CheckTableNotInUse(rel, "ALTER TABLE");
    4442             : 
    4443       34378 :     ATController(stmt, rel, stmt->cmds, stmt->relation->inh, lockmode, context);
    4444       31134 : }
    4445             : 
    4446             : /*
    4447             :  * AlterTableInternal
    4448             :  *
    4449             :  * ALTER TABLE with target specified by OID
    4450             :  *
    4451             :  * We do not reject if the relation is already open, because it's quite
    4452             :  * likely that one or more layers of caller have it open.  That means it
    4453             :  * is unsafe to use this entry point for alterations that could break
    4454             :  * existing query plans.  On the assumption it's not used for such, we
    4455             :  * don't have to reject pending AFTER triggers, either.
    4456             :  *
    4457             :  * Also, since we don't have an AlterTableUtilityContext, this cannot be
    4458             :  * used for any subcommand types that require parse transformation or
    4459             :  * could generate subcommands that have to be passed to ProcessUtility.
    4460             :  */
    4461             : void
    4462         278 : AlterTableInternal(Oid relid, List *cmds, bool recurse)
    4463             : {
    4464             :     Relation    rel;
    4465         278 :     LOCKMODE    lockmode = AlterTableGetLockLevel(cmds);
    4466             : 
    4467         278 :     rel = relation_open(relid, lockmode);
    4468             : 
    4469         278 :     EventTriggerAlterTableRelid(relid);
    4470             : 
    4471         278 :     ATController(NULL, rel, cmds, recurse, lockmode, NULL);
    4472         278 : }
    4473             : 
    4474             : /*
    4475             :  * AlterTableGetLockLevel
    4476             :  *
    4477             :  * Sets the overall lock level required for the supplied list of subcommands.
    4478             :  * Policy for doing this set according to needs of AlterTable(), see
    4479             :  * comments there for overall explanation.
    4480             :  *
    4481             :  * Function is called before and after parsing, so it must give same
    4482             :  * answer each time it is called. Some subcommands are transformed
    4483             :  * into other subcommand types, so the transform must never be made to a
    4484             :  * lower lock level than previously assigned. All transforms are noted below.
    4485             :  *
    4486             :  * Since this is called before we lock the table we cannot use table metadata
    4487             :  * to influence the type of lock we acquire.
    4488             :  *
    4489             :  * There should be no lockmodes hardcoded into the subcommand functions. All
    4490             :  * lockmode decisions for ALTER TABLE are made here only. The one exception is
    4491             :  * ALTER TABLE RENAME which is treated as a different statement type T_RenameStmt
    4492             :  * and does not travel through this section of code and cannot be combined with
    4493             :  * any of the subcommands given here.
    4494             :  *
    4495             :  * Note that Hot Standby only knows about AccessExclusiveLocks on the primary
    4496             :  * so any changes that might affect SELECTs running on standbys need to use
    4497             :  * AccessExclusiveLocks even if you think a lesser lock would do, unless you
    4498             :  * have a solution for that also.
    4499             :  *
    4500             :  * Also note that pg_dump uses only an AccessShareLock, meaning that anything
    4501             :  * that takes a lock less than AccessExclusiveLock can change object definitions
    4502             :  * while pg_dump is running. Be careful to check that the appropriate data is
    4503             :  * derived by pg_dump using an MVCC snapshot, rather than syscache lookups,
    4504             :  * otherwise we might end up with an inconsistent dump that can't restore.
    4505             :  */
    4506             : LOCKMODE
    4507       34896 : AlterTableGetLockLevel(List *cmds)
    4508             : {
    4509             :     /*
    4510             :      * This only works if we read catalog tables using MVCC snapshots.
    4511             :      */
    4512             :     ListCell   *lcmd;
    4513       34896 :     LOCKMODE    lockmode = ShareUpdateExclusiveLock;
    4514             : 
    4515       71796 :     foreach(lcmd, cmds)
    4516             :     {
    4517       36900 :         AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);
    4518       36900 :         LOCKMODE    cmd_lockmode = AccessExclusiveLock; /* default for compiler */
    4519             : 
    4520       36900 :         switch (cmd->subtype)
    4521             :         {
    4522             :                 /*
    4523             :                  * These subcommands rewrite the heap, so require full locks.
    4524             :                  */
    4525        3222 :             case AT_AddColumn:  /* may rewrite heap, in some cases and visible
    4526             :                                  * to SELECT */
    4527             :             case AT_SetAccessMethod:    /* must rewrite heap */
    4528             :             case AT_SetTableSpace:  /* must rewrite heap */
    4529             :             case AT_AlterColumnType:    /* must rewrite heap */
    4530        3222 :                 cmd_lockmode = AccessExclusiveLock;
    4531        3222 :                 break;
    4532             : 
    4533             :                 /*
    4534             :                  * These subcommands may require addition of toast tables. If
    4535             :                  * we add a toast table to a table currently being scanned, we
    4536             :                  * might miss data added to the new toast table by concurrent
    4537             :                  * insert transactions.
    4538             :                  */
    4539         212 :             case AT_SetStorage: /* may add toast tables, see
    4540             :                                  * ATRewriteCatalogs() */
    4541         212 :                 cmd_lockmode = AccessExclusiveLock;
    4542         212 :                 break;
    4543             : 
    4544             :                 /*
    4545             :                  * Removing constraints can affect SELECTs that have been
    4546             :                  * optimized assuming the constraint holds true. See also
    4547             :                  * CloneFkReferenced.
    4548             :                  */
    4549        1268 :             case AT_DropConstraint: /* as DROP INDEX */
    4550             :             case AT_DropNotNull:    /* may change some SQL plans */
    4551        1268 :                 cmd_lockmode = AccessExclusiveLock;
    4552        1268 :                 break;
    4553             : 
    4554             :                 /*
    4555             :                  * Subcommands that may be visible to concurrent SELECTs
    4556             :                  */
    4557        1718 :             case AT_DropColumn: /* change visible to SELECT */
    4558             :             case AT_AddColumnToView:    /* CREATE VIEW */
    4559             :             case AT_DropOids:   /* used to equiv to DropColumn */
    4560             :             case AT_EnableAlwaysRule:   /* may change SELECT rules */
    4561             :             case AT_EnableReplicaRule:  /* may change SELECT rules */
    4562             :             case AT_EnableRule: /* may change SELECT rules */
    4563             :             case AT_DisableRule:    /* may change SELECT rules */
    4564        1718 :                 cmd_lockmode = AccessExclusiveLock;
    4565        1718 :                 break;
    4566             : 
    4567             :                 /*
    4568             :                  * Changing owner may remove implicit SELECT privileges
    4569             :                  */
    4570        1820 :             case AT_ChangeOwner:    /* change visible to SELECT */
    4571        1820 :                 cmd_lockmode = AccessExclusiveLock;
    4572        1820 :                 break;
    4573             : 
    4574             :                 /*
    4575             :                  * Changing foreign table options may affect optimization.
    4576             :                  */
    4577         238 :             case AT_GenericOptions:
    4578             :             case AT_AlterColumnGenericOptions:
    4579         238 :                 cmd_lockmode = AccessExclusiveLock;
    4580         238 :                 break;
    4581             : 
    4582             :                 /*
    4583             :                  * These subcommands affect write operations only.
    4584             :                  */
    4585         340 :             case AT_EnableTrig:
    4586             :             case AT_EnableAlwaysTrig:
    4587             :             case AT_EnableReplicaTrig:
    4588             :             case AT_EnableTrigAll:
    4589             :             case AT_EnableTrigUser:
    4590             :             case AT_DisableTrig:
    4591             :             case AT_DisableTrigAll:
    4592             :             case AT_DisableTrigUser:
    4593         340 :                 cmd_lockmode = ShareRowExclusiveLock;
    4594         340 :                 break;
    4595             : 
    4596             :                 /*
    4597             :                  * These subcommands affect write operations only. XXX
    4598             :                  * Theoretically, these could be ShareRowExclusiveLock.
    4599             :                  */
    4600        9640 :             case AT_ColumnDefault:
    4601             :             case AT_CookedColumnDefault:
    4602             :             case AT_AlterConstraint:
    4603             :             case AT_AddIndex:   /* from ADD CONSTRAINT */
    4604             :             case AT_AddIndexConstraint:
    4605             :             case AT_ReplicaIdentity:
    4606             :             case AT_SetNotNull:
    4607             :             case AT_SetAttNotNull:
    4608             :             case AT_EnableRowSecurity:
    4609             :             case AT_DisableRowSecurity:
    4610             :             case AT_ForceRowSecurity:
    4611             :             case AT_NoForceRowSecurity:
    4612             :             case AT_AddIdentity:
    4613             :             case AT_DropIdentity:
    4614             :             case AT_SetIdentity:
    4615             :             case AT_SetExpression:
    4616             :             case AT_DropExpression:
    4617             :             case AT_SetCompression:
    4618        9640 :                 cmd_lockmode = AccessExclusiveLock;
    4619        9640 :                 break;
    4620             : 
    4621       12822 :             case AT_AddConstraint:
    4622             :             case AT_ReAddConstraint:    /* becomes AT_AddConstraint */
    4623             :             case AT_ReAddDomainConstraint:  /* becomes AT_AddConstraint */
    4624       12822 :                 if (IsA(cmd->def, Constraint))
    4625             :                 {
    4626       12822 :                     Constraint *con = (Constraint *) cmd->def;
    4627             : 
    4628       12822 :                     switch (con->contype)
    4629             :                     {
    4630        9502 :                         case CONSTR_EXCLUSION:
    4631             :                         case CONSTR_PRIMARY:
    4632             :                         case CONSTR_UNIQUE:
    4633             : 
    4634             :                             /*
    4635             :                              * Cases essentially the same as CREATE INDEX. We
    4636             :                              * could reduce the lock strength to ShareLock if
    4637             :                              * we can work out how to allow concurrent catalog
    4638             :                              * updates. XXX Might be set down to
    4639             :                              * ShareRowExclusiveLock but requires further
    4640             :                              * analysis.
    4641             :                              */
    4642        9502 :                             cmd_lockmode = AccessExclusiveLock;
    4643        9502 :                             break;
    4644        2386 :                         case CONSTR_FOREIGN:
    4645             : 
    4646             :                             /*
    4647             :                              * We add triggers to both tables when we add a
    4648             :                              * Foreign Key, so the lock level must be at least
    4649             :                              * as strong as CREATE TRIGGER.
    4650             :                              */
    4651        2386 :                             cmd_lockmode = ShareRowExclusiveLock;
    4652        2386 :                             break;
    4653             : 
    4654         934 :                         default:
    4655         934 :                             cmd_lockmode = AccessExclusiveLock;
    4656             :                     }
    4657           0 :                 }
    4658       12822 :                 break;
    4659             : 
    4660             :                 /*
    4661             :                  * These subcommands affect inheritance behaviour. Queries
    4662             :                  * started before us will continue to see the old inheritance
    4663             :                  * behaviour, while queries started after we commit will see
    4664             :                  * new behaviour. No need to prevent reads or writes to the
    4665             :                  * subtable while we hook it up though. Changing the TupDesc
    4666             :                  * may be a problem, so keep highest lock.
    4667             :                  */
    4668         382 :             case AT_AddInherit:
    4669             :             case AT_DropInherit:
    4670         382 :                 cmd_lockmode = AccessExclusiveLock;
    4671         382 :                 break;
    4672             : 
    4673             :                 /*
    4674             :                  * These subcommands affect implicit row type conversion. They
    4675             :                  * have affects similar to CREATE/DROP CAST on queries. don't
    4676             :                  * provide for invalidating parse trees as a result of such
    4677             :                  * changes, so we keep these at AccessExclusiveLock.
    4678             :                  */
    4679          72 :             case AT_AddOf:
    4680             :             case AT_DropOf:
    4681          72 :                 cmd_lockmode = AccessExclusiveLock;
    4682          72 :                 break;
    4683             : 
    4684             :                 /*
    4685             :                  * Only used by CREATE OR REPLACE VIEW which must conflict
    4686             :                  * with an SELECTs currently using the view.
    4687             :                  */
    4688         194 :             case AT_ReplaceRelOptions:
    4689         194 :                 cmd_lockmode = AccessExclusiveLock;
    4690         194 :                 break;
    4691             : 
    4692             :                 /*
    4693             :                  * These subcommands affect general strategies for performance
    4694             :                  * and maintenance, though don't change the semantic results
    4695             :                  * from normal data reads and writes. Delaying an ALTER TABLE
    4696             :                  * behind currently active writes only delays the point where
    4697             :                  * the new strategy begins to take effect, so there is no
    4698             :                  * benefit in waiting. In this case the minimum restriction
    4699             :                  * applies: we don't currently allow concurrent catalog
    4700             :                  * updates.
    4701             :                  */
    4702         234 :             case AT_SetStatistics:  /* Uses MVCC in getTableAttrs() */
    4703             :             case AT_ClusterOn:  /* Uses MVCC in getIndexes() */
    4704             :             case AT_DropCluster:    /* Uses MVCC in getIndexes() */
    4705             :             case AT_SetOptions: /* Uses MVCC in getTableAttrs() */
    4706             :             case AT_ResetOptions:   /* Uses MVCC in getTableAttrs() */
    4707         234 :                 cmd_lockmode = ShareUpdateExclusiveLock;
    4708         234 :                 break;
    4709             : 
    4710          88 :             case AT_SetLogged:
    4711             :             case AT_SetUnLogged:
    4712          88 :                 cmd_lockmode = AccessExclusiveLock;
    4713          88 :                 break;
    4714             : 
    4715         388 :             case AT_ValidateConstraint: /* Uses MVCC in getConstraints() */
    4716         388 :                 cmd_lockmode = ShareUpdateExclusiveLock;
    4717         388 :                 break;
    4718             : 
    4719             :                 /*
    4720             :                  * Rel options are more complex than first appears. Options
    4721             :                  * are set here for tables, views and indexes; for historical
    4722             :                  * reasons these can all be used with ALTER TABLE, so we can't
    4723             :                  * decide between them using the basic grammar.
    4724             :                  */
    4725         740 :             case AT_SetRelOptions:  /* Uses MVCC in getIndexes() and
    4726             :                                      * getTables() */
    4727             :             case AT_ResetRelOptions:    /* Uses MVCC in getIndexes() and
    4728             :                                          * getTables() */
    4729         740 :                 cmd_lockmode = AlterTableGetRelOptionsLockLevel((List *) cmd->def);
    4730         740 :                 break;
    4731             : 
    4732        2608 :             case AT_AttachPartition:
    4733        2608 :                 cmd_lockmode = ShareUpdateExclusiveLock;
    4734        2608 :                 break;
    4735             : 
    4736         528 :             case AT_DetachPartition:
    4737         528 :                 if (((PartitionCmd *) cmd->def)->concurrent)
    4738         158 :                     cmd_lockmode = ShareUpdateExclusiveLock;
    4739             :                 else
    4740         370 :                     cmd_lockmode = AccessExclusiveLock;
    4741         528 :                 break;
    4742             : 
    4743          14 :             case AT_DetachPartitionFinalize:
    4744          14 :                 cmd_lockmode = ShareUpdateExclusiveLock;
    4745          14 :                 break;
    4746             : 
    4747         252 :             case AT_SplitPartition:
    4748         252 :                 cmd_lockmode = AccessExclusiveLock;
    4749         252 :                 break;
    4750             : 
    4751         120 :             case AT_MergePartitions:
    4752         120 :                 cmd_lockmode = AccessExclusiveLock;
    4753         120 :                 break;
    4754             : 
    4755           0 :             default:            /* oops */
    4756           0 :                 elog(ERROR, "unrecognized alter table type: %d",
    4757             :                      (int) cmd->subtype);
    4758             :                 break;
    4759             :         }
    4760             : 
    4761             :         /*
    4762             :          * Take the greatest lockmode from any subcommand
    4763             :          */
    4764       36900 :         if (cmd_lockmode > lockmode)
    4765       30940 :             lockmode = cmd_lockmode;
    4766             :     }
    4767             : 
    4768       34896 :     return lockmode;
    4769             : }
    4770             : 
    4771             : /*
    4772             :  * ATController provides top level control over the phases.
    4773             :  *
    4774             :  * parsetree is passed in to allow it to be passed to event triggers
    4775             :  * when requested.
    4776             :  */
    4777             : static void
    4778       34656 : ATController(AlterTableStmt *parsetree,
    4779             :              Relation rel, List *cmds, bool recurse, LOCKMODE lockmode,
    4780             :              AlterTableUtilityContext *context)
    4781             : {
    4782       34656 :     List       *wqueue = NIL;
    4783             :     ListCell   *lcmd;
    4784             : 
    4785             :     /* Phase 1: preliminary examination of commands, create work queue */
    4786       71038 :     foreach(lcmd, cmds)
    4787             :     {
    4788       36654 :         AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);
    4789             : 
    4790       36654 :         ATPrepCmd(&wqueue, rel, cmd, recurse, false, lockmode, context);
    4791             :     }
    4792             : 
    4793             :     /* Close the relation, but keep lock until commit */
    4794       34384 :     relation_close(rel, NoLock);
    4795             : 
    4796             :     /* Phase 2: update system catalogs */
    4797       34384 :     ATRewriteCatalogs(&wqueue, lockmode, context);
    4798             : 
    4799             :     /* Phase 3: scan/rewrite tables as needed, and run afterStmts */
    4800       31748 :     ATRewriteTables(parsetree, &wqueue, lockmode, context);
    4801       31412 : }
    4802             : 
    4803             : /*
    4804             :  * ATPrepCmd
    4805             :  *
    4806             :  * Traffic cop for ALTER TABLE Phase 1 operations, including simple
    4807             :  * recursion and permission checks.
    4808             :  *
    4809             :  * Caller must have acquired appropriate lock type on relation already.
    4810             :  * This lock should be held until commit.
    4811             :  */
    4812             : static void
    4813       37294 : ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
    4814             :           bool recurse, bool recursing, LOCKMODE lockmode,
    4815             :           AlterTableUtilityContext *context)
    4816             : {
    4817             :     AlteredTableInfo *tab;
    4818       37294 :     AlterTablePass pass = AT_PASS_UNSET;
    4819             : 
    4820             :     /* Find or create work queue entry for this table */
    4821       37294 :     tab = ATGetQueueEntry(wqueue, rel);
    4822             : 
    4823             :     /*
    4824             :      * Disallow any ALTER TABLE other than ALTER TABLE DETACH FINALIZE on
    4825             :      * partitions that are pending detach.
    4826             :      */
    4827       37294 :     if (rel->rd_rel->relispartition &&
    4828        2628 :         cmd->subtype != AT_DetachPartitionFinalize &&
    4829        1314 :         PartitionHasPendingDetach(RelationGetRelid(rel)))
    4830           2 :         ereport(ERROR,
    4831             :                 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    4832             :                 errmsg("cannot alter partition \"%s\" with an incomplete detach",
    4833             :                        RelationGetRelationName(rel)),
    4834             :                 errhint("Use ALTER TABLE ... DETACH PARTITION ... FINALIZE to complete the pending detach operation."));
    4835             : 
    4836             :     /*
    4837             :      * Copy the original subcommand for each table, so we can scribble on it.
    4838             :      * This avoids conflicts when different child tables need to make
    4839             :      * different parse transformations (for example, the same column may have
    4840             :      * different column numbers in different children).
    4841             :      */
    4842       37292 :     cmd = copyObject(cmd);
    4843             : 
    4844             :     /*
    4845             :      * Do permissions and relkind checking, recursion to child tables if
    4846             :      * needed, and any additional phase-1 processing needed.  (But beware of
    4847             :      * adding any processing that looks at table details that another
    4848             :      * subcommand could change.  In some cases we reject multiple subcommands
    4849             :      * that could try to change the same state in contrary ways.)
    4850             :      */
    4851       37292 :     switch (cmd->subtype)
    4852             :     {
    4853        1962 :         case AT_AddColumn:      /* ADD COLUMN */
    4854        1962 :             ATSimplePermissions(cmd->subtype, rel,
    4855             :                                 ATT_TABLE | ATT_COMPOSITE_TYPE | ATT_FOREIGN_TABLE);
    4856        1962 :             ATPrepAddColumn(wqueue, rel, recurse, recursing, false, cmd,
    4857             :                             lockmode, context);
    4858             :             /* Recursion occurs during execution phase */
    4859        1950 :             pass = AT_PASS_ADD_COL;
    4860        1950 :             break;
    4861          24 :         case AT_AddColumnToView:    /* add column via CREATE OR REPLACE VIEW */
    4862          24 :             ATSimplePermissions(cmd->subtype, rel, ATT_VIEW);
    4863          24 :             ATPrepAddColumn(wqueue, rel, recurse, recursing, true, cmd,
    4864             :                             lockmode, context);
    4865             :             /* Recursion occurs during execution phase */
    4866          24 :             pass = AT_PASS_ADD_COL;
    4867          24 :             break;
    4868         590 :         case AT_ColumnDefault:  /* ALTER COLUMN DEFAULT */
    4869             : 
    4870             :             /*
    4871             :              * We allow defaults on views so that INSERT into a view can have
    4872             :              * default-ish behavior.  This works because the rewriter
    4873             :              * substitutes default values into INSERTs before it expands
    4874             :              * rules.
    4875             :              */
    4876         590 :             ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_VIEW | ATT_FOREIGN_TABLE);
    4877         590 :             ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
    4878             :             /* No command-specific prep needed */
    4879         590 :             pass = cmd->def ? AT_PASS_ADD_OTHERCONSTR : AT_PASS_DROP;
    4880         590 :             break;
    4881         110 :         case AT_CookedColumnDefault:    /* add a pre-cooked default */
    4882             :             /* This is currently used only in CREATE TABLE */
    4883             :             /* (so the permission check really isn't necessary) */
    4884         110 :             ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
    4885             :             /* This command never recurses */
    4886         110 :             pass = AT_PASS_ADD_OTHERCONSTR;
    4887         110 :             break;
    4888         156 :         case AT_AddIdentity:
    4889         156 :             ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_VIEW | ATT_FOREIGN_TABLE);
    4890             :             /* Set up recursion for phase 2; no other prep needed */
    4891         156 :             if (recurse)
    4892         150 :                 cmd->recurse = true;
    4893         156 :             pass = AT_PASS_ADD_OTHERCONSTR;
    4894         156 :             break;
    4895          62 :         case AT_SetIdentity:
    4896          62 :             ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_VIEW | ATT_FOREIGN_TABLE);
    4897             :             /* Set up recursion for phase 2; no other prep needed */
    4898          62 :             if (recurse)
    4899          56 :                 cmd->recurse = true;
    4900             :             /* This should run after AddIdentity, so do it in MISC pass */
    4901          62 :             pass = AT_PASS_MISC;
    4902          62 :             break;
    4903          62 :         case AT_DropIdentity:
    4904          62 :             ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_VIEW | ATT_FOREIGN_TABLE);
    4905             :             /* Set up recursion for phase 2; no other prep needed */
    4906          62 :             if (recurse)
    4907          56 :                 cmd->recurse = true;
    4908          62 :             pass = AT_PASS_DROP;
    4909          62 :             break;
    4910         262 :         case AT_DropNotNull:    /* ALTER COLUMN DROP NOT NULL */
    4911         262 :             ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
    4912             :             /* Set up recursion for phase 2; no other prep needed */
    4913         256 :             if (recurse)
    4914         244 :                 cmd->recurse = true;
    4915         256 :             pass = AT_PASS_DROP;
    4916         256 :             break;
    4917         372 :         case AT_SetNotNull:     /* ALTER COLUMN SET NOT NULL */
    4918         372 :             ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
    4919             :             /* Set up recursion for phase 2; no other prep needed */
    4920         366 :             if (recurse)
    4921         336 :                 cmd->recurse = true;
    4922         366 :             pass = AT_PASS_COL_ATTRS;
    4923         366 :             break;
    4924        7350 :         case AT_SetAttNotNull:  /* set pg_attribute.attnotnull without adding
    4925             :                                  * a constraint */
    4926        7350 :             ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
    4927             :             /* Need command-specific recursion decision */
    4928        7350 :             ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
    4929        7350 :             pass = AT_PASS_COL_ATTRS;
    4930        7350 :             break;
    4931          84 :         case AT_SetExpression:  /* ALTER COLUMN SET EXPRESSION */
    4932          84 :             ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
    4933          84 :             ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
    4934          84 :             pass = AT_PASS_SET_EXPRESSION;
    4935          84 :             break;
    4936          44 :         case AT_DropExpression: /* ALTER COLUMN DROP EXPRESSION */
    4937          44 :             ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
    4938          44 :             ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
    4939          44 :             ATPrepDropExpression(rel, cmd, recurse, recursing, lockmode);
    4940          32 :             pass = AT_PASS_DROP;
    4941          32 :             break;
    4942         164 :         case AT_SetStatistics:  /* ALTER COLUMN SET STATISTICS */
    4943         164 :             ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_MATVIEW | ATT_INDEX | ATT_PARTITIONED_INDEX | ATT_FOREIGN_TABLE);
    4944         164 :             ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
    4945             :             /* No command-specific prep needed */
    4946         164 :             pass = AT_PASS_MISC;
    4947         164 :             break;
    4948          44 :         case AT_SetOptions:     /* ALTER COLUMN SET ( options ) */
    4949             :         case AT_ResetOptions:   /* ALTER COLUMN RESET ( options ) */
    4950          44 :             ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_MATVIEW | ATT_FOREIGN_TABLE);
    4951             :             /* This command never recurses */
    4952          32 :             pass = AT_PASS_MISC;
    4953          32 :             break;
    4954         234 :         case AT_SetStorage:     /* ALTER COLUMN SET STORAGE */
    4955         234 :             ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_MATVIEW | ATT_FOREIGN_TABLE);
    4956         234 :             ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
    4957             :             /* No command-specific prep needed */
    4958         234 :             pass = AT_PASS_MISC;
    4959         234 :             break;
    4960          66 :         case AT_SetCompression: /* ALTER COLUMN SET COMPRESSION */
    4961          66 :             ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_MATVIEW);
    4962             :             /* This command never recurses */
    4963             :             /* No command-specific prep needed */
    4964          66 :             pass = AT_PASS_MISC;
    4965          66 :             break;
    4966        1624 :         case AT_DropColumn:     /* DROP COLUMN */
    4967        1624 :             ATSimplePermissions(cmd->subtype, rel,
    4968             :                                 ATT_TABLE | ATT_COMPOSITE_TYPE | ATT_FOREIGN_TABLE);
    4969        1618 :             ATPrepDropColumn(wqueue, rel, recurse, recursing, cmd,
    4970             :                              lockmode, context);
    4971             :             /* Recursion occurs during execution phase */
    4972        1606 :             pass = AT_PASS_DROP;
    4973        1606 :             break;
    4974           0 :         case AT_AddIndex:       /* ADD INDEX */
    4975           0 :             ATSimplePermissions(cmd->subtype, rel, ATT_TABLE);
    4976             :             /* This command never recurses */
    4977             :             /* No command-specific prep needed */
    4978           0 :             pass = AT_PASS_ADD_INDEX;
    4979           0 :             break;
    4980       12876 :         case AT_AddConstraint:  /* ADD CONSTRAINT */
    4981       12876 :             ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
    4982             :             /* Recursion occurs during execution phase */
    4983             :             /* No command-specific prep needed except saving recurse flag */
    4984       12876 :             if (recurse)
    4985       12526 :                 cmd->recurse = true;
    4986       12876 :             pass = AT_PASS_ADD_CONSTR;
    4987       12876 :             break;
    4988           0 :         case AT_AddIndexConstraint: /* ADD CONSTRAINT USING INDEX */
    4989           0 :             ATSimplePermissions(cmd->subtype, rel, ATT_TABLE);
    4990             :             /* This command never recurses */
    4991             :             /* No command-specific prep needed */
    4992           0 :             pass = AT_PASS_ADD_INDEXCONSTR;
    4993           0 :             break;
    4994         968 :         case AT_DropConstraint: /* DROP CONSTRAINT */
    4995         968 :             ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
    4996         968 :             ATCheckPartitionsNotInUse(rel, lockmode);
    4997             :             /* Other recursion occurs during execution phase */
    4998             :             /* No command-specific prep needed except saving recurse flag */
    4999         962 :             if (recurse)
    5000         732 :                 cmd->recurse = true;
    5001         962 :             pass = AT_PASS_DROP;
    5002         962 :             break;
    5003        1128 :         case AT_AlterColumnType:    /* ALTER COLUMN TYPE */
    5004        1128 :             ATSimplePermissions(cmd->subtype, rel,
    5005             :                                 ATT_TABLE | ATT_COMPOSITE_TYPE | ATT_FOREIGN_TABLE);
    5006             :             /* See comments for ATPrepAlterColumnType */
    5007        1128 :             cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, recurse, lockmode,
    5008             :                                       AT_PASS_UNSET, context);
    5009             :             Assert(cmd != NULL);
    5010             :             /* Performs own recursion */
    5011        1122 :             ATPrepAlterColumnType(wqueue, tab, rel, recurse, recursing, cmd,
    5012             :                                   lockmode, context);
    5013         978 :             pass = AT_PASS_ALTER_TYPE;
    5014         978 :             break;
    5015         164 :         case AT_AlterColumnGenericOptions:
    5016         164 :             ATSimplePermissions(cmd->subtype, rel, ATT_FOREIGN_TABLE);
    5017             :             /* This command never recurses */
    5018             :             /* No command-specific prep needed */
    5019         164 :             pass = AT_PASS_MISC;
    5020         164 :             break;
    5021        1796 :         case AT_ChangeOwner:    /* ALTER OWNER */
    5022             :             /* This command never recurses */
    5023             :             /* No command-specific prep needed */
    5024        1796 :             pass = AT_PASS_MISC;
    5025        1796 :             break;
    5026          64 :         case AT_ClusterOn:      /* CLUSTER ON */
    5027             :         case AT_DropCluster:    /* SET WITHOUT CLUSTER */
    5028          64 :             ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_MATVIEW);
    5029             :             /* These commands never recurse */
    5030             :             /* No command-specific prep needed */
    5031          64 :             pass = AT_PASS_MISC;
    5032          64 :             break;
    5033          38 :         case AT_SetLogged:      /* SET LOGGED */
    5034          38 :             ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_SEQUENCE);
    5035          38 :             if (tab->chgPersistence)
    5036           0 :                 ereport(ERROR,
    5037             :                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    5038             :                          errmsg("cannot change persistence setting twice")));
    5039          38 :             tab->chgPersistence = ATPrepChangePersistence(rel, true);
    5040             :             /* force rewrite if necessary; see comment in ATRewriteTables */
    5041          32 :             if (tab->chgPersistence)
    5042             :             {
    5043          26 :                 tab->rewrite |= AT_REWRITE_ALTER_PERSISTENCE;
    5044          26 :                 tab->newrelpersistence = RELPERSISTENCE_PERMANENT;
    5045             :             }
    5046          32 :             pass = AT_PASS_MISC;
    5047          32 :             break;
    5048          50 :         case AT_SetUnLogged:    /* SET UNLOGGED */
    5049          50 :             ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_SEQUENCE);
    5050          50 :             if (tab->chgPersistence)
    5051           0 :                 ereport(ERROR,
    5052             :                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    5053             :                          errmsg("cannot change persistence setting twice")));
    5054          50 :             tab->chgPersistence = ATPrepChangePersistence(rel, false);
    5055             :             /* force rewrite if necessary; see comment in ATRewriteTables */
    5056          44 :             if (tab->chgPersistence)
    5057             :             {
    5058          38 :                 tab->rewrite |= AT_REWRITE_ALTER_PERSISTENCE;
    5059          38 :                 tab->newrelpersistence = RELPERSISTENCE_UNLOGGED;
    5060             :             }
    5061          44 :             pass = AT_PASS_MISC;
    5062          44 :             break;
    5063           6 :         case AT_DropOids:       /* SET WITHOUT OIDS */
    5064           6 :             ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
    5065           6 :             pass = AT_PASS_DROP;
    5066           6 :             break;
    5067         128 :         case AT_SetAccessMethod:    /* SET ACCESS METHOD */
    5068         128 :             ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_MATVIEW);
    5069             : 
    5070             :             /* check if another access method change was already requested */
    5071         128 :             if (tab->chgAccessMethod)
    5072          18 :                 ereport(ERROR,
    5073             :                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    5074             :                          errmsg("cannot have multiple SET ACCESS METHOD subcommands")));
    5075             : 
    5076         110 :             ATPrepSetAccessMethod(tab, rel, cmd->name);
    5077         110 :             pass = AT_PASS_MISC;    /* does not matter; no work in Phase 2 */
    5078         110 :             break;
    5079         158 :         case AT_SetTableSpace:  /* SET TABLESPACE */
    5080         158 :             ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_MATVIEW | ATT_INDEX |
    5081             :                                 ATT_PARTITIONED_INDEX);
    5082             :             /* This command never recurses */
    5083         158 :             ATPrepSetTableSpace(tab, rel, cmd->name, lockmode);
    5084         158 :             pass = AT_PASS_MISC;    /* doesn't actually matter */
    5085         158 :             break;
    5086         934 :         case AT_SetRelOptions:  /* SET (...) */
    5087             :         case AT_ResetRelOptions:    /* RESET (...) */
    5088             :         case AT_ReplaceRelOptions:  /* reset them all, then set just these */
    5089         934 :             ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_VIEW | ATT_MATVIEW | ATT_INDEX);
    5090             :             /* This command never recurses */
    5091             :             /* No command-specific prep needed */
    5092         934 :             pass = AT_PASS_MISC;
    5093         934 :             break;
    5094         338 :         case AT_AddInherit:     /* INHERIT */
    5095         338 :             ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
    5096             :             /* This command never recurses */
    5097         338 :             ATPrepAddInherit(rel);
    5098         320 :             pass = AT_PASS_MISC;
    5099         320 :             break;
    5100          44 :         case AT_DropInherit:    /* NO INHERIT */
    5101          44 :             ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
    5102             :             /* This command never recurses */
    5103             :             /* No command-specific prep needed */
    5104          44 :             pass = AT_PASS_MISC;
    5105          44 :             break;
    5106         132 :         case AT_AlterConstraint:    /* ALTER CONSTRAINT */
    5107         132 :             ATSimplePermissions(cmd->subtype, rel, ATT_TABLE);
    5108             :             /* Recursion occurs during execution phase */
    5109         126 :             pass = AT_PASS_MISC;
    5110         126 :             break;
    5111         388 :         case AT_ValidateConstraint: /* VALIDATE CONSTRAINT */
    5112         388 :             ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
    5113             :             /* Recursion occurs during execution phase */
    5114             :             /* No command-specific prep needed except saving recurse flag */
    5115         388 :             if (recurse)
    5116         388 :                 cmd->recurse = true;
    5117         388 :             pass = AT_PASS_MISC;
    5118         388 :             break;
    5119         450 :         case AT_ReplicaIdentity:    /* REPLICA IDENTITY ... */
    5120         450 :             ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_MATVIEW);
    5121         450 :             pass = AT_PASS_MISC;
    5122             :             /* This command never recurses */
    5123             :             /* No command-specific prep needed */
    5124         450 :             break;
    5125         340 :         case AT_EnableTrig:     /* ENABLE TRIGGER variants */
    5126             :         case AT_EnableAlwaysTrig:
    5127             :         case AT_EnableReplicaTrig:
    5128             :         case AT_EnableTrigAll:
    5129             :         case AT_EnableTrigUser:
    5130             :         case AT_DisableTrig:    /* DISABLE TRIGGER variants */
    5131             :         case AT_DisableTrigAll:
    5132             :         case AT_DisableTrigUser:
    5133         340 :             ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
    5134             :             /* Set up recursion for phase 2; no other prep needed */
    5135         340 :             if (recurse)
    5136         312 :                 cmd->recurse = true;
    5137         340 :             pass = AT_PASS_MISC;
    5138         340 :             break;
    5139         520 :         case AT_EnableRule:     /* ENABLE/DISABLE RULE variants */
    5140             :         case AT_EnableAlwaysRule:
    5141             :         case AT_EnableReplicaRule:
    5142             :         case AT_DisableRule:
    5143             :         case AT_AddOf:          /* OF */
    5144             :         case AT_DropOf:         /* NOT OF */
    5145             :         case AT_EnableRowSecurity:
    5146             :         case AT_DisableRowSecurity:
    5147             :         case AT_ForceRowSecurity:
    5148             :         case AT_NoForceRowSecurity:
    5149         520 :             ATSimplePermissions(cmd->subtype, rel, ATT_TABLE);
    5150             :             /* These commands never recurse */
    5151             :             /* No command-specific prep needed */
    5152         520 :             pass = AT_PASS_MISC;
    5153         520 :             break;
    5154          50 :         case AT_GenericOptions:
    5155          50 :             ATSimplePermissions(cmd->subtype, rel, ATT_FOREIGN_TABLE);
    5156             :             /* No command-specific prep needed */
    5157          50 :             pass = AT_PASS_MISC;
    5158          50 :             break;
    5159        2596 :         case AT_AttachPartition:
    5160        2596 :             ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_PARTITIONED_INDEX);
    5161             :             /* No command-specific prep needed */
    5162        2596 :             pass = AT_PASS_MISC;
    5163        2596 :             break;
    5164         528 :         case AT_DetachPartition:
    5165         528 :             ATSimplePermissions(cmd->subtype, rel, ATT_TABLE);
    5166             :             /* No command-specific prep needed */
    5167         522 :             pass = AT_PASS_MISC;
    5168         522 :             break;
    5169          14 :         case AT_DetachPartitionFinalize:
    5170          14 :             ATSimplePermissions(cmd->subtype, rel, ATT_TABLE);
    5171             :             /* No command-specific prep needed */
    5172          14 :             pass = AT_PASS_MISC;
    5173          14 :             break;
    5174         252 :         case AT_SplitPartition:
    5175         252 :             ATSimplePermissions(cmd->subtype, rel, ATT_TABLE);
    5176             :             /* No command-specific prep needed */
    5177         252 :             pass = AT_PASS_MISC;
    5178         252 :             break;
    5179         120 :         case AT_MergePartitions:
    5180         120 :             ATSimplePermissions(cmd->subtype, rel, ATT_TABLE);
    5181             :             /* No command-specific prep needed */
    5182         120 :             pass = AT_PASS_MISC;
    5183         120 :             break;
    5184           0 :         default:                /* oops */
    5185           0 :             elog(ERROR, "unrecognized alter table type: %d",
    5186             :                  (int) cmd->subtype);
    5187             :             pass = AT_PASS_UNSET;   /* keep compiler quiet */
    5188             :             break;
    5189             :     }
    5190             :     Assert(pass > AT_PASS_UNSET);
    5191             : 
    5192             :     /* Add the subcommand to the appropriate list for phase 2 */
    5193       37010 :     tab->subcmds[pass] = lappend(tab->subcmds[pass], cmd);
    5194       37010 : }
    5195             : 
    5196             : /*
    5197             :  * ATRewriteCatalogs
    5198             :  *
    5199             :  * Traffic cop for ALTER TABLE Phase 2 operations.  Subcommands are
    5200             :  * dispatched in a "safe" execution order (designed to avoid unnecessary
    5201             :  * conflicts).
    5202             :  */
    5203             : static void
    5204       34384 : ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode,
    5205             :                   AlterTableUtilityContext *context)
    5206             : {
    5207             :     ListCell   *ltab;
    5208             : 
    5209             :     /*
    5210             :      * We process all the tables "in parallel", one pass at a time.  This is
    5211             :      * needed because we may have to propagate work from one table to another
    5212             :      * (specifically, ALTER TYPE on a foreign key's PK has to dispatch the
    5213             :      * re-adding of the foreign key constraint to the other table).  Work can
    5214             :      * only be propagated into later passes, however.
    5215             :      */
    5216      469428 :     for (AlterTablePass pass = 0; pass < AT_NUM_PASSES; pass++)
    5217             :     {
    5218             :         /* Go through each table that needs to be processed */
    5219      887580 :         foreach(ltab, *wqueue)
    5220             :         {
    5221      452536 :             AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
    5222      452536 :             List       *subcmds = tab->subcmds[pass];
    5223             :             ListCell   *lcmd;
    5224             : 
    5225      452536 :             if (subcmds == NIL)
    5226      398108 :                 continue;
    5227             : 
    5228             :             /*
    5229             :              * Open the relation and store it in tab.  This allows subroutines
    5230             :              * close and reopen, if necessary.  Appropriate lock was obtained
    5231             :              * by phase 1, needn't get it again.
    5232             :              */
    5233       54428 :             tab->rel = relation_open(tab->relid, NoLock);
    5234             : 
    5235      109892 :             foreach(lcmd, subcmds)
    5236       58100 :                 ATExecCmd(wqueue, tab,
    5237       58100 :                           lfirst_node(AlterTableCmd, lcmd),
    5238             :                           lockmode, pass, context);
    5239             : 
    5240             :             /*
    5241             :              * After the ALTER TYPE or SET EXPRESSION pass, do cleanup work
    5242             :              * (this is not done in ATExecAlterColumnType since it should be
    5243             :              * done only once if multiple columns of a table are altered).
    5244             :              */
    5245       51792 :             if (pass == AT_PASS_ALTER_TYPE || pass == AT_PASS_SET_EXPRESSION)
    5246         960 :                 ATPostAlterTypeCleanup(wqueue, tab, lockmode);
    5247             : 
    5248       51792 :             if (tab->rel)
    5249             :             {
    5250       51792 :                 relation_close(tab->rel, NoLock);
    5251       51792 :                 tab->rel = NULL;
    5252             :             }
    5253             :         }
    5254             :     }
    5255             : 
    5256             :     /* Check to see if a toast table must be added. */
    5257       67260 :     foreach(ltab, *wqueue)
    5258             :     {
    5259       35512 :         AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
    5260             : 
    5261             :         /*
    5262             :          * If the table is source table of ATTACH PARTITION command, we did
    5263             :          * not modify anything about it that will change its toasting
    5264             :          * requirement, so no need to check.
    5265             :          */
    5266       35512 :         if (((tab->relkind == RELKIND_RELATION ||
    5267        6440 :               tab->relkind == RELKIND_PARTITIONED_TABLE) &&
    5268       33674 :              tab->partition_constraint == NULL) ||
    5269        3770 :             tab->relkind == RELKIND_MATVIEW)
    5270       31792 :             AlterTableCreateToastTable(tab->relid, (Datum) 0, lockmode);
    5271             :     }
    5272       31748 : }
    5273             : 
    5274             : /*
    5275             :  * ATExecCmd: dispatch a subcommand to appropriate execution routine
    5276             :  */
    5277             : static void
    5278       58100 : ATExecCmd(List **wqueue, AlteredTableInfo *tab,
    5279             :           AlterTableCmd *cmd, LOCKMODE lockmode, AlterTablePass cur_pass,
    5280             :           AlterTableUtilityContext *context)
    5281             : {
    5282       58100 :     ObjectAddress address = InvalidObjectAddress;
    5283       58100 :     Relation    rel = tab->rel;
    5284             : 
    5285       58100 :     switch (cmd->subtype)
    5286             :     {
    5287        1968 :         case AT_AddColumn:      /* ADD COLUMN */
    5288             :         case AT_AddColumnToView:    /* add column via CREATE OR REPLACE VIEW */
    5289        1968 :             address = ATExecAddColumn(wqueue, tab, rel, &cmd,
    5290        1968 :                                       cmd->recurse, false,
    5291             :                                       lockmode, cur_pass, context);
    5292        1854 :             break;
    5293         566 :         case AT_ColumnDefault:  /* ALTER COLUMN DEFAULT */
    5294         566 :             address = ATExecColumnDefault(rel, cmd->name, cmd->def, lockmode);
    5295         506 :             break;
    5296         110 :         case AT_CookedColumnDefault:    /* add a pre-cooked default */
    5297         110 :             address = ATExecCookedColumnDefault(rel, cmd->num, cmd->def);
    5298         110 :             break;
    5299         150 :         case AT_AddIdentity:
    5300         150 :             cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
    5301             :                                       cur_pass, context);
    5302             :             Assert(cmd != NULL);
    5303         144 :             address = ATExecAddIdentity(rel, cmd->name, cmd->def, lockmode, cmd->recurse, false);
    5304         102 :             break;
    5305          62 :         case AT_SetIdentity:
    5306          62 :             cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
    5307             :                                       cur_pass, context);
    5308             :             Assert(cmd != NULL);
    5309          62 :             address = ATExecSetIdentity(rel, cmd->name, cmd->def, lockmode, cmd->recurse, false);
    5310          38 :             break;
    5311          62 :         case AT_DropIdentity:
    5312          62 :             address = ATExecDropIdentity(rel, cmd->name, cmd->missing_ok, lockmode, cmd->recurse, false);
    5313          44 :             break;
    5314         256 :         case AT_DropNotNull:    /* ALTER COLUMN DROP NOT NULL */
    5315         256 :             address = ATExecDropNotNull(rel, cmd->name, cmd->recurse, lockmode);
    5316         142 :             break;
    5317         366 :         case AT_SetNotNull:     /* ALTER COLUMN SET NOT NULL */
    5318         366 :             address = ATExecSetNotNull(wqueue, rel, NULL, cmd->name,
    5319         366 :                                        cmd->recurse, false, NULL, lockmode);
    5320         336 :             break;
    5321       14656 :         case AT_SetAttNotNull:  /* set pg_attribute.attnotnull */
    5322       14656 :             address = ATExecSetAttNotNull(wqueue, rel, cmd->name, lockmode);
    5323       14638 :             break;
    5324          84 :         case AT_SetExpression:
    5325          84 :             address = ATExecSetExpression(tab, rel, cmd->name, cmd->def, lockmode);
    5326          78 :             break;
    5327          32 :         case AT_DropExpression:
    5328          32 :             address = ATExecDropExpression(rel, cmd->name, cmd->missing_ok, lockmode);
    5329          26 :             break;
    5330         164 :         case AT_SetStatistics:  /* ALTER COLUMN SET STATISTICS */
    5331         164 :             address = ATExecSetStatistics(rel, cmd->name, cmd->num, cmd->def, lockmode);
    5332         116 :             break;
    5333          26 :         case AT_SetOptions:     /* ALTER COLUMN SET ( options ) */
    5334          26 :             address = ATExecSetOptions(rel, cmd->name, cmd->def, false, lockmode);
    5335          26 :             break;
    5336           6 :         case AT_ResetOptions:   /* ALTER COLUMN RESET ( options ) */
    5337           6 :             address = ATExecSetOptions(rel, cmd->name, cmd->def, true, lockmode);
    5338           6 :             break;
    5339         234 :         case AT_SetStorage:     /* ALTER COLUMN SET STORAGE */
    5340         234 :             address = ATExecSetStorage(rel, cmd->name, cmd->def, lockmode);
    5341         222 :             break;
    5342          66 :         case AT_SetCompression: /* ALTER COLUMN SET COMPRESSION */
    5343          66 :             address = ATExecSetCompression(rel, cmd->name, cmd->def,
    5344             :                                            lockmode);
    5345          60 :             break;
    5346        1606 :         case AT_DropColumn:     /* DROP COLUMN */
    5347        1606 :             address = ATExecDropColumn(wqueue, rel, cmd->name,
    5348        1606 :                                        cmd->behavior, cmd->recurse, false,
    5349        1606 :                                        cmd->missing_ok, lockmode,
    5350             :                                        NULL);
    5351        1432 :             break;
    5352        1024 :         case AT_AddIndex:       /* ADD INDEX */
    5353        1024 :             address = ATExecAddIndex(tab, rel, (IndexStmt *) cmd->def, false,
    5354             :                                      lockmode);
    5355         896 :             break;
    5356         434 :         case AT_ReAddIndex:     /* ADD INDEX */
    5357         434 :             address = ATExecAddIndex(tab, rel, (IndexStmt *) cmd->def, true,
    5358             :                                      lockmode);
    5359         434 :             break;
    5360          14 :         case AT_ReAddStatistics:    /* ADD STATISTICS */
    5361          14 :             address = ATExecAddStatistics(tab, rel, (CreateStatsStmt *) cmd->def,
    5362             :                                           true, lockmode);
    5363          14 :             break;
    5364       16546 :         case AT_AddConstraint:  /* ADD CONSTRAINT */
    5365             :             /* Transform the command only during initial examination */
    5366       16546 :             if (cur_pass == AT_PASS_ADD_CONSTR)
    5367       12846 :                 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd,
    5368       12876 :                                           cmd->recurse, lockmode,
    5369             :                                           cur_pass, context);
    5370             :             /* Depending on constraint type, might be no more work to do now */
    5371       16516 :             if (cmd != NULL)
    5372             :                 address =
    5373        3670 :                     ATExecAddConstraint(wqueue, tab, rel,
    5374        3670 :                                         (Constraint *) cmd->def,
    5375        3670 :                                         cmd->recurse, false, lockmode);
    5376       15962 :             break;
    5377         188 :         case AT_ReAddConstraint:    /* Re-add pre-existing check constraint */
    5378             :             address =
    5379         188 :                 ATExecAddConstraint(wqueue, tab, rel, (Constraint *) cmd->def,
    5380             :                                     true, true, lockmode);
    5381         176 :             break;
    5382          14 :         case AT_ReAddDomainConstraint:  /* Re-add pre-existing domain check
    5383             :                                          * constraint */
    5384             :             address =
    5385          14 :                 AlterDomainAddConstraint(((AlterDomainStmt *) cmd->def)->typeName,
    5386          14 :                                          ((AlterDomainStmt *) cmd->def)->def,
    5387             :                                          NULL);
    5388           8 :             break;
    5389          54 :         case AT_ReAddComment:   /* Re-add existing comment */
    5390          54 :             address = CommentObject((CommentStmt *) cmd->def);
    5391          54 :             break;
    5392        8434 :         case AT_AddIndexConstraint: /* ADD CONSTRAINT USING INDEX */
    5393        8434 :             address = ATExecAddIndexConstraint(tab, rel, (IndexStmt *) cmd->def,
    5394             :                                                lockmode);
    5395        8422 :             break;
    5396         126 :         case AT_AlterConstraint:    /* ALTER CONSTRAINT */
    5397         126 :             address = ATExecAlterConstraint(rel, cmd, false, false, lockmode);
    5398         114 :             break;
    5399         388 :         case AT_ValidateConstraint: /* VALIDATE CONSTRAINT */
    5400         388 :             address = ATExecValidateConstraint(wqueue, rel, cmd->name, cmd->recurse,
    5401             :                                                false, lockmode);
    5402         388 :             break;
    5403         962 :         case AT_DropConstraint: /* DROP CONSTRAINT */
    5404         962 :             ATExecDropConstraint(rel, cmd->name, cmd->behavior,
    5405         962 :                                  cmd->recurse,
    5406         962 :                                  cmd->missing_ok, lockmode);
    5407         764 :             break;
    5408         948 :         case AT_AlterColumnType:    /* ALTER COLUMN TYPE */
    5409             :             /* parse transformation was done earlier */
    5410         948 :             address = ATExecAlterColumnType(tab, rel, cmd, lockmode);
    5411         912 :             break;
    5412         164 :         case AT_AlterColumnGenericOptions:  /* ALTER COLUMN OPTIONS */
    5413             :             address =
    5414         164 :                 ATExecAlterColumnGenericOptions(rel, cmd->name,
    5415         164 :                                                 (List *) cmd->def, lockmode);
    5416         158 :             break;
    5417        1796 :         case AT_ChangeOwner:    /* ALTER OWNER */
    5418        1790 :             ATExecChangeOwner(RelationGetRelid(rel),
    5419        1796 :                               get_rolespec_oid(cmd->newowner, false),
    5420             :                               false, lockmode);
    5421        1778 :             break;
    5422          64 :         case AT_ClusterOn:      /* CLUSTER ON */
    5423          64 :             address = ATExecClusterOn(rel, cmd->name, lockmode);
    5424          58 :             break;
    5425          18 :         case AT_DropCluster:    /* SET WITHOUT CLUSTER */
    5426          18 :             ATExecDropCluster(rel, lockmode);
    5427          12 :             break;
    5428          76 :         case AT_SetLogged:      /* SET LOGGED */
    5429             :         case AT_SetUnLogged:    /* SET UNLOGGED */
    5430          76 :             break;
    5431           6 :         case AT_DropOids:       /* SET WITHOUT OIDS */
    5432             :             /* nothing to do here, oid columns don't exist anymore */
    5433           6 :             break;
    5434          92 :         case AT_SetAccessMethod:    /* SET ACCESS METHOD */
    5435             : 
    5436             :             /*
    5437             :              * Only do this for partitioned tables, for which this is just a
    5438             :              * catalog change.  Tables with storage are handled by Phase 3.
    5439             :              */
    5440          92 :             if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&
    5441          50 :                 tab->chgAccessMethod)
    5442          44 :                 ATExecSetAccessMethodNoStorage(rel, tab->newAccessMethod);
    5443          92 :             break;
    5444         158 :         case AT_SetTableSpace:  /* SET TABLESPACE */
    5445             : 
    5446             :             /*
    5447             :              * Only do this for partitioned tables and indexes, for which this
    5448             :              * is just a catalog change.  Other relation types which have
    5449             :              * storage are handled by Phase 3.
    5450             :              */
    5451         158 :             if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
    5452         146 :                 rel->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
    5453          36 :                 ATExecSetTableSpaceNoStorage(rel, tab->newTableSpace);
    5454             : 
    5455         152 :             break;
    5456         934 :         case AT_SetRelOptions:  /* SET (...) */
    5457             :         case AT_ResetRelOptions:    /* RESET (...) */
    5458             :         case AT_ReplaceRelOptions:  /* replace entire option list */
    5459         934 :             ATExecSetRelOptions(rel, (List *) cmd->def, cmd->subtype, lockmode);
    5460         882 :             break;
    5461         122 :         case AT_EnableTrig:     /* ENABLE TRIGGER name */
    5462         122 :             ATExecEnableDisableTrigger(rel, cmd->name,
    5463             :                                        TRIGGER_FIRES_ON_ORIGIN, false,
    5464         122 :                                        cmd->recurse,
    5465             :                                        lockmode);
    5466         122 :             break;
    5467          40 :         case AT_EnableAlwaysTrig:   /* ENABLE ALWAYS TRIGGER name */
    5468          40 :             ATExecEnableDisableTrigger(rel, cmd->name,
    5469             :                                        TRIGGER_FIRES_ALWAYS, false,
    5470          40 :                                        cmd->recurse,
    5471             :                                        lockmode);
    5472          40 :             break;
    5473          16 :         case AT_EnableReplicaTrig:  /* ENABLE REPLICA TRIGGER name */
    5474          16 :             ATExecEnableDisableTrigger(rel, cmd->name,
    5475             :                                        TRIGGER_FIRES_ON_REPLICA, false,
    5476          16 :                                        cmd->recurse,
    5477             :                                        lockmode);
    5478          16 :             break;
    5479         138 :         case AT_DisableTrig:    /* DISABLE TRIGGER name */
    5480         138 :             ATExecEnableDisableTrigger(rel, cmd->name,
    5481             :                                        TRIGGER_DISABLED, false,
    5482         138 :                                        cmd->recurse,
    5483             :                                        lockmode);
    5484         138 :             break;
    5485           0 :         case AT_EnableTrigAll:  /* ENABLE TRIGGER ALL */
    5486           0 :             ATExecEnableDisableTrigger(rel, NULL,
    5487             :                                        TRIGGER_FIRES_ON_ORIGIN, false,
    5488           0 :                                        cmd->recurse,
    5489             :                                        lockmode);
    5490           0 :             break;
    5491          12 :         case AT_DisableTrigAll: /* DISABLE TRIGGER ALL */
    5492          12 :             ATExecEnableDisableTrigger(rel, NULL,
    5493             :                                        TRIGGER_DISABLED, false,
    5494          12 :                                        cmd->recurse,
    5495             :                                        lockmode);
    5496          12 :             break;
    5497           0 :         case AT_EnableTrigUser: /* ENABLE TRIGGER USER */
    5498           0 :             ATExecEnableDisableTrigger(rel, NULL,
    5499             :                                        TRIGGER_FIRES_ON_ORIGIN, true,
    5500           0 :                                        cmd->recurse,
    5501             :                                        lockmode);
    5502           0 :             break;
    5503          12 :         case AT_DisableTrigUser:    /* DISABLE TRIGGER USER */
    5504          12 :             ATExecEnableDisableTrigger(rel, NULL,
    5505             :                                        TRIGGER_DISABLED, true,
    5506          12 :                                        cmd->recurse,
    5507             :                                        lockmode);
    5508          12 :             break;
    5509             : 
    5510           8 :         case AT_EnableRule:     /* ENABLE RULE name */
    5511           8 :             ATExecEnableDisableRule(rel, cmd->name,
    5512             :                                     RULE_FIRES_ON_ORIGIN, lockmode);
    5513           8 :             break;
    5514           0 :         case AT_EnableAlwaysRule:   /* ENABLE ALWAYS RULE name */
    5515           0 :             ATExecEnableDisableRule(rel, cmd->name,
    5516             :                                     RULE_FIRES_ALWAYS, lockmode);
    5517           0 :             break;
    5518           6 :         case AT_EnableReplicaRule:  /* ENABLE REPLICA RULE name */
    5519           6 :             ATExecEnableDisableRule(rel, cmd->name,
    5520             :                                     RULE_FIRES_ON_REPLICA, lockmode);
    5521           6 :             break;
    5522          32 :         case AT_DisableRule:    /* DISABLE RULE name */
    5523          32 :             ATExecEnableDisableRule(rel, cmd->name,
    5524             :                                     RULE_DISABLED, lockmode);
    5525          32 :             break;
    5526             : 
    5527         320 :         case AT_AddInherit:
    5528         320 :             address = ATExecAddInherit(rel, (RangeVar *) cmd->def, lockmode);
    5529         230 :             break;
    5530          44 :         case AT_DropInherit:
    5531          44 :             address = ATExecDropInherit(rel, (RangeVar *) cmd->def, lockmode);
    5532          38 :             break;
    5533          66 :         case AT_AddOf:
    5534          66 :             address = ATExecAddOf(rel, (TypeName *) cmd->def, lockmode);
    5535          30 :             break;
    5536           6 :         case AT_DropOf:
    5537           6 :             ATExecDropOf(rel, lockmode);
    5538           6 :             break;
    5539         468 :         case AT_ReplicaIdentity:
    5540         468 :             ATExecReplicaIdentity(rel, (ReplicaIdentityStmt *) cmd->def, lockmode);
    5541         420 :             break;
    5542         278 :         case AT_EnableRowSecurity:
    5543         278 :             ATExecSetRowSecurity(rel, true);
    5544         278 :             break;
    5545          10 :         case AT_DisableRowSecurity:
    5546          10 :             ATExecSetRowSecurity(rel, false);
    5547          10 :             break;
    5548          82 :         case AT_ForceRowSecurity:
    5549          82 :             ATExecForceNoForceRowSecurity(rel, true);
    5550          82 :             break;
    5551          32 :         case AT_NoForceRowSecurity:
    5552          32 :             ATExecForceNoForceRowSecurity(rel, false);
    5553          32 :             break;
    5554          50 :         case AT_GenericOptions:
    5555          50 :             ATExecGenericOptions(rel, (List *) cmd->def);
    5556          48 :             break;
    5557        2596 :         case AT_AttachPartition:
    5558        2596 :             cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
    5559             :                                       cur_pass, context);
    5560             :             Assert(cmd != NULL);
    5561        2566 :             if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
    5562        2182 :                 address = ATExecAttachPartition(wqueue, rel, (PartitionCmd *) cmd->def,
    5563             :                                                 context);
    5564             :             else
    5565         384 :                 address = ATExecAttachPartitionIdx(wqueue, rel,
    5566         384 :                                                    ((PartitionCmd *) cmd->def)->name);
    5567        2230 :             break;
    5568         522 :         case AT_DetachPartition:
    5569         522 :             cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
    5570             :                                       cur_pass, context);
    5571             :             Assert(cmd != NULL);
    5572             :             /* ATPrepCmd ensures it must be a table */
    5573             :             Assert(rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
    5574         516 :             address = ATExecDetachPartition(wqueue, tab, rel,
    5575         516 :                                             ((PartitionCmd *) cmd->def)->name,
    5576         516 :                                             ((PartitionCmd *) cmd->def)->concurrent);
    5577         386 :             break;
    5578          14 :         case AT_DetachPartitionFinalize:
    5579          14 :             address = ATExecDetachPartitionFinalize(rel, ((PartitionCmd *) cmd->def)->name);
    5580          14 :             break;
    5581         252 :         case AT_SplitPartition:
    5582         252 :             cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
    5583             :                                       cur_pass, context);
    5584             :             Assert(cmd != NULL);
    5585             :             Assert(rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
    5586         120 :             ATExecSplitPartition(wqueue, tab, rel, (PartitionCmd *) cmd->def,
    5587             :                                  context);
    5588         114 :             break;
    5589         120 :         case AT_MergePartitions:
    5590         120 :             cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
    5591             :                                       cur_pass, context);
    5592             :             Assert(cmd != NULL);
    5593             :             Assert(rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
    5594          66 :             ATExecMergePartitions(wqueue, tab, rel, (PartitionCmd *) cmd->def,
    5595             :                                   context);
    5596          66 :             break;
    5597           0 :         default:                /* oops */
    5598           0 :             elog(ERROR, "unrecognized alter table type: %d",
    5599             :                  (int) cmd->subtype);
    5600             :             break;
    5601             :     }
    5602             : 
    5603             :     /*
    5604             :      * Report the subcommand to interested event triggers.
    5605             :      */
    5606       55464 :     if (cmd)
    5607       42618 :         EventTriggerCollectAlterTableSubcmd((Node *) cmd, address);
    5608             : 
    5609             :     /*
    5610             :      * Bump the command counter to ensure the next subcommand in the sequence
    5611             :      * can see the changes so far
    5612             :      */
    5613       55464 :     CommandCounterIncrement();
    5614       55464 : }
    5615             : 
    5616             : /*
    5617             :  * ATParseTransformCmd: perform parse transformation for one subcommand
    5618             :  *
    5619             :  * Returns the transformed subcommand tree, if there is one, else NULL.
    5620             :  *
    5621             :  * The parser may hand back additional AlterTableCmd(s) and/or other
    5622             :  * utility statements, either before or after the original subcommand.
    5623             :  * Other AlterTableCmds are scheduled into the appropriate slot of the
    5624             :  * AlteredTableInfo (they had better be for later passes than the current one).
    5625             :  * Utility statements that are supposed to happen before the AlterTableCmd
    5626             :  * are executed immediately.  Those that are supposed to happen afterwards
    5627             :  * are added to the tab->afterStmts list to be done at the very end.
    5628             :  */
    5629             : static AlterTableCmd *
    5630       19554 : ATParseTransformCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
    5631             :                     AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode,
    5632             :                     AlterTablePass cur_pass, AlterTableUtilityContext *context)
    5633             : {
    5634       19554 :     AlterTableCmd *newcmd = NULL;
    5635       19554 :     AlterTableStmt *atstmt = makeNode(AlterTableStmt);
    5636             :     List       *beforeStmts;
    5637             :     List       *afterStmts;
    5638             :     ListCell   *lc;
    5639             : 
    5640             :     /* Gin up an AlterTableStmt with just this subcommand and this table */
    5641       19554 :     atstmt->relation =
    5642       19554 :         makeRangeVar(get_namespace_name(RelationGetNamespace(rel)),
    5643       19554 :                      pstrdup(RelationGetRelationName(rel)),
    5644             :                      -1);
    5645       19554 :     atstmt->relation->inh = recurse;
    5646       19554 :     atstmt->cmds = list_make1(cmd);
    5647       19554 :     atstmt->objtype = OBJECT_TABLE; /* needn't be picky here */
    5648       19554 :     atstmt->missing_ok = false;
    5649             : 
    5650             :     /* Transform the AlterTableStmt */
    5651       19554 :     atstmt = transformAlterTableStmt(RelationGetRelid(rel),
    5652             :                                      atstmt,
    5653             :                                      context->queryString,
    5654             :                                      &beforeStmts,
    5655             :                                      &afterStmts);
    5656             : 
    5657             :     /* Execute any statements that should happen before these subcommand(s) */
    5658       19738 :     foreach(lc, beforeStmts)
    5659             :     {
    5660         448 :         Node       *stmt = (Node *) lfirst(lc);
    5661             : 
    5662         448 :         ProcessUtilityForAlterTable(stmt, context);
    5663         436 :         CommandCounterIncrement();
    5664             :     }
    5665             : 
    5666             :     /* Examine the transformed subcommands and schedule them appropriately */
    5667       46048 :     foreach(lc, atstmt->cmds)
    5668             :     {
    5669       26758 :         AlterTableCmd *cmd2 = lfirst_node(AlterTableCmd, lc);
    5670             :         AlterTablePass pass;
    5671             : 
    5672             :         /*
    5673             :          * This switch need only cover the subcommand types that can be added
    5674             :          * by parse_utilcmd.c; otherwise, we'll use the default strategy of
    5675             :          * executing the subcommand immediately, as a substitute for the
    5676             :          * original subcommand.  (Note, however, that this does cause
    5677             :          * AT_AddConstraint subcommands to be rescheduled into later passes,
    5678             :          * which is important for index and foreign key constraints.)
    5679             :          *
    5680             :          * We assume we needn't do any phase-1 checks for added subcommands.
    5681             :          */
    5682       26758 :         switch (cmd2->subtype)
    5683             :         {
    5684        7150 :             case AT_SetAttNotNull:
    5685        7150 :                 ATSimpleRecursion(wqueue, rel, cmd2, recurse, lockmode, context);
    5686        7150 :                 pass = AT_PASS_COL_ATTRS;
    5687        7150 :                 break;
    5688        1042 :             case AT_AddIndex:
    5689             : 
    5690             :                 /*
    5691             :                  * A primary key on an inheritance parent needs supporting NOT
    5692             :                  * NULL constraint on its children; enqueue commands to create
    5693             :                  * those or mark them inherited if they already exist.
    5694             :                  */
    5695        1042 :                 ATPrepAddPrimaryKey(wqueue, rel, cmd2, lockmode, context);
    5696        1042 :                 pass = AT_PASS_ADD_INDEX;
    5697        1042 :                 break;
    5698        8434 :             case AT_AddIndexConstraint:
    5699             :                 /* as above */
    5700        8434 :                 ATPrepAddPrimaryKey(wqueue, rel, cmd2, lockmode, context);
    5701        8434 :                 pass = AT_PASS_ADD_INDEXCONSTR;
    5702        8434 :                 break;
    5703        3688 :             case AT_AddConstraint:
    5704             :                 /* Recursion occurs during execution phase */
    5705        3688 :                 if (recurse)
    5706        3638 :                     cmd2->recurse = true;
    5707        3688 :                 switch (castNode(Constraint, cmd2->def)->contype)
    5708             :                 {
    5709           0 :                     case CONSTR_PRIMARY:
    5710             :                     case CONSTR_UNIQUE:
    5711             :                     case CONSTR_EXCLUSION:
    5712           0 :                         pass = AT_PASS_ADD_INDEXCONSTR;
    5713           0 :                         break;
    5714        3688 :                     default:
    5715        3688 :                         pass = AT_PASS_ADD_OTHERCONSTR;
    5716        3688 :                         break;
    5717             :                 }
    5718        3688 :                 break;
    5719           0 :             case AT_AlterColumnGenericOptions:
    5720             :                 /* This command never recurses */
    5721             :                 /* No command-specific prep needed */
    5722           0 :                 pass = AT_PASS_MISC;
    5723           0 :                 break;
    5724        6444 :             default:
    5725        6444 :                 pass = cur_pass;
    5726        6444 :                 break;
    5727             :         }
    5728             : 
    5729       26758 :         if (pass < cur_pass)
    5730             :         {
    5731             :             /* Cannot schedule into a pass we already finished */
    5732           0 :             elog(ERROR, "ALTER TABLE scheduling failure: too late for pass %d",
    5733             :                  pass);
    5734             :         }
    5735       26758 :         else if (pass > cur_pass)
    5736             :         {
    5737             :             /* OK, queue it up for later */
    5738       20314 :             tab->subcmds[pass] = lappend(tab->subcmds[pass], cmd2);
    5739             :         }
    5740             :         else
    5741             :         {
    5742             :             /*
    5743             :              * We should see at most one subcommand for the current pass,
    5744             :              * which is the transformed version of the original subcommand.
    5745             :              */
    5746        6444 :             if (newcmd == NULL && cmd->subtype == cmd2->subtype)
    5747             :             {
    5748             :                 /* Found the transformed version of our subcommand */
    5749        6444 :                 newcmd = cmd2;
    5750             :             }
    5751             :             else
    5752           0 :                 elog(ERROR, "ALTER TABLE scheduling failure: bogus item for pass %d",
    5753             :                      pass);
    5754             :         }
    5755             :     }
    5756             : 
    5757             :     /* Queue up any after-statements to happen at the end */
    5758       19290 :     tab->afterStmts = list_concat(tab->afterStmts, afterStmts);
    5759             : 
    5760       19290 :     return newcmd;
    5761             : }
    5762             : 
    5763             : /*
    5764             :  * ATRewriteTables: ALTER TABLE phase 3
    5765             :  */
    5766             : static void
    5767       31748 : ATRewriteTables(AlterTableStmt *parsetree, List **wqueue, LOCKMODE lockmode,
    5768             :                 AlterTableUtilityContext *context)
    5769             : {
    5770             :     ListCell   *ltab;
    5771             : 
    5772             :     /* Go through each table that needs to be checked or rewritten */
    5773       66986 :     foreach(ltab, *wqueue)
    5774             :     {
    5775       35494 :         AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
    5776             : 
    5777             :         /* Relations without storage may be ignored here */
    5778       35494 :         if (!RELKIND_HAS_STORAGE(tab->relkind))
    5779        6154 :             continue;
    5780             : 
    5781             :         /*
    5782             :          * If we change column data types, the operation has to be propagated
    5783             :          * to tables that use this table's rowtype as a column type.
    5784             :          * tab->newvals will also be non-NULL in the case where we're adding a
    5785             :          * column with a default.  We choose to forbid that case as well,
    5786             :          * since composite types might eventually support defaults.
    5787             :          *
    5788             :          * (Eventually we'll probably need to check for composite type
    5789             :          * dependencies even when we're just scanning the table without a
    5790             :          * rewrite, but at the moment a composite type does not enforce any
    5791             :          * constraints, so it's not necessary/appropriate to enforce them just
    5792             :          * during ALTER.)
    5793             :          */
    5794       29340 :         if (tab->newvals != NIL || tab->rewrite > 0)
    5795             :         {
    5796             :             Relation    rel;
    5797             : 
    5798        1456 :             rel = table_open(tab->relid, NoLock);
    5799        1456 :             find_composite_type_dependencies(rel->rd_rel->reltype, rel, NULL);
    5800        1444 :             table_close(rel, NoLock);
    5801             :         }
    5802             : 
    5803             :         /*
    5804             :          * We only need to rewrite the table if at least one column needs to
    5805             :          * be recomputed, or we are changing its persistence or access method.
    5806             :          *
    5807             :          * There are two reasons for requiring a rewrite when changing
    5808             :          * persistence: on one hand, we need to ensure that the buffers
    5809             :          * belonging to each of the two relations are marked with or without
    5810             :          * BM_PERMANENT properly.  On the other hand, since rewriting creates
    5811             :          * and assigns a new relfilenumber, we automatically create or drop an
    5812             :          * init fork for the relation as appropriate.
    5813             :          */
    5814       29328 :         if (tab->rewrite > 0 && tab->relkind != RELKIND_SEQUENCE)
    5815         848 :         {
    5816             :             /* Build a temporary relation and copy data */
    5817             :             Relation    OldHeap;
    5818             :             Oid         OIDNewHeap;
    5819             :             Oid         NewAccessMethod;
    5820             :             Oid         NewTableSpace;
    5821             :             char        persistence;
    5822             : 
    5823         886 :             OldHeap = table_open(tab->relid, NoLock);
    5824             : 
    5825             :             /*
    5826             :              * We don't support rewriting of system catalogs; there are too
    5827             :              * many corner cases and too little benefit.  In particular this
    5828             :              * is certainly not going to work for mapped catalogs.
    5829             :              */
    5830         886 :             if (IsSystemRelation(OldHeap))
    5831           0 :                 ereport(ERROR,
    5832             :                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    5833             :                          errmsg("cannot rewrite system relation \"%s\"",
    5834             :                                 RelationGetRelationName(OldHeap))));
    5835             : 
    5836         886 :             if (RelationIsUsedAsCatalogTable(OldHeap))
    5837           2 :                 ereport(ERROR,
    5838             :                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    5839             :                          errmsg("cannot rewrite table \"%s\" used as a catalog table",
    5840             :                                 RelationGetRelationName(OldHeap))));
    5841             : 
    5842             :             /*
    5843             :              * Don't allow rewrite on temp tables of other backends ... their
    5844             :              * local buffer manager is not going to cope.
    5845             :              */
    5846         884 :             if (RELATION_IS_OTHER_TEMP(OldHeap))
    5847           0 :                 ereport(ERROR,
    5848             :                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    5849             :                          errmsg("cannot rewrite temporary tables of other sessions")));
    5850             : 
    5851             :             /*
    5852             :              * Select destination tablespace (same as original unless user
    5853             :              * requested a change)
    5854             :              */
    5855         884 :             if (tab->newTableSpace)
    5856           0 :                 NewTableSpace = tab->newTableSpace;
    5857             :             else
    5858         884 :                 NewTableSpace = OldHeap->rd_rel->reltablespace;
    5859             : 
    5860             :             /*
    5861             :              * Select destination access method (same as original unless user
    5862             :              * requested a change)
    5863             :              */
    5864         884 :             if (tab->chgAccessMethod)
    5865          36 :                 NewAccessMethod = tab->newAccessMethod;
    5866             :             else
    5867         848 :                 NewAccessMethod = OldHeap->rd_rel->relam;
    5868             : 
    5869             :             /*
    5870             :              * Select persistence of transient table (same as original unless
    5871             :              * user requested a change)
    5872             :              */
    5873         884 :             persistence = tab->chgPersistence ?
    5874         832 :                 tab->newrelpersistence : OldHeap->rd_rel->relpersistence;
    5875             : 
    5876         884 :             table_close(OldHeap, NoLock);
    5877             : 
    5878             :             /*
    5879             :              * Fire off an Event Trigger now, before actually rewriting the
    5880             :              * table.
    5881             :              *
    5882             :              * We don't support Event Trigger for nested commands anywhere,
    5883             :              * here included, and parsetree is given NULL when coming from
    5884             :              * AlterTableInternal.
    5885             :              *
    5886             :              * And fire it only once.
    5887             :              */
    5888         884 :             if (parsetree)
    5889         884 :                 EventTriggerTableRewrite((Node *) parsetree,
    5890             :                                          tab->relid,
    5891             :                                          tab->rewrite);
    5892             : 
    5893             :             /*
    5894             :              * Create transient table that will receive the modified data.
    5895             :              *
    5896             :              * Ensure it is marked correctly as logged or unlogged.  We have
    5897             :              * to do this here so that buffers for the new relfilenumber will
    5898             :              * have the right persistence set, and at the same time ensure
    5899             :              * that the original filenumbers's buffers will get read in with
    5900             :              * the correct setting (i.e. the original one).  Otherwise a
    5901             :              * rollback after the rewrite would possibly result with buffers
    5902             :              * for the original filenumbers having the wrong persistence
    5903             :              * setting.
    5904             :              *
    5905             :              * NB: This relies on swap_relation_files() also swapping the
    5906             :              * persistence. That wouldn't work for pg_class, but that can't be
    5907             :              * unlogged anyway.
    5908             :              */
    5909         878 :             OIDNewHeap = make_new_heap(tab->relid, NewTableSpace, NewAccessMethod,
    5910             :                                        persistence, lockmode);
    5911             : 
    5912             :             /*
    5913             :              * Copy the heap data into the new table with the desired
    5914             :              * modifications, and test the current data within the table
    5915             :              * against new constraints generated by ALTER TABLE commands.
    5916             :              */
    5917         878 :             ATRewriteTable(tab, OIDNewHeap, lockmode);
    5918             : 
    5919             :             /*
    5920             :              * Swap the physical files of the old and new heaps, then rebuild
    5921             :              * indexes and discard the old heap.  We can use RecentXmin for
    5922             :              * the table's new relfrozenxid because we rewrote all the tuples
    5923             :              * in ATRewriteTable, so no older Xid remains in the table.  Also,
    5924             :              * we never try to swap toast tables by content, since we have no
    5925             :              * interest in letting this code work on system catalogs.
    5926             :              */
    5927         854 :             finish_heap_swap(tab->relid, OIDNewHeap,
    5928             :                              false, false, true,
    5929         854 :                              !OidIsValid(tab->newTableSpace),
    5930             :                              RecentXmin,
    5931             :                              ReadNextMultiXactId(),
    5932             :                              persistence);
    5933             : 
    5934         848 :             InvokeObjectPostAlterHook(RelationRelationId, tab->relid, 0);
    5935             :         }
    5936       28442 :         else if (tab->rewrite > 0 && tab->relkind == RELKIND_SEQUENCE)
    5937             :         {
    5938          12 :             if (tab->chgPersistence)
    5939          12 :                 SequenceChangePersistence(tab->relid, tab->newrelpersistence);
    5940             :         }
    5941             :         else
    5942             :         {
    5943             :             /*
    5944             :              * If required, test the current data within the table against new
    5945             :              * constraints generated by ALTER TABLE commands, but don't
    5946             :              * rebuild data.
    5947             :              */
    5948       28430 :             if (tab->constraints != NIL || tab->verify_new_notnull ||
    5949       25674 :                 tab->partition_constraint != NULL)
    5950        4540 :                 ATRewriteTable(tab, InvalidOid, lockmode);
    5951             : 
    5952             :             /*
    5953             :              * If we had SET TABLESPACE but no reason to reconstruct tuples,
    5954             :              * just do a block-by-block copy.
    5955             :              */
    5956       28224 :             if (tab->newTableSpace)
    5957         122 :                 ATExecSetTableSpace(tab->relid, tab->newTableSpace, lockmode);
    5958             :         }
    5959             : 
    5960             :         /*
    5961             :          * Also change persistence of owned sequences, so that it matches the
    5962             :          * table persistence.
    5963             :          */
    5964       29084 :         if (tab->chgPersistence)
    5965             :         {
    5966          64 :             List       *seqlist = getOwnedSequences(tab->relid);
    5967             :             ListCell   *lc;
    5968             : 
    5969         112 :             foreach(lc, seqlist)
    5970             :             {
    5971          48 :                 Oid         seq_relid = lfirst_oid(lc);
    5972             : 
    5973          48 :                 SequenceChangePersistence(seq_relid, tab->newrelpersistence);
    5974             :             }
    5975             :         }
    5976             :     }
    5977             : 
    5978             :     /*
    5979             :      * Foreign key constraints are checked in a final pass, since (a) it's
    5980             :      * generally best to examine each one separately, and (b) it's at least
    5981             :      * theoretically possible that we have changed both relations of the
    5982             :      * foreign key, and we'd better have finished both rewrites before we try
    5983             :      * to read the tables.
    5984             :      */
    5985       66514 :     foreach(ltab, *wqueue)
    5986             :     {
    5987       35102 :         AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
    5988       35102 :         Relation    rel = NULL;
    5989             :         ListCell   *lcon;
    5990             : 
    5991             :         /* Relations without storage may be ignored here too */
    5992       35102 :         if (!RELKIND_HAS_STORAGE(tab->relkind))
    5993        6074 :             continue;
    5994             : 
    5995       30684 :         foreach(lcon, tab->constraints)
    5996             :         {
    5997        1736 :             NewConstraint *con = lfirst(lcon);
    5998             : 
    5999        1736 :             if (con->contype == CONSTR_FOREIGN)
    6000             :             {
    6001        1084 :                 Constraint *fkconstraint = (Constraint *) con->qual;
    6002             :                 Relation    refrel;
    6003             : 
    6004        1084 :                 if (rel == NULL)
    6005             :                 {
    6006             :                     /* Long since locked, no need for another */
    6007        1072 :                     rel = table_open(tab->relid, NoLock);
    6008             :                 }
    6009             : 
    6010        1084 :                 refrel = table_open(con->refrelid, RowShareLock);
    6011             : 
    6012        1084 :                 validateForeignKeyConstraint(fkconstraint->conname, rel, refrel,
    6013             :                                              con->refindid,
    6014             :                                              con->conid,
    6015        1084 :                                              con->conwithperiod);
    6016             : 
    6017             :                 /*
    6018             :                  * No need to mark the constraint row as validated, we did
    6019             :                  * that when we inserted the row earlier.
    6020             :                  */
    6021             : 
    6022        1004 :                 table_close(refrel, NoLock);
    6023             :             }
    6024             :         }
    6025             : 
    6026       28948 :         if (rel)
    6027         992 :             table_close(rel, NoLock);
    6028             :     }
    6029             : 
    6030             :     /* Finally, run any afterStmts that were queued up */
    6031       66396 :     foreach(ltab, *wqueue)
    6032             :     {
    6033       34984 :         AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
    6034             :         ListCell   *lc;
    6035             : 
    6036       35070 :         foreach(lc, tab->afterStmts)
    6037             :         {
    6038          86 :             Node       *stmt = (Node *) lfirst(lc);
    6039             : 
    6040          86 :             ProcessUtilityForAlterTable(stmt, context);
    6041          86 :             CommandCounterIncrement();
    6042             :         }
    6043             :     }
    6044       31412 : }
    6045             : 
    6046             : /*
    6047             :  * ATRewriteTable: scan or rewrite one table
    6048             :  *
    6049             :  * OIDNewHeap is InvalidOid if we don't need to rewrite
    6050             :  */
    6051             : static void
    6052        5418 : ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
    6053             : {
    6054             :     Relation    oldrel;
    6055             :     Relation    newrel;
    6056             :     TupleDesc   oldTupDesc;
    6057             :     TupleDesc   newTupDesc;
    6058        5418 :     bool        needscan = false;
    6059             :     List       *notnull_attrs;
    6060             :     int         i;
    6061             :     ListCell   *l;
    6062             :     EState     *estate;
    6063             :     CommandId   mycid;
    6064             :     BulkInsertState bistate;
    6065             :     int         ti_options;
    6066        5418 :     ExprState  *partqualstate = NULL;
    6067             : 
    6068             :     /*
    6069             :      * Open the relation(s).  We have surely already locked the existing
    6070             :      * table.
    6071             :      */
    6072        5418 :     oldrel = table_open(tab->relid, NoLock);
    6073        5418 :     oldTupDesc = tab->oldDesc;
    6074        5418 :     newTupDesc = RelationGetDescr(oldrel);  /* includes all mods */
    6075             : 
    6076        5418 :     if (OidIsValid(OIDNewHeap))
    6077         878 :         newrel = table_open(OIDNewHeap, lockmode);
    6078             :     else
    6079        4540 :         newrel = NULL;
    6080             : 
    6081             :     /*
    6082             :      * Prepare a BulkInsertState and options for table_tuple_insert.  The FSM
    6083             :      * is empty, so don't bother using it.
    6084             :      */
    6085        5418 :     if (newrel)
    6086             :     {
    6087         878 :         mycid = GetCurrentCommandId(true);
    6088         878 :         bistate = GetBulkInsertState();
    6089         878 :         ti_options = TABLE_INSERT_SKIP_FSM;
    6090             :     }
    6091             :     else
    6092             :     {
    6093             :         /* keep compiler quiet about using these uninitialized */
    6094        4540 :         mycid = 0;
    6095        4540 :         bistate = NULL;
    6096        4540 :         ti_options = 0;
    6097             :     }
    6098             : 
    6099             :     /*
    6100             :      * Generate the constraint and default execution states
    6101             :      */
    6102             : 
    6103        5418 :     estate = CreateExecutorState();
    6104             : 
    6105             :     /* Build the needed expression execution states */
    6106        7250 :     foreach(l, tab->constraints)
    6107             :     {
    6108        1832 :         NewConstraint *con = lfirst(l);
    6109             : 
    6110        1832 :         switch (con->contype)
    6111             :         {
    6112         742 :             case CONSTR_CHECK:
    6113         742 :                 needscan = true;
    6114         742 :                 con->qualstate = ExecPrepareExpr((Expr *) con->qual, estate);
    6115         742 :                 break;
    6116        1090 :             case CONSTR_FOREIGN:
    6117             :                 /* Nothing to do here */
    6118        1090 :                 break;
    6119           0 :             default:
    6120           0 :                 elog(ERROR, "unrecognized constraint type: %d",
    6121             :                      (int) con->contype);
    6122             :         }
    6123             :     }
    6124             : 
    6125             :     /* Build expression execution states for partition check quals */
    6126        5418 :     if (tab->partition_constraint)
    6127             :     {
    6128        1926 :         needscan = true;
    6129        1926 :         partqualstate = ExecPrepareExpr(tab->partition_constraint, estate);
    6130             :     }
    6131             : 
    6132        6322 :     foreach(l, tab->newvals)
    6133             :     {
    6134         904 :         NewColumnValue *ex = lfirst(l);
    6135             : 
    6136             :         /* expr already planned */
    6137         904 :         ex->exprstate = ExecInitExpr((Expr *) ex->expr, NULL);
    6138             :     }
    6139             : 
    6140        5418 :     notnull_attrs = NIL;
    6141        5418 :     if (newrel || tab->verify_new_notnull)
    6142             :     {
    6143             :         /*
    6144             :          * If we are rebuilding the tuples OR if we added any new but not
    6145             :          * verified not-null constraints, check all not-null constraints. This
    6146             :          * is a bit of overkill but it minimizes risk of bugs, and
    6147             :          * heap_attisnull is a pretty cheap test anyway.
    6148             :          */
    6149        7174 :         for (i = 0; i < newTupDesc->natts; i++)
    6150             :         {
    6151        5226 :             Form_pg_attribute attr = TupleDescAttr(newTupDesc, i);
    6152             : 
    6153        5226 :             if (attr->attnotnull && !attr->attisdropped)
    6154        2100 :                 notnull_attrs = lappend_int(notnull_attrs, i);
    6155             :         }
    6156        1948 :         if (notnull_attrs)
    6157        1540 :             needscan = true;
    6158             :     }
    6159             : 
    6160        5418 :     if (newrel || needscan)
    6161             :     {
    6162             :         ExprContext *econtext;
    6163             :         TupleTableSlot *oldslot;
    6164             :         TupleTableSlot *newslot;
    6165             :         TableScanDesc scan;
    6166             :         MemoryContext oldCxt;
    6167        4512 :         List       *dropped_attrs = NIL;
    6168             :         ListCell   *lc;
    6169             :         Snapshot    snapshot;
    6170             : 
    6171        4512 :         if (newrel)
    6172         878 :             ereport(DEBUG1,
    6173             :                     (errmsg_internal("rewriting table \"%s\"",
    6174             :                                      RelationGetRelationName(oldrel))));
    6175             :         else
    6176        3634 :             ereport(DEBUG1,
    6177             :                     (errmsg_internal("verifying table \"%s\"",
    6178             :                                      RelationGetRelationName(oldrel))));
    6179             : 
    6180        4512 :         if (newrel)
    6181             :         {
    6182             :             /*
    6183             :              * All predicate locks on the tuples or pages are about to be made
    6184             :              * invalid, because we move tuples around.  Promote them to
    6185             :              * relation locks.
    6186             :              */
    6187         878 :             TransferPredicateLocksToHeapRelation(oldrel);
    6188             :         }
    6189             : 
    6190        4512 :         econtext = GetPerTupleExprContext(estate);
    6191             : 
    6192             :         /*
    6193             :          * Create necessary tuple slots. When rewriting, two slots are needed,
    6194             :          * otherwise one suffices. In the case where one slot suffices, we
    6195             :          * need to use the new tuple descriptor, otherwise some constraints
    6196             :          * can't be evaluated.  Note that even when the tuple layout is the
    6197             :          * same and no rewrite is required, the tupDescs might not be
    6198             :          * (consider ADD COLUMN without a default).
    6199             :          */
    6200        4512 :         if (tab->rewrite)
    6201             :         {
    6202             :             Assert(newrel != NULL);
    6203         878 :             oldslot = MakeSingleTupleTableSlot(oldTupDesc,
    6204             :                                                table_slot_callbacks(oldrel));
    6205         878 :             newslot = MakeSingleTupleTableSlot(newTupDesc,
    6206             :                                                table_slot_callbacks(newrel));
    6207             : 
    6208             :             /*
    6209             :              * Set all columns in the new slot to NULL initially, to ensure
    6210             :              * columns added as part of the rewrite are initialized to NULL.
    6211             :              * That is necessary as tab->newvals will not contain an
    6212             :              * expression for columns with a NULL default, e.g. when adding a
    6213             :              * column without a default together with a column with a default
    6214             :              * requiring an actual rewrite.
    6215             :              */
    6216         878 :             ExecStoreAllNullTuple(newslot);
    6217             :         }
    6218             :         else
    6219             :         {
    6220        3634 :             oldslot = MakeSingleTupleTableSlot(newTupDesc,
    6221             :                                                table_slot_callbacks(oldrel));
    6222        3634 :             newslot = NULL;
    6223             :         }
    6224             : 
    6225             :         /*
    6226             :          * Any attributes that are dropped according to the new tuple
    6227             :          * descriptor can be set to NULL. We precompute the list of dropped
    6228             :          * attributes to avoid needing to do so in the per-tuple loop.
    6229             :          */
    6230       16008 :         for (i = 0; i < newTupDesc->natts; i++)
    6231             :         {
    6232       11496 :             if (TupleDescAttr(newTupDesc, i)->attisdropped)
    6233         790 :                 dropped_attrs = lappend_int(dropped_attrs, i);
    6234             :         }
    6235             : 
    6236             :         /*
    6237             :          * Scan through the rows, generating a new row if needed and then
    6238             :          * checking all the constraints.
    6239             :          */
    6240        4512 :         snapshot = RegisterSnapshot(GetLatestSnapshot());
    6241        4512 :         scan = table_beginscan(oldrel, snapshot, 0, NULL);
    6242             : 
    6243             :         /*
    6244             :          * Switch to per-tuple memory context and reset it for each tuple
    6245             :          * produced, so we don't leak memory.
    6246             :          */
    6247        4512 :         oldCxt = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
    6248             : 
    6249      767092 :         while (table_scan_getnextslot(scan, ForwardScanDirection, oldslot))
    6250             :         {
    6251             :             TupleTableSlot *insertslot;
    6252             : 
    6253      762810 :             if (tab->rewrite > 0)
    6254             :             {
    6255             :                 /* Extract data from old tuple */
    6256       97564 :                 slot_getallattrs(oldslot);
    6257       97564 :                 ExecClearTuple(newslot);
    6258             : 
    6259             :                 /* copy attributes */
    6260       97564 :                 memcpy(newslot->tts_values, oldslot->tts_values,
    6261       97564 :                        sizeof(Datum) * oldslot->tts_nvalid);
    6262       97564 :                 memcpy(newslot->tts_isnull, oldslot->tts_isnull,
    6263       97564 :                        sizeof(bool) * oldslot->tts_nvalid);
    6264             : 
    6265             :                 /* Set dropped attributes to null in new tuple */
    6266       97650 :                 foreach(lc, dropped_attrs)
    6267          86 :                     newslot->tts_isnull[lfirst_int(lc)] = true;
    6268             : 
    6269             :                 /*
    6270             :                  * Constraints and GENERATED expressions might reference the
    6271             :                  * tableoid column, so fill tts_tableOid with the desired
    6272             :                  * value.  (We must do this each time, because it gets
    6273             :                  * overwritten with newrel's OID during storing.)
    6274             :                  */
    6275       97564 :                 newslot->tts_tableOid = RelationGetRelid(oldrel);
    6276             : 
    6277             :                 /*
    6278             :                  * Process supplied expressions to replace selected columns.
    6279             :                  *
    6280             :                  * First, evaluate expressions whose inputs come from the old
    6281             :                  * tuple.
    6282             :                  */
    6283       97564 :                 econtext->ecxt_scantuple = oldslot;
    6284             : 
    6285      200948 :                 foreach(l, tab->newvals)
    6286             :                 {
    6287      103396 :                     NewColumnValue *ex = lfirst(l);
    6288             : 
    6289      103396 :                     if (ex->is_generated)
    6290         150 :                         continue;
    6291             : 
    6292      103246 :                     newslot->tts_values[ex->attnum - 1]
    6293      103234 :                         = ExecEvalExpr(ex->exprstate,
    6294             :                                        econtext,
    6295      103246 :                                        &newslot->tts_isnull[ex->attnum - 1]);
    6296             :                 }
    6297             : 
    6298       97552 :                 ExecStoreVirtualTuple(newslot);
    6299             : 
    6300             :                 /*
    6301             :                  * Now, evaluate any expressions whose inputs come from the
    6302             :                  * new tuple.  We assume these columns won't reference each
    6303             :                  * other, so that there's no ordering dependency.
    6304             :                  */
    6305       97552 :                 econtext->ecxt_scantuple = newslot;
    6306             : 
    6307      200936 :                 foreach(l, tab->newvals)
    6308             :                 {
    6309      103384 :                     NewColumnValue *ex = lfirst(l);
    6310             : 
    6311      103384 :                     if (!ex->is_generated)
    6312      103234 :                         continue;
    6313             : 
    6314         150 :                     newslot->tts_values[ex->attnum - 1]
    6315         150 :                         = ExecEvalExpr(ex->exprstate,
    6316             :                                        econtext,
    6317         150 :                                        &newslot->tts_isnull[ex->attnum - 1]);
    6318             :                 }
    6319             : 
    6320       97552 :                 insertslot = newslot;
    6321             :             }
    6322             :             else
    6323             :             {
    6324             :                 /*
    6325             :                  * If there's no rewrite, old and new table are guaranteed to
    6326             :                  * have the same AM, so we can just use the old slot to verify
    6327             :                  * new constraints etc.
    6328             :                  */
    6329      665246 :                 insertslot = oldslot;
    6330             :             }
    6331             : 
    6332             :             /* Now check any constraints on the possibly-changed tuple */
    6333      762798 :             econtext->ecxt_scantuple = insertslot;
    6334             : 
    6335     3339112 :             foreach(l, notnull_attrs)
    6336             :             {
    6337     2576386 :                 int         attn = lfirst_int(l);
    6338             : 
    6339     2576386 :                 if (slot_attisnull(insertslot, attn + 1))
    6340             :                 {
    6341          72 :                     Form_pg_attribute attr = TupleDescAttr(newTupDesc, attn);
    6342             : 
    6343          72 :                     ereport(ERROR,
    6344             :                             (errcode(ERRCODE_NOT_NULL_VIOLATION),
    6345             :                              errmsg("column \"%s\" of relation \"%s\" contains null values",
    6346             :                                     NameStr(attr->attname),
    6347             :                                     RelationGetRelationName(oldrel)),
    6348             :                              errtablecol(oldrel, attn + 1)));
    6349             :                 }
    6350             :             }
    6351             : 
    6352      770826 :             foreach(l, tab->constraints)
    6353             :             {
    6354        8172 :                 NewConstraint *con = lfirst(l);
    6355             : 
    6356        8172 :                 switch (con->contype)
    6357             :                 {
    6358        8072 :                     case CONSTR_CHECK:
    6359        8072 :                         if (!ExecCheck(con->qualstate, econtext))
    6360          72 :                             ereport(ERROR,
    6361             :                                     (errcode(ERRCODE_CHECK_VIOLATION),
    6362             :                                      errmsg("check constraint \"%s\" of relation \"%s\" is violated by some row",
    6363             :                                             con->name,
    6364             :                                             RelationGetRelationName(oldrel)),
    6365             :                                      errtableconstraint(oldrel, con->name)));
    6366        8000 :                         break;
    6367         100 :                     case CONSTR_NOTNULL:
    6368             :                     case CONSTR_FOREIGN:
    6369             :                         /* Nothing to do here */
    6370         100 :                         break;
    6371           0 :                     default:
    6372           0 :                         elog(ERROR, "unrecognized constraint type: %d",
    6373             :                              (int) con->contype);
    6374             :                 }
    6375             :             }
    6376             : 
    6377      762654 :             if (partqualstate && !ExecCheck(partqualstate, econtext))
    6378             :             {
    6379          74 :                 if (tab->validate_default)
    6380          26 :                     ereport(ERROR,
    6381             :                             (errcode(ERRCODE_CHECK_VIOLATION),
    6382             :                              errmsg("updated partition constraint for default partition \"%s\" would be violated by some row",
    6383             :                                     RelationGetRelationName(oldrel)),
    6384             :                              errtable(oldrel)));
    6385             :                 else
    6386          48 :                     ereport(ERROR,
    6387             :                             (errcode(ERRCODE_CHECK_VIOLATION),
    6388             :                              errmsg("partition constraint of relation \"%s\" is violated by some row",
    6389             :                                     RelationGetRelationName(oldrel)),
    6390             :                              errtable(oldrel)));
    6391             :             }
    6392             : 
    6393             :             /* Write the tuple out to the new relation */
    6394      762580 :             if (newrel)
    6395       97540 :                 table_tuple_insert(newrel, insertslot, mycid,
    6396             :                                    ti_options, bistate);
    6397             : 
    6398      762580 :             ResetExprContext(econtext);
    6399             : 
    6400      762580 :             CHECK_FOR_INTERRUPTS();
    6401             :         }
    6402             : 
    6403        4282 :         MemoryContextSwitchTo(oldCxt);
    6404        4282 :         table_endscan(scan);
    6405        4282 :         UnregisterSnapshot(snapshot);
    6406             : 
    6407        4282 :         ExecDropSingleTupleTableSlot(oldslot);
    6408        4282 :         if (newslot)
    6409         854 :             ExecDropSingleTupleTableSlot(newslot);
    6410             :     }
    6411             : 
    6412        5188 :     FreeExecutorState(estate);
    6413             : 
    6414        5188 :     table_close(oldrel, NoLock);
    6415        5188 :     if (newrel)
    6416             :     {
    6417         854 :         FreeBulkInsertState(bistate);
    6418             : 
    6419         854 :         table_finish_bulk_insert(newrel, ti_options);
    6420             : 
    6421         854 :         table_close(newrel, NoLock);
    6422             :     }
    6423        5188 : }
    6424             : 
    6425             : /*
    6426             :  * ATGetQueueEntry: find or create an entry in the ALTER TABLE work queue
    6427             :  */
    6428             : static AlteredTableInfo *
    6429       43882 : ATGetQueueEntry(List **wqueue, Relation rel)
    6430             : {
    6431       43882 :     Oid         relid = RelationGetRelid(rel);
    6432             :     AlteredTableInfo *tab;
    6433             :     ListCell   *ltab;
    6434             : 
    6435       53076 :     foreach(ltab, *wqueue)
    6436             :     {
    6437       14484 :         tab = (AlteredTableInfo *) lfirst(ltab);
    6438       14484 :         if (tab->relid == relid)
    6439        5290 :             return tab;
    6440             :     }
    6441             : 
    6442             :     /*
    6443             :      * Not there, so add it.  Note that we make a copy of the relation's
    6444             :      * existing descriptor before anything interesting can happen to it.
    6445             :      */
    6446       38592 :     tab = (AlteredTableInfo *) palloc0(sizeof(AlteredTableInfo));
    6447       38592 :     tab->relid = relid;
    6448       38592 :     tab->rel = NULL;         /* set later */
    6449       38592 :     tab->relkind = rel->rd_rel->relkind;
    6450       38592 :     tab->oldDesc = CreateTupleDescCopyConstr(RelationGetDescr(rel));
    6451       38592 :     tab->newAccessMethod = InvalidOid;
    6452       38592 :     tab->chgAccessMethod = false;
    6453       38592 :     tab->newTableSpace = InvalidOid;
    6454       38592 :     tab->newrelpersistence = RELPERSISTENCE_PERMANENT;
    6455       38592 :     tab->chgPersistence = false;
    6456             : 
    6457       38592 :     *wqueue = lappend(*wqueue, tab);
    6458             : 
    6459       38592 :     return tab;
    6460             : }
    6461             : 
    6462             : static const char *
    6463          42 : alter_table_type_to_string(AlterTableType cmdtype)
    6464             : {
    6465          42 :     switch (cmdtype)
    6466             :     {
    6467           0 :         case AT_AddColumn:
    6468             :         case AT_AddColumnToView:
    6469           0 :             return "ADD COLUMN";
    6470           0 :         case AT_ColumnDefault:
    6471             :         case AT_CookedColumnDefault:
    6472           0 :             return "ALTER COLUMN ... SET DEFAULT";
    6473           6 :         case AT_DropNotNull:
    6474           6 :             return "ALTER COLUMN ... DROP NOT NULL";
    6475           6 :         case AT_SetNotNull:
    6476           6 :             return "ALTER COLUMN ... SET NOT NULL";
    6477           0 :         case AT_SetAttNotNull:
    6478           0 :             return NULL;        /* not real grammar */
    6479           0 :         case AT_SetExpression:
    6480           0 :             return "ALTER COLUMN ... SET EXPRESSION";
    6481           0 :         case AT_DropExpression:
    6482           0 :             return "ALTER COLUMN ... DROP EXPRESSION";
    6483           0 :         case AT_SetStatistics:
    6484           0 :             return "ALTER COLUMN ... SET STATISTICS";
    6485          12 :         case AT_SetOptions:
    6486          12 :             return "ALTER COLUMN ... SET";
    6487           0 :         case AT_ResetOptions:
    6488           0 :             return "ALTER COLUMN ... RESET";
    6489           0 :         case AT_SetStorage:
    6490           0 :             return "ALTER COLUMN ... SET STORAGE";
    6491           0 :         case AT_SetCompression:
    6492           0 :             return "ALTER COLUMN ... SET COMPRESSION";
    6493           6 :         case AT_DropColumn:
    6494           6 :             return "DROP COLUMN";
    6495           0 :         case AT_AddIndex:
    6496             :         case AT_ReAddIndex:
    6497           0 :             return NULL;        /* not real grammar */
    6498           0 :         case AT_AddConstraint:
    6499             :         case AT_ReAddConstraint:
    6500             :         case AT_ReAddDomainConstraint:
    6501             :         case AT_AddIndexConstraint:
    6502           0 :             return "ADD CONSTRAINT";
    6503           6 :         case AT_AlterConstraint:
    6504           6 :             return "ALTER CONSTRAINT";
    6505           0 :         case AT_ValidateConstraint:
    6506           0 :             return "VALIDATE CONSTRAINT";
    6507           0 :         case AT_DropConstraint:
    6508           0 :             return "DROP CONSTRAINT";
    6509           0 :         case AT_ReAddComment:
    6510           0 :             return NULL;        /* not real grammar */
    6511           0 :         case AT_AlterColumnType:
    6512           0 :             return "ALTER COLUMN ... SET DATA TYPE";
    6513           0 :         case AT_AlterColumnGenericOptions:
    6514           0 :             return "ALTER COLUMN ... OPTIONS";
    6515           0 :         case AT_ChangeOwner:
    6516           0 :             return "OWNER TO";
    6517           0 :         case AT_ClusterOn:
    6518           0 :             return "CLUSTER ON";
    6519           0 :         case AT_DropCluster:
    6520           0 :             return "SET WITHOUT CLUSTER";
    6521           0 :         case AT_SetAccessMethod:
    6522           0 :             return "SET ACCESS METHOD";
    6523           0 :         case AT_SetLogged:
    6524           0 :             return "SET LOGGED";
    6525           0 :         case AT_SetUnLogged:
    6526           0 :             return "SET UNLOGGED";
    6527           0 :         case AT_DropOids:
    6528           0 :             return "SET WITHOUT OIDS";
    6529           0 :         case AT_SetTableSpace:
    6530           0 :             return "SET TABLESPACE";
    6531           0 :         case AT_SetRelOptions:
    6532           0 :             return "SET";
    6533           0 :         case AT_ResetRelOptions:
    6534           0 :             return "RESET";
    6535           0 :         case AT_ReplaceRelOptions:
    6536           0 :             return NULL;        /* not real grammar */
    6537           0 :         case AT_EnableTrig:
    6538           0 :             return "ENABLE TRIGGER";
    6539           0 :         case AT_EnableAlwaysTrig:
    6540           0 :             return "ENABLE ALWAYS TRIGGER";
    6541           0 :         case AT_EnableReplicaTrig:
    6542           0 :             return "ENABLE REPLICA TRIGGER";
    6543           0 :         case AT_DisableTrig:
    6544           0 :             return "DISABLE TRIGGER";
    6545           0 :         case AT_EnableTrigAll:
    6546           0 :             return "ENABLE TRIGGER ALL";
    6547           0 :         case AT_DisableTrigAll:
    6548           0 :             return "DISABLE TRIGGER ALL";
    6549           0 :         case AT_EnableTrigUser:
    6550           0 :             return "ENABLE TRIGGER USER";
    6551           0 :         case AT_DisableTrigUser:
    6552           0 :             return "DISABLE TRIGGER USER";
    6553           0 :         case AT_EnableRule:
    6554           0 :             return "ENABLE RULE";
    6555           0 :         case AT_EnableAlwaysRule:
    6556           0 :             return "ENABLE ALWAYS RULE";
    6557           0 :         case AT_EnableReplicaRule:
    6558           0 :             return "ENABLE REPLICA RULE";
    6559           0 :         case AT_DisableRule:
    6560           0 :             return "DISABLE RULE";
    6561           0 :         case AT_AddInherit:
    6562           0 :             return "INHERIT";
    6563           0 :         case AT_DropInherit:
    6564           0 :             return "NO INHERIT";
    6565           0 :         case AT_AddOf:
    6566           0 :             return "OF";
    6567           0 :         case AT_DropOf:
    6568           0 :             return "NOT OF";
    6569           0 :         case AT_ReplicaIdentity:
    6570           0 :             return "REPLICA IDENTITY";
    6571           0 :         case AT_EnableRowSecurity:
    6572           0 :             return "ENABLE ROW SECURITY";
    6573           0 :         case AT_DisableRowSecurity:
    6574           0 :             return "DISABLE ROW SECURITY";
    6575           0 :         case AT_ForceRowSecurity:
    6576           0 :             return "FORCE ROW SECURITY";
    6577           0 :         case AT_NoForceRowSecurity:
    6578           0 :             return "NO FORCE ROW SECURITY";
    6579           0 :         case AT_GenericOptions:
    6580           0 :             return "OPTIONS";
    6581           0 :         case AT_AttachPartition:
    6582           0 :             return "ATTACH PARTITION";
    6583           6 :         case AT_DetachPartition:
    6584           6 :             return "DETACH PARTITION";
    6585           0 :         case AT_DetachPartitionFinalize:
    6586           0 :             return "DETACH PARTITION ... FINALIZE";
    6587           0 :         case AT_SplitPartition:
    6588           0 :             return "SPLIT PARTITION";
    6589           0 :         case AT_MergePartitions:
    6590           0 :             return "MERGE PARTITIONS";
    6591           0 :         case AT_AddIdentity:
    6592           0 :             return "ALTER COLUMN ... ADD IDENTITY";
    6593           0 :         case AT_SetIdentity:
    6594           0 :             return "ALTER COLUMN ... SET";
    6595           0 :         case AT_DropIdentity:
    6596           0 :             return "ALTER COLUMN ... DROP IDENTITY";
    6597           0 :         case AT_ReAddStatistics:
    6598           0 :             return NULL;        /* not real grammar */
    6599             :     }
    6600             : 
    6601           0 :     return NULL;
    6602             : }
    6603             : 
    6604             : /*
    6605             :  * ATSimplePermissions
    6606             :  *
    6607             :  * - Ensure that it is a relation (or possibly a view)
    6608             :  * - Ensure this user is the owner
    6609             :  * - Ensure that it is not a system table
    6610             :  */
    6611             : static void
    6612       40046 : ATSimplePermissions(AlterTableType cmdtype, Relation rel, int allowed_targets)
    6613             : {
    6614             :     int         actual_target;
    6615             : 
    6616       40046 :     switch (rel->rd_rel->relkind)
    6617             :     {
    6618       37840 :         case RELKIND_RELATION:
    6619             :         case RELKIND_PARTITIONED_TABLE:
    6620       37840 :             actual_target = ATT_TABLE;
    6621       37840 :             break;
    6622         396 :         case RELKIND_VIEW:
    6623         396 :             actual_target = ATT_VIEW;
    6624         396 :             break;
    6625          46 :         case RELKIND_MATVIEW:
    6626          46 :             actual_target = ATT_MATVIEW;
    6627          46 :             break;
    6628         226 :         case RELKIND_INDEX:
    6629         226 :             actual_target = ATT_INDEX;
    6630         226 :             break;
    6631         426 :         case RELKIND_PARTITIONED_INDEX:
    6632         426 :             actual_target = ATT_PARTITIONED_INDEX;
    6633         426 :             break;
    6634         214 :         case RELKIND_COMPOSITE_TYPE:
    6635         214 :             actual_target = ATT_COMPOSITE_TYPE;
    6636         214 :             break;
    6637         886 :         case RELKIND_FOREIGN_TABLE:
    6638         886 :             actual_target = ATT_FOREIGN_TABLE;
    6639         886 :             break;
    6640          12 :         case RELKIND_SEQUENCE:
    6641          12 :             actual_target = ATT_SEQUENCE;
    6642          12 :             break;
    6643           0 :         default:
    6644           0 :             actual_target = 0;
    6645           0 :             break;
    6646             :     }
    6647             : 
    6648             :     /* Wrong target type? */
    6649       40046 :     if ((actual_target & allowed_targets) == 0)
    6650             :     {
    6651          42 :         const char *action_str = alter_table_type_to_string(cmdtype);
    6652             : 
    6653          42 :         if (action_str)
    6654          42 :             ereport(ERROR,
    6655             :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    6656             :             /* translator: %s is a group of some SQL keywords */
    6657             :                      errmsg("ALTER action %s cannot be performed on relation \"%s\"",
    6658             :                             action_str, RelationGetRelationName(rel)),
    6659             :                      errdetail_relkind_not_supported(rel->rd_rel->relkind)));
    6660             :         else
    6661             :             /* internal error? */
    6662           0 :             elog(ERROR, "invalid ALTER action attempted on relation \"%s\"",
    6663             :                  RelationGetRelationName(rel));
    6664             :     }
    6665             : 
    6666             :     /* Permissions checks */
    6667       40004 :     if (!object_ownercheck(RelationRelationId, RelationGetRelid(rel), GetUserId()))
    6668          12 :         aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(rel->rd_rel->relkind),
    6669          12 :                        RelationGetRelationName(rel));
    6670             : 
    6671       39992 :     if (!allowSystemTableMods && IsSystemRelation(rel))
    6672           0 :         ereport(ERROR,
    6673             :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
    6674             :                  errmsg("permission denied: \"%s\" is a system catalog",
    6675             :                         RelationGetRelationName(rel))));
    6676       39992 : }
    6677             : 
    6678             : /*
    6679             :  * ATSimpleRecursion
    6680             :  *
    6681             :  * Simple table recursion sufficient for most ALTER TABLE operations.
    6682             :  * All direct and indirect children are processed in an unspecified order.
    6683             :  * Note that if a child inherits from the original table via multiple
    6684             :  * inheritance paths, it will be visited just once.
    6685             :  */
    6686             : static void
    6687       15616 : ATSimpleRecursion(List **wqueue, Relation rel,
    6688             :                   AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode,
    6689             :                   AlterTableUtilityContext *context)
    6690             : {
    6691             :     /*
    6692             :      * Propagate to children, if desired and if there are (or might be) any
    6693             :      * children.
    6694             :      */
    6695       15616 :     if (recurse && rel->rd_rel->relhassubclass)
    6696             :     {
    6697         148 :         Oid         relid = RelationGetRelid(rel);
    6698             :         ListCell   *child;
    6699             :         List       *children;
    6700             : 
    6701         148 :         children = find_all_inheritors(relid, lockmode, NULL);
    6702             : 
    6703             :         /*
    6704             :          * find_all_inheritors does the recursive search of the inheritance
    6705             :          * hierarchy, so all we have to do is process all of the relids in the
    6706             :          * list that it returns.
    6707             :          */
    6708         624 :         foreach(child, children)
    6709             :         {
    6710         476 :             Oid         childrelid = lfirst_oid(child);
    6711             :             Relation    childrel;
    6712             : 
    6713         476 :             if (childrelid == relid)
    6714         148 :                 continue;
    6715             :             /* find_all_inheritors already got lock */
    6716         328 :             childrel = relation_open(childrelid, NoLock);
    6717         328 :             CheckTableNotInUse(childrel, "ALTER TABLE");
    6718         328 :             ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode, context);
    6719         328 :             relation_close(childrel, NoLock);
    6720             :         }
    6721             :     }
    6722       15616 : }
    6723             : 
    6724             : /*
    6725             :  * Obtain list of partitions of the given table, locking them all at the given
    6726             :  * lockmode and ensuring that they all pass CheckTableNotInUse.
    6727             :  *
    6728             :  * This function is a no-op if the given relation is not a partitioned table;
    6729             :  * in particular, nothing is done if it's a legacy inheritance parent.
    6730             :  */
    6731             : static void
    6732         968 : ATCheckPartitionsNotInUse(Relation rel, LOCKMODE lockmode)
    6733             : {
    6734         968 :     if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
    6735             :     {
    6736             :         List       *inh;
    6737             :         ListCell   *cell;
    6738             : 
    6739         172 :         inh = find_all_inheritors(RelationGetRelid(rel), lockmode, NULL);
    6740             :         /* first element is the parent rel; must ignore it */
    6741         588 :         for_each_from(cell, inh, 1)
    6742             :         {
    6743             :             Relation    childrel;
    6744             : 
    6745             :             /* find_all_inheritors already got lock */
    6746         422 :             childrel = table_open(lfirst_oid(cell), NoLock);
    6747         422 :             CheckTableNotInUse(childrel, "ALTER TABLE");
    6748         416 :             table_close(childrel, NoLock);
    6749             :         }
    6750         166 :         list_free(inh);
    6751             :     }
    6752         962 : }
    6753             : 
    6754             : /*
    6755             :  * ATTypedTableRecursion
    6756             :  *
    6757             :  * Propagate ALTER TYPE operations to the typed tables of that type.
    6758             :  * Also check the RESTRICT/CASCADE behavior.  Given CASCADE, also permit
    6759             :  * recursion to inheritance children of the typed tables.
    6760             :  */
    6761             : static void
    6762         190 : ATTypedTableRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd,
    6763             :                       LOCKMODE lockmode, AlterTableUtilityContext *context)
    6764             : {
    6765             :     ListCell   *child;
    6766             :     List       *children;
    6767             : 
    6768             :     Assert(rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE);
    6769             : 
    6770         190 :     children = find_typed_table_dependencies(rel->rd_rel->reltype,
    6771         190 :                                              RelationGetRelationName(rel),
    6772             :                                              cmd->behavior);
    6773             : 
    6774         202 :     foreach(child, children)
    6775             :     {
    6776          30 :         Oid         childrelid = lfirst_oid(child);
    6777             :         Relation    childrel;
    6778             : 
    6779          30 :         childrel = relation_open(childrelid, lockmode);
    6780          30 :         CheckTableNotInUse(childrel, "ALTER TABLE");
    6781          30 :         ATPrepCmd(wqueue, childrel, cmd, true, true, lockmode, context);
    6782          30 :         relation_close(childrel, NoLock);
    6783             :     }
    6784         172 : }
    6785             : 
    6786             : 
    6787             : /*
    6788             :  * find_composite_type_dependencies
    6789             :  *
    6790             :  * Check to see if the type "typeOid" is being used as a column in some table
    6791             :  * (possibly nested several levels deep in composite types, arrays, etc!).
    6792             :  * Eventually, we'd like to propagate the check or rewrite operation
    6793             :  * into such tables, but for now, just error out if we find any.
    6794             :  *
    6795             :  * Caller should provide either the associated relation of a rowtype,
    6796             :  * or a type name (not both) for use in the error message, if any.
    6797             :  *
    6798             :  * Note that "typeOid" is not necessarily a composite type; it could also be
    6799             :  * another container type such as an array or range, or a domain over one of
    6800             :  * these things.  The name of this function is therefore somewhat historical,
    6801             :  * but it's not worth changing.
    6802             :  *
    6803             :  * We assume that functions and views depending on the type are not reasons
    6804             :  * to reject the ALTER.  (How safe is this really?)
    6805             :  */
    6806             : void
    6807        3866 : find_composite_type_dependencies(Oid typeOid, Relation origRelation,
    6808             :                                  const char *origTypeName)
    6809             : {
    6810             :     Relation    depRel;
    6811             :     ScanKeyData key[2];
    6812             :     SysScanDesc depScan;
    6813             :     HeapTuple   depTup;
    6814             : 
    6815             :     /* since this function recurses, it could be driven to stack overflow */
    6816        3866 :     check_stack_depth();
    6817             : 
    6818             :     /*
    6819             :      * We scan pg_depend to find those things that depend on the given type.
    6820             :      * (We assume we can ignore refobjsubid for a type.)
    6821             :      */
    6822        3866 :     depRel = table_open(DependRelationId, AccessShareLock);
    6823             : 
    6824        3866 :     ScanKeyInit(&key[0],
    6825             :                 Anum_pg_depend_refclassid,
    6826             :                 BTEqualStrategyNumber, F_OIDEQ,
    6827             :                 ObjectIdGetDatum(TypeRelationId));
    6828        3866 :     ScanKeyInit(&key[1],
    6829             :                 Anum_pg_depend_refobjid,
    6830             :                 BTEqualStrategyNumber, F_OIDEQ,
    6831             :                 ObjectIdGetDatum(typeOid));
    6832             : 
    6833        3866 :     depScan = systable_beginscan(depRel, DependReferenceIndexId, true,
    6834             :                                  NULL, 2, key);
    6835             : 
    6836        5976 :     while (HeapTupleIsValid(depTup = systable_getnext(depScan)))
    6837             :     {
    6838        2206 :         Form_pg_depend pg_depend = (Form_pg_depend) GETSTRUCT(depTup);
    6839             :         Relation    rel;
    6840             :         TupleDesc   tupleDesc;
    6841             :         Form_pg_attribute att;
    6842             : 
    6843             :         /* Check for directly dependent types */
    6844        2206 :         if (pg_depend->classid == TypeRelationId)
    6845             :         {
    6846             :             /*
    6847             :              * This must be an array, domain, or range containing the given
    6848             :              * type, so recursively check for uses of this type.  Note that
    6849             :              * any error message will mention the original type not the
    6850             :              * container; this is intentional.
    6851             :              */
    6852        1860 :             find_composite_type_dependencies(pg_depend->objid,
    6853             :                                              origRelation, origTypeName);
    6854        1836 :             continue;
    6855             :         }
    6856             : 
    6857             :         /* Else, ignore dependees that aren't relations */
    6858         346 :         if (pg_depend->classid != RelationRelationId)
    6859         122 :             continue;
    6860             : 
    6861         224 :         rel = relation_open(pg_depend->objid, AccessShareLock);
    6862         224 :         tupleDesc = RelationGetDescr(rel);
    6863             : 
    6864             :         /*
    6865             :          * If objsubid identifies a specific column, refer to that in error
    6866             :          * messages.  Otherwise, search to see if there's a user column of the
    6867             :          * type.  (We assume system columns are never of interesting types.)
    6868             :          * The search is needed because an index containing an expression
    6869             :          * column of the target type will just be recorded as a whole-relation
    6870             :          * dependency.  If we do not find a column of the type, the dependency
    6871             :          * must indicate that the type is transiently referenced in an index
    6872             :          * expression but not stored on disk, which we assume is OK, just as
    6873             :          * we do for references in views.  (It could also be that the target
    6874             :          * type is embedded in some container type that is stored in an index
    6875             :          * column, but the previous recursion should catch such cases.)
    6876             :          */
    6877         224 :         if (pg_depend->objsubid > 0 && pg_depend->objsubid <= tupleDesc->natts)
    6878          66 :             att = TupleDescAttr(tupleDesc, pg_depend->objsubid - 1);
    6879             :         else
    6880             :         {
    6881         158 :             att = NULL;
    6882         406 :             for (int attno = 1; attno <= tupleDesc->natts; attno++)
    6883             :             {
    6884         254 :                 att = TupleDescAttr(tupleDesc, attno - 1);
    6885         254 :                 if (att->atttypid == typeOid && !att->attisdropped)
    6886           6 :                     break;
    6887         248 :                 att = NULL;
    6888             :             }
    6889         158 :             if (att == NULL)
    6890             :             {
    6891             :                 /* No such column, so assume OK */
    6892         152 :                 relation_close(rel, AccessShareLock);
    6893         152 :                 continue;
    6894             :             }
    6895             :         }
    6896             : 
    6897             :         /*
    6898             :          * We definitely should reject if the relation has storage.  If it's
    6899             :          * partitioned, then perhaps we don't have to reject: if there are
    6900             :          * partitions then we'll fail when we find one, else there is no
    6901             :          * stored data to worry about.  However, it's possible that the type
    6902             :          * change would affect conclusions about whether the type is sortable
    6903             :          * or hashable and thus (if it's a partitioning column) break the
    6904             :          * partitioning rule.  For now, reject for partitioned rels too.
    6905             :          */
    6906          72 :         if (RELKIND_HAS_STORAGE(rel->rd_rel->relkind) ||
    6907           0 :             RELKIND_HAS_PARTITIONS(rel->rd_rel->relkind))
    6908             :         {
    6909          72 :             if (origTypeName)
    6910          30 :                 ereport(ERROR,
    6911             :                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    6912             :                          errmsg("cannot alter type \"%s\" because column \"%s.%s\" uses it",
    6913             :                                 origTypeName,
    6914             :                                 RelationGetRelationName(rel),
    6915             :                                 NameStr(att->attname))));
    6916          42 :             else if (origRelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
    6917          18 :                 ereport(ERROR,
    6918             :                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    6919             :                          errmsg("cannot alter type \"%s\" because column \"%s.%s\" uses it",
    6920             :                                 RelationGetRelationName(origRelation),
    6921             :                                 RelationGetRelationName(rel),
    6922             :                                 NameStr(att->attname))));
    6923          24 :             else if (origRelation->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
    6924           6 :                 ereport(ERROR,
    6925             :                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    6926             :                          errmsg("cannot alter foreign table \"%s\" because column \"%s.%s\" uses its row type",
    6927             :                                 RelationGetRelationName(origRelation),
    6928             :                                 RelationGetRelationName(rel),
    6929             :                                 NameStr(att->attname))));
    6930             :             else
    6931          18 :                 ereport(ERROR,
    6932             :                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    6933             :                          errmsg("cannot alter table \"%s\" because column \"%s.%s\" uses its row type",
    6934             :                                 RelationGetRelationName(origRelation),
    6935             :                                 RelationGetRelationName(rel),
    6936             :                                 NameStr(att->attname))));
    6937             :         }
    6938           0 :         else if (OidIsValid(rel->rd_rel->reltype))
    6939             :         {
    6940             :             /*
    6941             :              * A view or composite type itself isn't a problem, but we must
    6942             :              * recursively check for indirect dependencies via its rowtype.
    6943             :              */
    6944           0 :             find_composite_type_dependencies(rel->rd_rel->reltype,
    6945             :                                              origRelation, origTypeName);
    6946             :         }
    6947             : 
    6948           0 :         relation_close(rel, AccessShareLock);
    6949             :     }
    6950             : 
    6951        3770 :     systable_endscan(depScan);
    6952             : 
    6953        3770 :     relation_close(depRel, AccessShareLock);
    6954        3770 : }
    6955             : 
    6956             : 
    6957             : /*
    6958             :  * find_typed_table_dependencies
    6959             :  *
    6960             :  * Check to see if a composite type is being used as the type of a
    6961             :  * typed table.  Abort if any are found and behavior is RESTRICT.
    6962             :  * Else return the list of tables.
    6963             :  */
    6964             : static List *
    6965         214 : find_typed_table_dependencies(Oid typeOid, const char *typeName, DropBehavior behavior)
    6966             : {
    6967             :     Relation    classRel;
    6968             :     ScanKeyData key[1];
    6969             :     TableScanDesc scan;
    6970             :     HeapTuple   tuple;
    6971         214 :     List       *result = NIL;
    6972             : 
    6973         214 :     classRel = table_open(RelationRelationId, AccessShareLock);
    6974             : 
    6975         214 :     ScanKeyInit(&key[0],
    6976             :                 Anum_pg_class_reloftype,
    6977             :                 BTEqualStrategyNumber, F_OIDEQ,
    6978             :                 ObjectIdGetDatum(typeOid));
    6979             : 
    6980         214 :     scan = table_beginscan_catalog(classRel, 1, key);
    6981             : 
    6982         250 :     while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
    6983             :     {
    6984          60 :         Form_pg_class classform = (Form_pg_class) GETSTRUCT(tuple);
    6985             : 
    6986          60 :         if (behavior == DROP_RESTRICT)
    6987          24 :             ereport(ERROR,
    6988             :                     (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
    6989             :                      errmsg("cannot alter type \"%s\" because it is the type of a typed table",
    6990             :                             typeName),
    6991             :                      errhint("Use ALTER ... CASCADE to alter the typed tables too.")));
    6992             :         else
    6993          36 :             result = lappend_oid(result, classform->oid);
    6994             :     }
    6995             : 
    6996         190 :     table_endscan(scan);
    6997         190 :     table_close(classRel, AccessShareLock);
    6998             : 
    6999         190 :     return result;
    7000             : }
    7001             : 
    7002             : 
    7003             : /*
    7004             :  * check_of_type
    7005             :  *
    7006             :  * Check whether a type is suitable for CREATE TABLE OF/ALTER TABLE OF.  If it
    7007             :  * isn't suitable, throw an error.  Currently, we require that the type
    7008             :  * originated with CREATE TYPE AS.  We could support any row type, but doing so
    7009             :  * would require handling a number of extra corner cases in the DDL commands.
    7010             :  * (Also, allowing domain-over-composite would open up a can of worms about
    7011             :  * whether and how the domain's constraints should apply to derived tables.)
    7012             :  */
    7013             : void
    7014         170 : check_of_type(HeapTuple typetuple)
    7015             : {
    7016         170 :     Form_pg_type typ = (Form_pg_type) GETSTRUCT(typetuple);
    7017         170 :     bool        typeOk = false;
    7018             : 
    7019         170 :     if (typ->typtype == TYPTYPE_COMPOSITE)
    7020             :     {
    7021             :         Relation    typeRelation;
    7022             : 
    7023             :         Assert(OidIsValid(typ->typrelid));
    7024         170 :         typeRelation = relation_open(typ->typrelid, AccessShareLock);
    7025         170 :         typeOk = (typeRelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE);
    7026             : 
    7027             :         /*
    7028             :          * Close the parent rel, but keep our AccessShareLock on it until xact
    7029             :          * commit.  That will prevent someone else from deleting or ALTERing
    7030             :          * the type before the typed table creation/conversion commits.
    7031             :          */
    7032         170 :         relation_close(typeRelation, NoLock);
    7033             :     }
    7034         170 :     if (!typeOk)
    7035           6 :         ereport(ERROR,
    7036             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    7037             :                  errmsg("type %s is not a composite type",
    7038             :                         format_type_be(typ->oid))));
    7039         164 : }
    7040             : 
    7041             : 
    7042             : /*
    7043             :  * ALTER TABLE ADD COLUMN
    7044             :  *
    7045             :  * Adds an additional attribute to a relation making the assumption that
    7046             :  * CHECK, NOT NULL, and FOREIGN KEY constraints will be removed from the
    7047             :  * AT_AddColumn AlterTableCmd by parse_utilcmd.c and added as independent
    7048             :  * AlterTableCmd's.
    7049             :  *
    7050             :  * ADD COLUMN cannot use the normal ALTER TABLE recursion mechanism, because we
    7051             :  * have to decide at runtime whether to recurse or not depending on whether we
    7052             :  * actually add a column or merely merge with an existing column.  (We can't
    7053             :  * check this in a static pre-pass because it won't handle multiple inheritance
    7054             :  * situations correctly.)
    7055             :  */
    7056             : static void
    7057        1986 : ATPrepAddColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
    7058             :                 bool is_view, AlterTableCmd *cmd, LOCKMODE lockmode,
    7059             :                 AlterTableUtilityContext *context)
    7060             : {
    7061        1986 :     if (rel->rd_rel->reloftype && !recursing)
    7062           6 :         ereport(ERROR,
    7063             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    7064             :                  errmsg("cannot add column to typed table")));
    7065             : 
    7066        1980 :     if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
    7067          58 :         ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
    7068             : 
    7069        1974 :     if (recurse && !is_view)
    7070        1874 :         cmd->recurse = true;
    7071        1974 : }
    7072             : 
    7073             : /*
    7074             :  * Add a column to a table.  The return value is the address of the
    7075             :  * new column in the parent relation.
    7076             :  *
    7077             :  * cmd is pass-by-ref so that we can replace it with the parse-transformed
    7078             :  * copy (but that happens only after we check for IF NOT EXISTS).
    7079             :  */
    7080             : static ObjectAddress
    7081        2604 : ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
    7082             :                 AlterTableCmd **cmd, bool recurse, bool recursing,
    7083             :                 LOCKMODE lockmode, AlterTablePass cur_pass,
    7084             :                 AlterTableUtilityContext *context)
    7085             : {
    7086        2604 :     Oid         myrelid = RelationGetRelid(rel);
    7087        2604 :     ColumnDef  *colDef = castNode(ColumnDef, (*cmd)->def);
    7088        2604 :     bool        if_not_exists = (*cmd)->missing_ok;
    7089             :     Relation    pgclass,
    7090             :                 attrdesc;
    7091             :     HeapTuple   reltup;
    7092             :     Form_pg_attribute attribute;
    7093             :     int         newattnum;
    7094             :     char        relkind;
    7095             :     Expr       *defval;
    7096             :     List       *children;
    7097             :     ListCell   *child;
    7098             :     AlterTableCmd *childcmd;
    7099             :     ObjectAddress address;
    7100             :     TupleDesc   tupdesc;
    7101             : 
    7102             :     /* since this function recurses, it could be driven to stack overflow */
    7103        2604 :     check_stack_depth();
    7104             : 
    7105             :     /* At top level, permission check was done in ATPrepCmd, else do it */
    7106        2604 :     if (recursing)
    7107         636 :         ATSimplePermissions((*cmd)->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
    7108             : 
    7109        2604 :     if (rel->rd_rel->relispartition && !recursing)
    7110          12 :         ereport(ERROR,
    7111             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    7112             :                  errmsg("cannot add column to a partition")));
    7113             : 
    7114        2592 :     attrdesc = table_open(AttributeRelationId, RowExclusiveLock);
    7115             : 
    7116             :     /*
    7117             :      * Are we adding the column to a recursion child?  If so, check whether to
    7118             :      * merge with an existing definition for the column.  If we do merge, we
    7119             :      * must not recurse.  Children will already have the column, and recursing
    7120             :      * into them would mess up attinhcount.
    7121             :      */
    7122        2592 :     if (colDef->inhcount > 0)
    7123             :     {
    7124             :         HeapTuple   tuple;
    7125             : 
    7126             :         /* Does child already have a column by this name? */
    7127         636 :         tuple = SearchSysCacheCopyAttName(myrelid, colDef->colname);
    7128         636 :         if (HeapTupleIsValid(tuple))
    7129             :         {
    7130          48 :             Form_pg_attribute childatt = (Form_pg_attribute) GETSTRUCT(tuple);
    7131             :             Oid         ctypeId;
    7132             :             int32       ctypmod;
    7133             :             Oid         ccollid;
    7134             : 
    7135             :             /* Child column must match on type, typmod, and collation */
    7136          48 :             typenameTypeIdAndMod(NULL, colDef->typeName, &ctypeId, &ctypmod);
    7137          48 :             if (ctypeId != childatt->atttypid ||
    7138          48 :                 ctypmod != childatt->atttypmod)
    7139           0 :                 ereport(ERROR,
    7140             :                         (errcode(ERRCODE_DATATYPE_MISMATCH),
    7141             :                          errmsg("child table \"%s\" has different type for column \"%s\"",
    7142             :                                 RelationGetRelationName(rel), colDef->colname)));
    7143          48 :             ccollid = GetColumnDefCollation(NULL, colDef, ctypeId);
    7144          48 :             if (ccollid != childatt->attcollation)
    7145           0 :                 ereport(ERROR,
    7146             :                         (errcode(ERRCODE_COLLATION_MISMATCH),
    7147             :                          errmsg("child table \"%s\" has different collation for column \"%s\"",
    7148             :                                 RelationGetRelationName(rel), colDef->colname),
    7149             :                          errdetail("\"%s\" versus \"%s\"",
    7150             :                                    get_collation_name(ccollid),
    7151             :                                    get_collation_name(childatt->attcollation))));
    7152             : 
    7153             :             /* Bump the existing child att's inhcount */
    7154          48 :             childatt->attinhcount++;
    7155          48 :             if (childatt->attinhcount < 0)
    7156           0 :                 ereport(ERROR,
    7157             :                         errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
    7158             :                         errmsg("too many inheritance parents"));
    7159          48 :             CatalogTupleUpdate(attrdesc, &tuple->t_self, tuple);
    7160             : 
    7161          48 :             heap_freetuple(tuple);
    7162             : 
    7163             :             /* Inform the user about the merge */
    7164          48 :             ereport(NOTICE,
    7165             :                     (errmsg("merging definition of column \"%s\" for child \"%s\"",
    7166             :                             colDef->colname, RelationGetRelationName(rel))));
    7167             : 
    7168          48 :             table_close(attrdesc, RowExclusiveLock);
    7169             : 
    7170             :             /* Make the child column change visible */
    7171          48 :             CommandCounterIncrement();
    7172             : 
    7173          48 :             return InvalidObjectAddress;
    7174             :         }
    7175             :     }
    7176             : 
    7177             :     /* skip if the name already exists and if_not_exists is true */
    7178        2544 :     if (!check_for_column_name_collision(rel, colDef->colname, if_not_exists))
    7179             :     {
    7180          54 :         table_close(attrdesc, RowExclusiveLock);
    7181          54 :         return InvalidObjectAddress;
    7182             :     }
    7183             : 
    7184             :     /*
    7185             :      * Okay, we need to add the column, so go ahead and do parse
    7186             :      * transformation.  This can result in queueing up, or even immediately
    7187             :      * executing, subsidiary operations (such as creation of unique indexes);
    7188             :      * so we mustn't do it until we have made the if_not_exists check.
    7189             :      *
    7190             :      * When recursing, the command was already transformed and we needn't do
    7191             :      * so again.  Also, if context isn't given we can't transform.  (That
    7192             :      * currently happens only for AT_AddColumnToView; we expect that view.c
    7193             :      * passed us a ColumnDef that doesn't need work.)
    7194             :      */
    7195        2460 :     if (context != NULL && !recursing)
    7196             :     {
    7197        1848 :         *cmd = ATParseTransformCmd(wqueue, tab, rel, *cmd, recurse, lockmode,
    7198             :                                    cur_pass, context);
    7199             :         Assert(*cmd != NULL);
    7200        1848 :         colDef = castNode(ColumnDef, (*cmd)->def);
    7201             :     }
    7202             : 
    7203             :     /*
    7204             :      * Regular inheritance children are independent enough not to inherit the
    7205             :      * identity column from parent hence cannot recursively add identity
    7206             :      * column if the table has inheritance children.
    7207             :      *
    7208             :      * Partitions, on the other hand, are integral part of a partitioned table
    7209             :      * and inherit identity column.  Hence propagate identity column down the
    7210             :      * partition hierarchy.
    7211             :      */
    7212        2460 :     if (colDef->identity &&
    7213          54 :         recurse &&
    7214         102 :         rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE &&
    7215          48 :         find_inheritance_children(myrelid, NoLock) != NIL)
    7216           6 :         ereport(ERROR,
    7217             :                 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    7218             :                  errmsg("cannot recursively add identity column to table that has child tables")));
    7219             : 
    7220        2454 :     pgclass = table_open(RelationRelationId, RowExclusiveLock);
    7221             : 
    7222        2454 :     reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(myrelid));
    7223        2454 :     if (!HeapTupleIsValid(reltup))
    7224           0 :         elog(ERROR, "cache lookup failed for relation %u", myrelid);
    7225        2454 :     relkind = ((Form_pg_class) GETSTRUCT(reltup))->relkind;
    7226             : 
    7227             :     /* Determine the new attribute's number */
    7228        2454 :     newattnum = ((Form_pg_class) GETSTRUCT(reltup))->relnatts + 1;
    7229        2454 :     if (newattnum > MaxHeapAttributeNumber)
    7230           0 :         ereport(ERROR,
    7231             :                 (errcode(ERRCODE_TOO_MANY_COLUMNS),
    7232             :                  errmsg("tables can have at most %d columns",
    7233             :                         MaxHeapAttributeNumber)));
    7234             : 
    7235             :     /*
    7236             :      * Construct new attribute's pg_attribute entry.
    7237             :      */
    7238        2454 :     tupdesc = BuildDescForRelation(list_make1(colDef));
    7239             : 
    7240        2442 :     attribute = TupleDescAttr(tupdesc, 0);
    7241             : 
    7242             :     /* Fix up attribute number */
    7243        2442 :     attribute->attnum = newattnum;
    7244             : 
    7245             :     /* make sure datatype is legal for a column */
    7246        2442 :     CheckAttributeType(NameStr(attribute->attname), attribute->atttypid, attribute->attcollation,
    7247        2442 :                        list_make1_oid(rel->rd_rel->reltype),
    7248             :                        0);
    7249             : 
    7250        2412 :     InsertPgAttributeTuples(attrdesc, tupdesc, myrelid, NULL, NULL);
    7251             : 
    7252        2412 :     table_close(attrdesc, RowExclusiveLock);
    7253             : 
    7254             :     /*
    7255             :      * Update pg_class tuple as appropriate
    7256             :      */
    7257        2412 :     ((Form_pg_class) GETSTRUCT(reltup))->relnatts = newattnum;
    7258             : 
    7259        2412 :     CatalogTupleUpdate(pgclass, &reltup->t_self, reltup);
    7260             : 
    7261        2412 :     heap_freetuple(reltup);
    7262             : 
    7263             :     /* Post creation hook for new attribute */
    7264        2412 :     InvokeObjectPostCreateHook(RelationRelationId, myrelid, newattnum);
    7265             : 
    7266        2412 :     table_close(pgclass, RowExclusiveLock);
    7267             : 
    7268             :     /* Make the attribute's catalog entry visible */
    7269        2412 :     CommandCounterIncrement();
    7270             : 
    7271             :     /*
    7272             :      * Store the DEFAULT, if any, in the catalogs
    7273             :      */
    7274        2412 :     if (colDef->raw_default)
    7275             :     {
    7276             :         RawColumnDefault *rawEnt;
    7277             : 
    7278         700 :         rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
    7279         700 :         rawEnt->attnum = attribute->attnum;
    7280         700 :         rawEnt->raw_default = copyObject(colDef->raw_default);
    7281             : 
    7282             :         /*
    7283             :          * Attempt to skip a complete table rewrite by storing the specified
    7284             :          * DEFAULT value outside of the heap.  This may be disabled inside
    7285             :          * AddRelationNewConstraints if the optimization cannot be applied.
    7286             :          */
    7287         700 :         rawEnt->missingMode = (!colDef->generated);
    7288             : 
    7289         700 :         rawEnt->generated = colDef->generated;
    7290             : 
    7291             :         /*
    7292             :          * This function is intended for CREATE TABLE, so it processes a
    7293             :          * _list_ of defaults, but we just do one.
    7294             :          */
    7295         700 :         AddRelationNewConstraints(rel, list_make1(rawEnt), NIL,
    7296             :                                   false, true, false, NULL);
    7297             : 
    7298             :         /* Make the additional catalog changes visible */
    7299         688 :         CommandCounterIncrement();
    7300             : 
    7301             :         /*
    7302             :          * Did the request for a missing value work? If not we'll have to do a
    7303             :          * rewrite
    7304             :          */
    7305         688 :         if (!rawEnt->missingMode)
    7306         108 :             tab->rewrite |= AT_REWRITE_DEFAULT_VAL;
    7307             :     }
    7308             : 
    7309             :     /*
    7310             :      * Tell Phase 3 to fill in the default expression, if there is one.
    7311             :      *
    7312             :      * If there is no default, Phase 3 doesn't have to do anything, because
    7313             :      * that effectively means that the default is NULL.  The heap tuple access
    7314             :      * routines always check for attnum > # of attributes in tuple, and return
    7315             :      * NULL if so, so without any modification of the tuple data we will get
    7316             :      * the effect of NULL values in the new column.
    7317             :      *
    7318             :      * An exception occurs when the new column is of a domain type: the domain
    7319             :      * might have a not-null constraint, or a check constraint that indirectly
    7320             :      * rejects nulls.  If there are any domain constraints then we construct
    7321             :      * an explicit NULL default value that will be passed through
    7322             :      * CoerceToDomain processing.  (This is a tad inefficient, since it causes
    7323             :      * rewriting the table which we really don't have to do, but the present
    7324             :      * design of domain processing doesn't offer any simple way of checking
    7325             :      * the constraints more directly.)
    7326             :      *
    7327             :      * Note: we use build_column_default, and not just the cooked default
    7328             :      * returned by AddRelationNewConstraints, so that the right thing happens
    7329             :      * when a datatype's default applies.
    7330             :      *
    7331             :      * Note: it might seem that this should happen at the end of Phase 2, so
    7332             :      * that the effects of subsequent subcommands can be taken into account.
    7333             :      * It's intentional that we do it now, though.  The new column should be
    7334             :      * filled according to what is said in the ADD COLUMN subcommand, so that
    7335             :      * the effects are the same as if this subcommand had been run by itself
    7336             :      * and the later subcommands had been issued in new ALTER TABLE commands.
    7337             :      *
    7338             :      * We can skip this entirely for relations without storage, since Phase 3
    7339             :      * is certainly not going to touch them.  System attributes don't have
    7340             :      * interesting defaults, either.
    7341             :      */
    7342        2400 :     if (RELKIND_HAS_STORAGE(relkind))
    7343             :     {
    7344             :         /*
    7345             :          * For an identity column, we can't use build_column_default(),
    7346             :          * because the sequence ownership isn't set yet.  So do it manually.
    7347             :          */
    7348        2048 :         if (colDef->identity)
    7349             :         {
    7350          42 :             NextValueExpr *nve = makeNode(NextValueExpr);
    7351             : 
    7352          42 :             nve->seqid = RangeVarGetRelid(colDef->identitySequence, NoLock, false);
    7353          42 :             nve->typeId = attribute->atttypid;
    7354             : 
    7355          42 :             defval = (Expr *) nve;
    7356             : 
    7357             :             /* must do a rewrite for identity columns */
    7358          42 :             tab->rewrite |= AT_REWRITE_DEFAULT_VAL;
    7359             :         }
    7360             :         else
    7361        2006 :             defval = (Expr *) build_column_default(rel, attribute->attnum);
    7362             : 
    7363        2048 :         if (!defval && DomainHasConstraints(attribute->atttypid))
    7364             :         {
    7365             :             Oid         baseTypeId;
    7366             :             int32       baseTypeMod;
    7367             :             Oid         baseTypeColl;
    7368             : 
    7369           6 :             baseTypeMod = attribute->atttypmod;
    7370           6 :             baseTypeId = getBaseTypeAndTypmod(attribute->atttypid, &baseTypeMod);
    7371           6 :             baseTypeColl = get_typcollation(baseTypeId);
    7372           6 :             defval = (Expr *) makeNullConst(baseTypeId, baseTypeMod, baseTypeColl);
    7373           6 :             defval = (Expr *) coerce_to_target_type(NULL,
    7374             :                                                     (Node *) defval,
    7375             :                                                     baseTypeId,
    7376             :                                                     attribute->atttypid,
    7377             :                                                     attribute->atttypmod,
    7378             :                                                     COERCION_ASSIGNMENT,
    7379             :                                                     COERCE_IMPLICIT_CAST,
    7380             :                                                     -1);
    7381           6 :             if (defval == NULL) /* should not happen */
    7382           0 :                 elog(ERROR, "failed to coerce base type to domain");
    7383             :         }
    7384             : 
    7385        2048 :         if (defval)
    7386             :         {
    7387             :             NewColumnValue *newval;
    7388             : 
    7389         606 :             newval = (NewColumnValue *) palloc0(sizeof(NewColumnValue));
    7390         606 :             newval->attnum = attribute->attnum;
    7391         606 :             newval->expr = expression_planner(defval);
    7392         606 :             newval->is_generated = (colDef->generated != '\0');
    7393             : 
    7394         606 :             tab->newvals = lappend(tab->newvals, newval);
    7395             :         }
    7396             : 
    7397        2048 :         if (DomainHasConstraints(attribute->atttypid))
    7398          12 :             tab->rewrite |= AT_REWRITE_DEFAULT_VAL;
    7399             : 
    7400        2048 :         if (!TupleDescAttr(rel->rd_att, attribute->attnum - 1)->atthasmissing)
    7401             :         {
    7402             :             /*
    7403             :              * If the new column is NOT NULL, and there is no missing value,
    7404             :              * tell Phase 3 it needs to check for NULLs.
    7405             :              */
    7406        1598 :             tab->verify_new_notnull |= colDef->is_not_null;
    7407             :         }
    7408             :     }
    7409             : 
    7410             :     /*
    7411             :      * Add needed dependency entries for the new column.
    7412             :      */
    7413        2400 :     add_column_datatype_dependency(myrelid, newattnum, attribute->atttypid);
    7414        2400 :     add_column_collation_dependency(myrelid, newattnum, attribute->attcollation);
    7415             : 
    7416             :     /*
    7417             :      * Propagate to children as appropriate.  Unlike most other ALTER
    7418             :      * routines, we have to do this one level of recursion at a time; we can't
    7419             :      * use find_all_inheritors to do it in one pass.
    7420             :      */
    7421             :     children =
    7422        2400 :         find_inheritance_children(RelationGetRelid(rel), lockmode);
    7423             : 
    7424             :     /*
    7425             :      * If we are told not to recurse, there had better not be any child
    7426             :      * tables; else the addition would put them out of step.
    7427             :      */
    7428        2400 :     if (children && !recurse)
    7429          12 :         ereport(ERROR,
    7430             :                 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    7431             :                  errmsg("column must be added to child tables too")));
    7432             : 
    7433             :     /* Children should see column as singly inherited */
    7434        2388 :     if (!recursing)
    7435             :     {
    7436        1800 :         childcmd = copyObject(*cmd);
    7437        1800 :         colDef = castNode(ColumnDef, childcmd->def);
    7438        1800 :         colDef->inhcount = 1;
    7439        1800 :         colDef->is_local = false;
    7440             :     }
    7441             :     else
    7442         588 :         childcmd = *cmd;        /* no need to copy again */
    7443             : 
    7444        3024 :     foreach(child, children)
    7445             :     {
    7446         636 :         Oid         childrelid = lfirst_oid(child);
    7447             :         Relation    childrel;
    7448             :         AlteredTableInfo *childtab;
    7449             : 
    7450             :         /* find_inheritance_children already got lock */
    7451         636 :         childrel = table_open(childrelid, NoLock);
    7452         636 :         CheckTableNotInUse(childrel, "ALTER TABLE");
    7453             : 
    7454             :         /* Find or create work queue entry for this table */
    7455         636 :         childtab = ATGetQueueEntry(wqueue, childrel);
    7456             : 
    7457             :         /* Recurse to child; return value is ignored */
    7458         636 :         ATExecAddColumn(wqueue, childtab, childrel,
    7459             :                         &childcmd, recurse, true,
    7460             :                         lockmode, cur_pass, context);
    7461             : 
    7462         636 :         table_close(childrel, NoLock);
    7463             :     }
    7464             : 
    7465        2388 :     ObjectAddressSubSet(address, RelationRelationId, myrelid, newattnum);
    7466        2388 :     return address;
    7467             : }
    7468             : 
    7469             : /*
    7470             :  * If a new or renamed column will collide with the name of an existing
    7471             :  * column and if_not_exists is false then error out, else do nothing.
    7472             :  */
    7473             : static bool
    7474        2982 : check_for_column_name_collision(Relation rel, const char *colname,
    7475             :                                 bool if_not_exists)
    7476             : {
    7477             :     HeapTuple   attTuple;
    7478             :     int         attnum;
    7479             : 
    7480             :     /*
    7481             :      * this test is deliberately not attisdropped-aware, since if one tries to
    7482             :      * add a column matching a dropped column name, it's gonna fail anyway.
    7483             :      */
    7484        2982 :     attTuple = SearchSysCache2(ATTNAME,
    7485             :                                ObjectIdGetDatum(RelationGetRelid(rel)),
    7486             :                                PointerGetDatum(colname));
    7487        2982 :     if (!HeapTupleIsValid(attTuple))
    7488        2886 :         return true;
    7489             : 
    7490          96 :     attnum = ((Form_pg_attribute) GETSTRUCT(attTuple))->attnum;
    7491          96 :     ReleaseSysCache(attTuple);
    7492             : 
    7493             :     /*
    7494             :      * We throw a different error message for conflicts with system column
    7495             :      * names, since they are normally not shown and the user might otherwise
    7496             :      * be confused about the reason for the conflict.
    7497             :      */
    7498          96 :     if (attnum <= 0)
    7499          12 :         ereport(ERROR,
    7500             :                 (errcode(ERRCODE_DUPLICATE_COLUMN),
    7501             :                  errmsg("column name \"%s\" conflicts with a system column name",
    7502             :                         colname)));
    7503             :     else
    7504             :     {
    7505          84 :         if (if_not_exists)
    7506             :         {
    7507          54 :             ereport(NOTICE,
    7508             :                     (errcode(ERRCODE_DUPLICATE_COLUMN),
    7509             :                      errmsg("column \"%s\" of relation \"%s\" already exists, skipping",
    7510             :                             colname, RelationGetRelationName(rel))));
    7511          54 :             return false;
    7512             :         }
    7513             : 
    7514          30 :         ereport(ERROR,
    7515             :                 (errcode(ERRCODE_DUPLICATE_COLUMN),
    7516             :                  errmsg("column \"%s\" of relation \"%s\" already exists",
    7517             :                         colname, RelationGetRelationName(rel))));
    7518             :     }
    7519             : 
    7520             :     return true;
    7521             : }
    7522             : 
    7523             : /*
    7524             :  * Install a column's dependency on its datatype.
    7525             :  */
    7526             : static void
    7527        3312 : add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid)
    7528             : {
    7529             :     ObjectAddress myself,
    7530             :                 referenced;
    7531             : 
    7532        3312 :     myself.classId = RelationRelationId;
    7533        3312 :     myself.objectId = relid;
    7534        3312 :     myself.objectSubId = attnum;
    7535        3312 :     referenced.classId = TypeRelationId;
    7536        3312 :     referenced.objectId = typid;
    7537        3312 :     referenced.objectSubId = 0;
    7538        3312 :     recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
    7539        3312 : }
    7540             : 
    7541             : /*
    7542             :  * Install a column's dependency on its collation.
    7543             :  */
    7544             : static void
    7545        3312 : add_column_collation_dependency(Oid relid, int32 attnum, Oid collid)
    7546             : {
    7547             :     ObjectAddress myself,
    7548             :                 referenced;
    7549             : 
    7550             :     /* We know the default collation is pinned, so don't bother recording it */
    7551        3312 :     if (OidIsValid(collid) && collid != DEFAULT_COLLATION_OID)
    7552             :     {
    7553          18 :         myself.classId = RelationRelationId;
    7554          18 :         myself.objectId = relid;
    7555          18 :         myself.objectSubId = attnum;
    7556          18 :         referenced.classId = CollationRelationId;
    7557          18 :         referenced.objectId = collid;
    7558          18 :         referenced.objectSubId = 0;
    7559          18 :         recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
    7560             :     }
    7561        3312 : }
    7562             : 
    7563             : /*
    7564             :  * ALTER TABLE ALTER COLUMN DROP NOT NULL
    7565             :  *
    7566             :  * Return the address of the modified column.  If the column was already
    7567             :  * nullable, InvalidObjectAddress is returned.
    7568             :  */
    7569             : static ObjectAddress
    7570         256 : ATExecDropNotNull(Relation rel, const char *colName, bool recurse,
    7571             :                   LOCKMODE lockmode)
    7572             : {
    7573             :     HeapTuple   tuple;
    7574             :     HeapTuple   conTup;
    7575             :     Form_pg_attribute attTup;
    7576             :     AttrNumber  attnum;
    7577             :     Relation    attr_rel;
    7578             :     ObjectAddress address;
    7579             :     List       *readyRels;
    7580             : 
    7581             :     /*
    7582             :      * lookup the attribute
    7583             :      */
    7584         256 :     attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
    7585             : 
    7586         256 :     tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
    7587         256 :     if (!HeapTupleIsValid(tuple))
    7588          18 :         ereport(ERROR,
    7589             :                 (errcode(ERRCODE_UNDEFINED_COLUMN),
    7590             :                  errmsg("column \"%s\" of relation \"%s\" does not exist",
    7591             :                         colName, RelationGetRelationName(rel))));
    7592         238 :     attTup = (Form_pg_attribute) GETSTRUCT(tuple);
    7593         238 :     attnum = attTup->attnum;
    7594         238 :     ObjectAddressSubSet(address, RelationRelationId,
    7595             :                         RelationGetRelid(rel), attnum);
    7596             : 
    7597             :     /* If the column is already nullable there's nothing to do. */
    7598         238 :     if (!attTup->attnotnull)
    7599             :     {
    7600           6 :         table_close(attr_rel, RowExclusiveLock);
    7601           6 :         return InvalidObjectAddress;
    7602             :     }
    7603             : 
    7604             :     /* Prevent them from altering a system attribute */
    7605         232 :     if (attnum <= 0)
    7606           0 :         ereport(ERROR,
    7607             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    7608             :                  errmsg("cannot alter system column \"%s\"",
    7609             :                         colName)));
    7610             : 
    7611         232 :     if (attTup->attidentity)
    7612          18 :         ereport(ERROR,
    7613             :                 (errcode(ERRCODE_SYNTAX_ERROR),
    7614             :                  errmsg("column \"%s\" of relation \"%s\" is an identity column",
    7615             :                         colName, RelationGetRelationName(rel))));
    7616             : 
    7617             :     /*
    7618             :      * It's not OK to remove a constraint only for the parent and leave it in
    7619             :      * the children, so disallow that.
    7620             :      */
    7621         214 :     if (!recurse)
    7622             :     {
    7623          12 :         if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
    7624             :         {
    7625             :             PartitionDesc partdesc;
    7626             : 
    7627          12 :             partdesc = RelationGetPartitionDesc(rel, true);
    7628             : 
    7629          12 :             if (partdesc->nparts > 0)
    7630           6 :                 ereport(ERROR,
    7631             :                         errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    7632             :                         errmsg("cannot remove constraint from only the partitioned table when partitions exist"),
    7633             :                         errhint("Do not specify the ONLY keyword."));
    7634             :         }
    7635           0 :         else if (rel->rd_rel->relhassubclass &&
    7636           0 :                  find_inheritance_children(RelationGetRelid(rel), NoLock) != NIL)
    7637             :         {
    7638           0 :             ereport(ERROR,
    7639             :                     errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    7640             :                     errmsg("not-null constraint on column \"%s\" must be removed in child tables too",
    7641             :                            colName),
    7642             :                     errhint("Do not specify the ONLY keyword."));
    7643             :         }
    7644             :     }
    7645             : 
    7646             :     /*
    7647             :      * If rel is partition, shouldn't drop NOT NULL if parent has the same.
    7648             :      */
    7649         208 :     if (rel->rd_rel->relispartition)
    7650             :     {
    7651          18 :         Oid         parentId = get_partition_parent(RelationGetRelid(rel), false);
    7652          18 :         Relation    parent = table_open(parentId, AccessShareLock);
    7653          18 :         TupleDesc   tupDesc = RelationGetDescr(parent);
    7654             :         AttrNumber  parent_attnum;
    7655             : 
    7656          18 :         parent_attnum = get_attnum(parentId, colName);
    7657          18 :         if (TupleDescAttr(tupDesc, parent_attnum - 1)->attnotnull)
    7658          18 :             ereport(ERROR,
    7659             :                     (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    7660             :                      errmsg("column \"%s\" is marked NOT NULL in parent table",
    7661             :                             colName)));
    7662           0 :         table_close(parent, AccessShareLock);
    7663             :     }
    7664             : 
    7665             :     /*
    7666             :      * Find the constraint that makes this column NOT NULL, and drop it if we
    7667             :      * see one.  dropconstraint_internal() will do necessary consistency
    7668             :      * checking.  If there isn't one, there are two possibilities: either the
    7669             :      * column is marked attnotnull because it's part of the primary key, and
    7670             :      * then we just throw an appropriate error; or it's a leftover marking
    7671             :      * that we can remove.  However, before doing the latter, to avoid
    7672             :      * breaking consistency any further, prevent this if the column is part of
    7673             :      * the replica identity.
    7674             :      */
    7675         190 :     conTup = findNotNullConstraint(RelationGetRelid(rel), colName);
    7676         190 :     if (conTup == NULL)
    7677             :     {
    7678             :         Bitmapset  *pkcols;
    7679             :         Bitmapset  *ircols;
    7680             : 
    7681             :         /*
    7682             :          * If the column is in a primary key, throw a specific error message.
    7683             :          */
    7684          30 :         pkcols = RelationGetIndexAttrBitmap(rel, INDEX_ATTR_BITMAP_PRIMARY_KEY);
    7685          30 :         if (bms_is_member(attnum - FirstLowInvalidHeapAttributeNumber,
    7686             :                           pkcols))
    7687          12 :             ereport(ERROR,
    7688             :                     errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    7689             :                     errmsg("column \"%s\" is in a primary key", colName));
    7690             : 
    7691             :         /* Also throw an error if the column is in the replica identity */
    7692          18 :         ircols = RelationGetIndexAttrBitmap(rel, INDEX_ATTR_BITMAP_IDENTITY_KEY);
    7693          18 :         if (bms_is_member(attnum - FirstLowInvalidHeapAttributeNumber, ircols))
    7694           6 :             ereport(ERROR,
    7695             :                     errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    7696             :                     errmsg("column \"%s\" is in index used as replica identity",
    7697             :                            get_attname(RelationGetRelid(rel), attnum, false)));
    7698             : 
    7699             :         /* Otherwise, just remove the attnotnull marking and do nothing else. */
    7700          12 :         attTup->attnotnull = false;
    7701          12 :         CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
    7702             :     }
    7703             :     else
    7704             :     {
    7705             :         /* The normal case: we have a pg_constraint row, remove it */
    7706         160 :         readyRels = NIL;
    7707         160 :         dropconstraint_internal(rel, conTup, DROP_RESTRICT, recurse, false,
    7708             :                                 false, &readyRels, lockmode);
    7709             : 
    7710         124 :         heap_freetuple(conTup);
    7711             :     }
    7712             : 
    7713         136 :     InvokeObjectPostAlterHook(RelationRelationId,
    7714             :                               RelationGetRelid(rel), attnum);
    7715             : 
    7716         136 :     table_close(attr_rel, RowExclusiveLock);
    7717             : 
    7718         136 :     return address;
    7719             : }
    7720             : 
    7721             : /*
    7722             :  * Helper to set pg_attribute.attnotnull if it isn't set, and to tell phase 3
    7723             :  * to verify it; recurses to apply the same to children.
    7724             :  *
    7725             :  * When called to alter an existing table, 'wqueue' must be given so that we can
    7726             :  * queue a check that existing tuples pass the constraint.  When called from
    7727             :  * table creation, 'wqueue' should be passed as NULL.
    7728             :  *
    7729             :  * Returns true if the flag was set in any table, otherwise false.
    7730             :  */
    7731             : static bool
    7732       23660 : set_attnotnull(List **wqueue, Relation rel, AttrNumber attnum, bool recurse,
    7733             :                LOCKMODE lockmode)
    7734             : {
    7735             :     HeapTuple   tuple;
    7736             :     Form_pg_attribute attForm;
    7737       23660 :     bool        retval = false;
    7738             : 
    7739             :     /* Guard against stack overflow due to overly deep inheritance tree. */
    7740       23660 :     check_stack_depth();
    7741             : 
    7742       23660 :     tuple = SearchSysCacheCopyAttNum(RelationGetRelid(rel), attnum);
    7743       23660 :     if (!HeapTupleIsValid(tuple))
    7744           0 :         elog(ERROR, "cache lookup failed for attribute %d of relation %u",
    7745             :              attnum, RelationGetRelid(rel));
    7746       23660 :     attForm = (Form_pg_attribute) GETSTRUCT(tuple);
    7747       23660 :     if (!attForm->attnotnull)
    7748             :     {
    7749             :         Relation    attr_rel;
    7750             : 
    7751        1786 :         attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
    7752             : 
    7753        1786 :         attForm->attnotnull = true;
    7754        1786 :         CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
    7755             : 
    7756        1786 :         table_close(attr_rel, RowExclusiveLock);
    7757             : 
    7758             :         /*
    7759             :          * And set up for existing values to be checked, unless another
    7760             :          * constraint already proves this.
    7761             :          */
    7762        1786 :         if (wqueue && !NotNullImpliedByRelConstraints(rel, attForm))
    7763             :         {
    7764             :             AlteredTableInfo *tab;
    7765             : 
    7766        1516 :             tab = ATGetQueueEntry(wqueue, rel);
    7767        1516 :             tab->verify_new_notnull = true;
    7768             :         }
    7769             : 
    7770        1786 :         retval = true;
    7771             :     }
    7772             : 
    7773       23660 :     if (recurse)
    7774             :     {
    7775             :         List       *children;
    7776             :         ListCell   *lc;
    7777             : 
    7778             :         /* Make above update visible, for multiple inheritance cases */
    7779        1038 :         if (retval)
    7780         284 :             CommandCounterIncrement();
    7781             : 
    7782        1038 :         children = find_inheritance_children(RelationGetRelid(rel), lockmode);
    7783        1230 :         foreach(lc, children)
    7784             :         {
    7785         192 :             Oid         childrelid = lfirst_oid(lc);
    7786             :             Relation    childrel;
    7787             :             AttrNumber  childattno;
    7788             : 
    7789             :             /* find_inheritance_children already got lock */
    7790         192 :             childrel = table_open(childrelid, NoLock);
    7791         192 :             CheckTableNotInUse(childrel, "ALTER TABLE");
    7792             : 
    7793         192 :             childattno = get_attnum(RelationGetRelid(childrel),
    7794         192 :                                     get_attname(RelationGetRelid(rel), attnum,
    7795             :                                                 false));
    7796         192 :             retval |= set_attnotnull(wqueue, childrel, childattno,
    7797             :                                      recurse, lockmode);
    7798         192 :             table_close(childrel, NoLock);
    7799             :         }
    7800             :     }
    7801             : 
    7802       23660 :     return retval;
    7803             : }
    7804             : 
    7805             : /*
    7806             :  * ALTER TABLE ALTER COLUMN SET NOT NULL
    7807             :  *
    7808             :  * Add a not-null constraint to a single table and its children.  Returns
    7809             :  * the address of the constraint added to the parent relation, if one gets
    7810             :  * added, or InvalidObjectAddress otherwise.
    7811             :  *
    7812             :  * We must recurse to child tables during execution, rather than using
    7813             :  * ALTER TABLE's normal prep-time recursion.
    7814             :  */
    7815             : static ObjectAddress
    7816         496 : ATExecSetNotNull(List **wqueue, Relation rel, char *conName, char *colName,
    7817             :                  bool recurse, bool recursing, List **readyRels,
    7818             :                  LOCKMODE lockmode)
    7819             : {
    7820             :     HeapTuple   tuple;
    7821             :     Relation    constr_rel;
    7822             :     ScanKeyData skey;
    7823             :     SysScanDesc conscan;
    7824             :     AttrNumber  attnum;
    7825             :     ObjectAddress address;
    7826             :     Constraint *constraint;
    7827             :     CookedConstraint *ccon;
    7828             :     List       *cooked;
    7829         496 :     bool        is_no_inherit = false;
    7830         496 :     List       *ready = NIL;
    7831             : 
    7832             :     /* Guard against stack overflow due to overly deep inheritance tree. */
    7833         496 :     check_stack_depth();
    7834             : 
    7835             :     /*
    7836             :      * In cases of multiple inheritance, we might visit the same child more
    7837             :      * than once.  In the topmost call, set up a list that we fill with all
    7838             :      * visited relations, to skip those.
    7839             :      */
    7840         496 :     if (readyRels == NULL)
    7841             :     {
    7842             :         Assert(!recursing);
    7843         366 :         readyRels = &ready;
    7844             :     }
    7845         496 :     if (list_member_oid(*readyRels, RelationGetRelid(rel)))
    7846           6 :         return InvalidObjectAddress;
    7847         490 :     *readyRels = lappend_oid(*readyRels, RelationGetRelid(rel));
    7848             : 
    7849             :     /* At top level, permission check was done in ATPrepCmd, else do it */
    7850         490 :     if (recursing)
    7851             :     {
    7852         124 :         ATSimplePermissions(AT_AddConstraint, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
    7853             :         Assert(conName != NULL);
    7854             :     }
    7855             : 
    7856         490 :     attnum = get_attnum(RelationGetRelid(rel), colName);
    7857         490 :     if (attnum == InvalidAttrNumber)
    7858          18 :         ereport(ERROR,
    7859             :                 (errcode(ERRCODE_UNDEFINED_COLUMN),
    7860             :                  errmsg("column \"%s\" of relation \"%s\" does not exist",
    7861             :                         colName, RelationGetRelationName(rel))));
    7862             : 
    7863             :     /* Prevent them from altering a system attribute */
    7864         472 :     if (attnum <= 0)
    7865           0 :         ereport(ERROR,
    7866             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    7867             :                  errmsg("cannot alter system column \"%s\"",
    7868             :                         colName)));
    7869             : 
    7870             :     /* See if there's already a constraint */
    7871         472 :     constr_rel = table_open(ConstraintRelationId, RowExclusiveLock);
    7872         472 :     ScanKeyInit(&skey,
    7873             :                 Anum_pg_constraint_conrelid,
    7874             :                 BTEqualStrategyNumber, F_OIDEQ,
    7875             :                 ObjectIdGetDatum(RelationGetRelid(rel)));
    7876         472 :     conscan = systable_beginscan(constr_rel, ConstraintRelidTypidNameIndexId, true,
    7877             :                                  NULL, 1, &skey);
    7878             : 
    7879         898 :     while (HeapTupleIsValid(tuple = systable_getnext(conscan)))
    7880             :     {
    7881         446 :         Form_pg_constraint conForm = (Form_pg_constraint) GETSTRUCT(tuple);
    7882         446 :         bool        changed = false;
    7883             :         HeapTuple   copytup;
    7884             : 
    7885         446 :         if (conForm->contype != CONSTRAINT_NOTNULL)
    7886         206 :             continue;
    7887             : 
    7888         240 :         if (extractNotNullColumn(tuple) != attnum)
    7889         220 :             continue;
    7890             : 
    7891          20 :         copytup = heap_copytuple(tuple);
    7892          20 :         conForm = (Form_pg_constraint) GETSTRUCT(copytup);
    7893             : 
    7894             :         /*
    7895             :          * If we find an appropriate constraint, we're almost done, but just
    7896             :          * need to change some properties on it: if we're recursing, increment
    7897             :          * coninhcount; if not, set conislocal if not already set.
    7898             :          */
    7899          20 :         if (recursing)
    7900             :         {
    7901           6 :             conForm->coninhcount++;
    7902           6 :             changed = true;
    7903             :         }
    7904          14 :         else if (!conForm->conislocal)
    7905             :         {
    7906           0 :             conForm->conislocal = true;
    7907           0 :             changed = true;
    7908             :         }
    7909             : 
    7910          20 :         if (changed)
    7911             :         {
    7912           6 :             CatalogTupleUpdate(constr_rel, &copytup->t_self, copytup);
    7913           6 :             ObjectAddressSet(address, ConstraintRelationId, conForm->oid);
    7914             :         }
    7915             : 
    7916          20 :         systable_endscan(conscan);
    7917          20 :         table_close(constr_rel, RowExclusiveLock);
    7918             : 
    7919          20 :         if (changed)
    7920           6 :             return address;
    7921             :         else
    7922          14 :             return InvalidObjectAddress;
    7923             :     }
    7924             : 
    7925         452 :     systable_endscan(conscan);
    7926         452 :     table_close(constr_rel, RowExclusiveLock);
    7927             : 
    7928             :     /*
    7929             :      * If we're asked not to recurse, and children exist, raise an error for
    7930             :      * partitioned tables.  For inheritance, we act as if NO INHERIT had been
    7931             :      * specified.
    7932             :      */
    7933         482 :     if (!recurse &&
    7934          30 :         find_inheritance_children(RelationGetRelid(rel),
    7935             :                                   NoLock) != NIL)
    7936             :     {
    7937          24 :         if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
    7938          12 :             ereport(ERROR,
    7939             :                     errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    7940             :                     errmsg("constraint must be added to child tables too"),
    7941             :                     errhint("Do not specify the ONLY keyword."));
    7942             :         else
    7943          12 :             is_no_inherit = true;
    7944             :     }
    7945             : 
    7946             :     /*
    7947             :      * No constraint exists; we must add one.  First determine a name to use,
    7948             :      * if we haven't already.
    7949             :      */
    7950         440 :     if (!recursing)
    7951             :     {
    7952             :         Assert(conName == NULL);
    7953         322 :         conName = ChooseConstraintName(RelationGetRelationName(rel),
    7954             :                                        colName, "not_null",
    7955         322 :                                        RelationGetNamespace(rel),
    7956             :                                        NIL);
    7957             :     }
    7958         440 :     constraint = makeNode(Constraint);
    7959         440 :     constraint->contype = CONSTR_NOTNULL;
    7960         440 :     constraint->conname = conName;
    7961         440 :     constraint->deferrable = false;
    7962         440 :     constraint->initdeferred = false;
    7963         440 :     constraint->location = -1;
    7964         440 :     constraint->keys = list_make1(makeString(colName));
    7965         440 :     constraint->is_no_inherit = is_no_inherit;
    7966         440 :     constraint->inhcount = recursing ? 1 : 0;
    7967         440 :     constraint->skip_validation = false;
    7968         440 :     constraint->initially_valid = true;
    7969             : 
    7970             :     /* and do it */
    7971         440 :     cooked = AddRelationNewConstraints(rel, NIL, list_make1(constraint),
    7972         440 :                                        false, !recursing, false, NULL);
    7973         440 :     ccon = linitial(cooked);
    7974         440 :     ObjectAddressSet(address, ConstraintRelationId, ccon->conoid);
    7975             : 
    7976         440 :     InvokeObjectPostAlterHook(RelationRelationId,
    7977             :                               RelationGetRelid(rel), attnum);
    7978             : 
    7979             :     /*
    7980             :      * Mark pg_attribute.attnotnull for the column. Tell that function not to
    7981             :      * recurse, because we're going to do it here.
    7982             :      */
    7983         440 :     set_attnotnull(wqueue, rel, attnum, false, lockmode);
    7984             : 
    7985             :     /*
    7986             :      * Recurse to propagate the constraint to children that don't have one.
    7987             :      */
    7988         440 :     if (recurse)
    7989             :     {
    7990             :         List       *children;
    7991             :         ListCell   *lc;
    7992             : 
    7993         422 :         children = find_inheritance_children(RelationGetRelid(rel),
    7994             :                                              lockmode);
    7995             : 
    7996         552 :         foreach(lc, children)
    7997             :         {
    7998             :             Relation    childrel;
    7999             : 
    8000         130 :             childrel = table_open(lfirst_oid(lc), NoLock);
    8001             : 
    8002         130 :             ATExecSetNotNull(wqueue, childrel,
    8003             :                              conName, colName, recurse, true,
    8004             :                              readyRels, lockmode);
    8005             : 
    8006         130 :             table_close(childrel, NoLock);
    8007             :         }
    8008             :     }
    8009             : 
    8010         440 :     return address;
    8011             : }
    8012             : 
    8013             : /*
    8014             :  * ALTER TABLE ALTER COLUMN SET ATTNOTNULL
    8015             :  *
    8016             :  * This doesn't exist in the grammar; it's used when creating a
    8017             :  * primary key and the column is not already marked attnotnull.
    8018             :  */
    8019             : static ObjectAddress
    8020       14656 : ATExecSetAttNotNull(List **wqueue, Relation rel,
    8021             :                     const char *colName, LOCKMODE lockmode)
    8022             : {
    8023             :     AttrNumber  attnum;
    8024       14656 :     ObjectAddress address = InvalidObjectAddress;
    8025             : 
    8026       14656 :     attnum = get_attnum(RelationGetRelid(rel), colName);
    8027       14656 :     if (attnum == InvalidAttrNumber)
    8028          18 :         ereport(ERROR,
    8029             :                 errcode(ERRCODE_UNDEFINED_COLUMN),
    8030             :                 errmsg("column \"%s\" of relation \"%s\" does not exist",
    8031             :                        colName, RelationGetRelationName(rel)));
    8032             : 
    8033             :     /*
    8034             :      * Make the change, if necessary, and only if so report the column as
    8035             :      * changed
    8036             :      */
    8037       14638 :     if (set_attnotnull(wqueue, rel, attnum, false, lockmode))
    8038         844 :         ObjectAddressSubSet(address, RelationRelationId,
    8039             :                             RelationGetRelid(rel), attnum);
    8040             : 
    8041       14638 :     return address;
    8042             : }
    8043             : 
    8044             : /*
    8045             :  * NotNullImpliedByRelConstraints
    8046             :  *      Does rel's existing constraints imply NOT NULL for the given attribute?
    8047             :  */
    8048             : static bool
    8049        1566 : NotNullImpliedByRelConstraints(Relation rel, Form_pg_attribute attr)
    8050             : {
    8051        1566 :     NullTest   *nnulltest = makeNode(NullTest);
    8052             : 
    8053        3132 :     nnulltest->arg = (Expr *) makeVar(1,
    8054        1566 :                                       attr->attnum,
    8055             :                                       attr->atttypid,
    8056             :                                       attr->atttypmod,
    8057             :                                       attr->attcollation,
    8058             :                                       0);
    8059        1566 :     nnulltest->nulltesttype = IS_NOT_NULL;
    8060             : 
    8061             :     /*
    8062             :      * argisrow = false is correct even for a composite column, because
    8063             :      * attnotnull does not represent a SQL-spec IS NOT NULL test in such a
    8064             :      * case, just IS DISTINCT FROM NULL.
    8065             :      */
    8066        1566 :     nnulltest->argisrow = false;
    8067        1566 :     nnulltest->location = -1;
    8068             : 
    8069        1566 :     if (ConstraintImpliedByRelConstraint(rel, list_make1(nnulltest), NIL))
    8070             :     {
    8071          50 :         ereport(DEBUG1,
    8072             :                 (errmsg_internal("existing constraints on column \"%s.%s\" are sufficient to prove that it does not contain nulls",
    8073             :                                  RelationGetRelationName(rel), NameStr(attr->attname))));
    8074          50 :         return true;
    8075             :     }
    8076             : 
    8077        1516 :     return false;
    8078             : }
    8079             : 
    8080             : /*
    8081             :  * ALTER TABLE ALTER COLUMN SET/DROP DEFAULT
    8082             :  *
    8083             :  * Return the address of the affected column.
    8084             :  */
    8085             : static ObjectAddress
    8086         566 : ATExecColumnDefault(Relation rel, const char *colName,
    8087             :                     Node *newDefault, LOCKMODE lockmode)
    8088             : {
    8089         566 :     TupleDesc   tupdesc = RelationGetDescr(rel);
    8090             :     AttrNumber  attnum;
    8091             :     ObjectAddress address;
    8092             : 
    8093             :     /*
    8094             :      * get the number of the attribute
    8095             :      */
    8096         566 :     attnum = get_attnum(RelationGetRelid(rel), colName);
    8097         566 :     if (attnum == InvalidAttrNumber)
    8098          30 :         ereport(ERROR,
    8099             :                 (errcode(ERRCODE_UNDEFINED_COLUMN),
    8100             :                  errmsg("column \"%s\" of relation \"%s\" does not exist",
    8101             :                         colName, RelationGetRelationName(rel))));
    8102             : 
    8103             :     /* Prevent them from altering a system attribute */
    8104         536 :     if (attnum <= 0)
    8105           0 :         ereport(ERROR,
    8106             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    8107             :                  errmsg("cannot alter system column \"%s\"",
    8108             :                         colName)));
    8109             : 
    8110         536 :     if (TupleDescAttr(tupdesc, attnum - 1)->attidentity)
    8111          18 :         ereport(ERROR,
    8112             :                 (errcode(ERRCODE_SYNTAX_ERROR),
    8113             :                  errmsg("column \"%s\" of relation \"%s\" is an identity column",
    8114             :                         colName, RelationGetRelationName(rel)),
    8115             :         /* translator: %s is an SQL ALTER command */
    8116             :                  newDefault ? 0 : errhint("Use %s instead.",
    8117             :                                           "ALTER TABLE ... ALTER COLUMN ... DROP IDENTITY")));
    8118             : 
    8119         518 :     if (TupleDescAttr(tupdesc, attnum - 1)->attgenerated)
    8120           6 :         ereport(ERROR,
    8121             :                 (errcode(ERRCODE_SYNTAX_ERROR),
    8122             :                  errmsg("column \"%s\" of relation \"%s\" is a generated column",
    8123             :                         colName, RelationGetRelationName(rel)),
    8124             :                  newDefault ?
    8125             :         /* translator: %s is an SQL ALTER command */
    8126             :                  errhint("Use %s instead.", "ALTER TABLE ... ALTER COLUMN ... SET EXPRESSION") :
    8127             :                  (TupleDescAttr(tupdesc, attnum - 1)->attgenerated == ATTRIBUTE_GENERATED_STORED ?
    8128             :                   errhint("Use %s instead.", "ALTER TABLE ... ALTER COLUMN ... DROP EXPRESSION") : 0)));
    8129             : 
    8130             :     /*
    8131             :      * Remove any old default for the column.  We use RESTRICT here for
    8132             :      * safety, but at present we do not expect anything to depend on the
    8133             :      * default.
    8134             :      *
    8135             :      * We treat removing the existing default as an internal operation when it
    8136             :      * is preparatory to adding a new default, but as a user-initiated
    8137             :      * operation when the user asked for a drop.
    8138             :      */
    8139         512 :     RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT, false,
    8140             :                       newDefault != NULL);
    8141             : 
    8142         512 :     if (newDefault)
    8143             :     {
    8144             :         /* SET DEFAULT */
    8145             :         RawColumnDefault *rawEnt;
    8146             : 
    8147         338 :         rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
    8148         338 :         rawEnt->attnum = attnum;
    8149         338 :         rawEnt->raw_default = newDefault;
    8150         338 :         rawEnt->missingMode = false;
    8151         338 :         rawEnt->generated = '\0';
    8152             : 
    8153             :         /*
    8154             :          * This function is intended for CREATE TABLE, so it processes a
    8155             :          * _list_ of defaults, but we just do one.
    8156             :          */
    8157         338 :         AddRelationNewConstraints(rel, list_make1(rawEnt), NIL,
    8158             :                                   false, true, false, NULL);
    8159             :     }
    8160             : 
    8161         506 :     ObjectAddressSubSet(address, RelationRelationId,
    8162             :                         RelationGetRelid(rel), attnum);
    8163         506 :     return address;
    8164             : }
    8165             : 
    8166             : /*
    8167             :  * Add a pre-cooked default expression.
    8168             :  *
    8169             :  * Return the address of the affected column.
    8170             :  */
    8171             : static ObjectAddress
    8172         110 : ATExecCookedColumnDefault(Relation rel, AttrNumber attnum,
    8173             :                           Node *newDefault)
    8174             : {
    8175             :     ObjectAddress address;
    8176             : 
    8177             :     /* We assume no checking is required */
    8178             : 
    8179             :     /*
    8180             :      * Remove any old default for the column.  We use RESTRICT here for
    8181             :      * safety, but at present we do not expect anything to depend on the
    8182             :      * default.  (In ordinary cases, there could not be a default in place
    8183             :      * anyway, but it's possible when combining LIKE with inheritance.)
    8184             :      */
    8185         110 :     RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT, false,
    8186             :                       true);
    8187             : 
    8188         110 :     (void) StoreAttrDefault(rel, attnum, newDefault, true, false);
    8189             : 
    8190         110 :     ObjectAddressSubSet(address, RelationRelationId,
    8191             :                         RelationGetRelid(rel), attnum);
    8192         110 :     return address;
    8193             : }
    8194             : 
    8195             : /*
    8196             :  * ALTER TABLE ALTER COLUMN ADD IDENTITY
    8197             :  *
    8198             :  * Return the address of the affected column.
    8199             :  */
    8200             : static ObjectAddress
    8201         150 : ATExecAddIdentity(Relation rel, const char *colName,
    8202             :                   Node *def, LOCKMODE lockmode, bool recurse, bool recursing)
    8203             : {
    8204             :     Relation    attrelation;
    8205             :     HeapTuple   tuple;
    8206             :     Form_pg_attribute attTup;
    8207             :     AttrNumber  attnum;
    8208             :     ObjectAddress address;
    8209         150 :     ColumnDef  *cdef = castNode(ColumnDef, def);
    8210             :     bool        ispartitioned;
    8211             : 
    8212         150 :     ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
    8213         150 :     if (ispartitioned && !recurse)
    8214           0 :         ereport(ERROR,
    8215             :                 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    8216             :                  errmsg("cannot add identity to a column of only the partitioned table"),
    8217             :                  errhint("Do not specify the ONLY keyword.")));
    8218             : 
    8219         150 :     if (rel->rd_rel->relispartition && !recursing)
    8220          12 :         ereport(ERROR,
    8221             :                 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    8222             :                 errmsg("cannot add identity to a column of a partition"));
    8223             : 
    8224         138 :     attrelation = table_open(AttributeRelationId, RowExclusiveLock);
    8225             : 
    8226         138 :     tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
    8227         138 :     if (!HeapTupleIsValid(tuple))
    8228           0 :         ereport(ERROR,
    8229             :                 (errcode(ERRCODE_UNDEFINED_COLUMN),
    8230             :                  errmsg("column \"%s\" of relation \"%s\" does not exist",
    8231             :                         colName, RelationGetRelationName(rel))));
    8232         138 :     attTup = (Form_pg_attribute) GETSTRUCT(tuple);
    8233         138 :     attnum = attTup->attnum;
    8234             : 
    8235             :     /* Can't alter a system attribute */
    8236         138 :     if (attnum <= 0)
    8237           0 :         ereport(ERROR,
    8238             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    8239             :                  errmsg("cannot alter system column \"%s\"",
    8240             :                         colName)));
    8241             : 
    8242             :     /*
    8243             :      * Creating a column as identity implies NOT NULL, so adding the identity
    8244             :      * to an existing column that is not NOT NULL would create a state that
    8245             :      * cannot be reproduced without contortions.
    8246             :      */
    8247         138 :     if (!attTup->attnotnull)
    8248           6 :         ereport(ERROR,
    8249             :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    8250             :                  errmsg("column \"%s\" of relation \"%s\" must be declared NOT NULL before identity can be added",
    8251             :                         colName, RelationGetRelationName(rel))));
    8252             : 
    8253         132 :     if (attTup->attidentity)
    8254          18 :         ereport(ERROR,
    8255             :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    8256             :                  errmsg("column \"%s\" of relation \"%s\" is already an identity column",
    8257             :                         colName, RelationGetRelationName(rel))));
    8258             : 
    8259         114 :     if (attTup->atthasdef)
    8260           6 :         ereport(ERROR,
    8261             :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    8262             :                  errmsg("column \"%s\" of relation \"%s\" already has a default value",
    8263             :                         colName, RelationGetRelationName(rel))));
    8264             : 
    8265         108 :     attTup->attidentity = cdef->identity;
    8266         108 :     CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
    8267             : 
    8268         108 :     InvokeObjectPostAlterHook(RelationRelationId,
    8269             :                               RelationGetRelid(rel),
    8270             :                               attTup->attnum);
    8271         108 :     ObjectAddressSubSet(address, RelationRelationId,
    8272             :                         RelationGetRelid(rel), attnum);
    8273         108 :     heap_freetuple(tuple);
    8274             : 
    8275         108 :     table_close(attrelation, RowExclusiveLock);
    8276             : 
    8277             :     /*
    8278             :      * Recurse to propagate the identity column to partitions.  Identity is
    8279             :      * not inherited in regular inheritance children.
    8280             :      */
    8281         108 :     if (recurse && ispartitioned)
    8282             :     {
    8283             :         List       *children;
    8284             :         ListCell   *lc;
    8285             : 
    8286          10 :         children = find_inheritance_children(RelationGetRelid(rel), lockmode);
    8287             : 
    8288          16 :         foreach(lc, children)
    8289             :         {
    8290             :             Relation    childrel;
    8291             : 
    8292           6 :             childrel = table_open(lfirst_oid(lc), NoLock);
    8293           6 :             ATExecAddIdentity(childrel, colName, def, lockmode, recurse, true);
    8294           6 :             table_close(childrel, NoLock);
    8295             :         }
    8296             :     }
    8297             : 
    8298         108 :     return address;
    8299             : }
    8300             : 
    8301             : /*
    8302             :  * ALTER TABLE ALTER COLUMN SET { GENERATED or sequence options }
    8303             :  *
    8304             :  * Return the address of the affected column.
    8305             :  */
    8306             : static ObjectAddress
    8307          74 : ATExecSetIdentity(Relation rel, const char *colName, Node *def,
    8308             :                   LOCKMODE lockmode, bool recurse, bool recursing)
    8309             : {
    8310             :     ListCell   *option;
    8311          74 :     DefElem    *generatedEl = NULL;
    8312             :     HeapTuple   tuple;
    8313             :     Form_pg_attribute attTup;
    8314             :     AttrNumber  attnum;
    8315             :     Relation    attrelation;
    8316             :     ObjectAddress address;
    8317             :     bool        ispartitioned;
    8318             : 
    8319          74 :     ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
    8320          74 :     if (ispartitioned && !recurse)
    8321           6 :         ereport(ERROR,
    8322             :                 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    8323             :                  errmsg("cannot change identity column of only the partitioned table"),
    8324             :                  errhint("Do not specify the ONLY keyword.")));
    8325             : 
    8326          68 :     if (rel->rd_rel->relispartition && !recursing)
    8327          12 :         ereport(ERROR,
    8328             :                 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    8329             :                 errmsg("cannot change identity column of a partition"));
    8330             : 
    8331         100 :     foreach(option, castNode(List, def))
    8332             :     {
    8333          44 :         DefElem    *defel = lfirst_node(DefElem, option);
    8334             : 
    8335          44 :         if (strcmp(defel->defname, "generated") == 0)
    8336             :         {
    8337          44 :             if (generatedEl)
    8338           0 :                 ereport(ERROR,
    8339             :                         (errcode(ERRCODE_SYNTAX_ERROR),
    8340             :                          errmsg("conflicting or redundant options")));
    8341          44 :             generatedEl = defel;
    8342             :         }
    8343             :         else
    8344           0 :             elog(ERROR, "option \"%s\" not recognized",
    8345             :                  defel->defname);
    8346             :     }
    8347             : 
    8348             :     /*
    8349             :      * Even if there is nothing to change here, we run all the checks.  There
    8350             :      * will be a subsequent ALTER SEQUENCE that relies on everything being
    8351             :      * there.
    8352             :      */
    8353             : 
    8354          56 :     attrelation = table_open(AttributeRelationId, RowExclusiveLock);
    8355          56 :     tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
    8356          56 :     if (!HeapTupleIsValid(tuple))
    8357           0 :         ereport(ERROR,
    8358             :                 (errcode(ERRCODE_UNDEFINED_COLUMN),
    8359             :                  errmsg("column \"%s\" of relation \"%s\" does not exist",
    8360             :                         colName, RelationGetRelationName(rel))));
    8361             : 
    8362          56 :     attTup = (Form_pg_attribute) GETSTRUCT(tuple);
    8363          56 :     attnum = attTup->attnum;
    8364             : 
    8365          56 :     if (attnum <= 0)
    8366           0 :         ereport(ERROR,
    8367             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    8368             :                  errmsg("cannot alter system column \"%s\"",
    8369             :                         colName)));
    8370             : 
    8371          56 :     if (!attTup->attidentity)
    8372           6 :         ereport(ERROR,
    8373             :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    8374             :                  errmsg("column \"%s\" of relation \"%s\" is not an identity column",
    8375             :                         colName, RelationGetRelationName(rel))));
    8376             : 
    8377          50 :     if (generatedEl)
    8378             :     {
    8379          44 :         attTup->attidentity = defGetInt32(generatedEl);
    8380          44 :         CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
    8381             : 
    8382          44 :         InvokeObjectPostAlterHook(RelationRelationId,
    8383             :                                   RelationGetRelid(rel),
    8384             :                                   attTup->attnum);
    8385          44 :         ObjectAddressSubSet(address, RelationRelationId,
    8386             :                             RelationGetRelid(rel), attnum);
    8387             :     }
    8388             :     else
    8389           6 :         address = InvalidObjectAddress;
    8390             : 
    8391          50 :     heap_freetuple(tuple);
    8392          50 :     table_close(attrelation, RowExclusiveLock);
    8393             : 
    8394             :     /*
    8395             :      * Recurse to propagate the identity change to partitions. Identity is not
    8396             :      * inherited in regular inheritance children.
    8397             :      */
    8398          50 :     if (generatedEl && recurse && ispartitioned)
    8399             :     {
    8400             :         List       *children;
    8401             :         ListCell   *lc;
    8402             : 
    8403           6 :         children = find_inheritance_children(RelationGetRelid(rel), lockmode);
    8404             : 
    8405          18 :         foreach(lc, children)
    8406             :         {
    8407             :             Relation    childrel;
    8408             : 
    8409          12 :             childrel = table_open(lfirst_oid(lc), NoLock);
    8410          12 :             ATExecSetIdentity(childrel, colName, def, lockmode, recurse, true);
    8411          12 :             table_close(childrel, NoLock);
    8412             :         }
    8413             :     }
    8414             : 
    8415          50 :     return address;
    8416             : }
    8417             : 
    8418             : /*
    8419             :  * ALTER TABLE ALTER COLUMN DROP IDENTITY
    8420             :  *
    8421             :  * Return the address of the affected column.
    8422             :  */
    8423             : static ObjectAddress
    8424          98 : ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode,
    8425             :                    bool recurse, bool recursing)
    8426             : {
    8427             :     HeapTuple   tuple;
    8428             :     Form_pg_attribute attTup;
    8429             :     AttrNumber  attnum;
    8430             :     Relation    attrelation;
    8431             :     ObjectAddress address;
    8432             :     Oid         seqid;
    8433             :     ObjectAddress seqaddress;
    8434             :     bool        ispartitioned;
    8435             : 
    8436          98 :     ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
    8437          98 :     if (ispartitioned && !recurse)
    8438           6 :         ereport(ERROR,
    8439             :                 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    8440             :                  errmsg("cannot drop identity from a column of only the partitioned table"),
    8441             :                  errhint("Do not specify the ONLY keyword.")));
    8442             : 
    8443          92 :     if (rel->rd_rel->relispartition && !recursing)
    8444           6 :         ereport(ERROR,
    8445             :                 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    8446             :                 errmsg("cannot drop identity from a column of a partition"));
    8447             : 
    8448          86 :     attrelation = table_open(AttributeRelationId, RowExclusiveLock);
    8449          86 :     tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
    8450          86 :     if (!HeapTupleIsValid(tuple))
    8451           0 :         ereport(ERROR,
    8452             :                 (errcode(ERRCODE_UNDEFINED_COLUMN),
    8453             :                  errmsg("column \"%s\" of relation \"%s\" does not exist",
    8454             :                         colName, RelationGetRelationName(rel))));
    8455             : 
    8456          86 :     attTup = (Form_pg_attribute) GETSTRUCT(tuple);
    8457          86 :     attnum = attTup->attnum;
    8458             : 
    8459          86 :     if (attnum <= 0)
    8460           0 :         ereport(ERROR,
    8461             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    8462             :                  errmsg("cannot alter system column \"%s\"",
    8463             :                         colName)));
    8464             : 
    8465          86 :     if (!attTup->attidentity)
    8466             :     {
    8467          12 :         if (!missing_ok)
    8468           6 :             ereport(ERROR,
    8469             :                     (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    8470             :                      errmsg("column \"%s\" of relation \"%s\" is not an identity column",
    8471             :                             colName, RelationGetRelationName(rel))));
    8472             :         else
    8473             :         {
    8474           6 :             ereport(NOTICE,
    8475             :                     (errmsg("column \"%s\" of relation \"%s\" is not an identity column, skipping",
    8476             :                             colName, RelationGetRelationName(rel))));
    8477           6 :             heap_freetuple(tuple);
    8478           6 :             table_close(attrelation, RowExclusiveLock);
    8479           6 :             return InvalidObjectAddress;
    8480             :         }
    8481             :     }
    8482             : 
    8483          74 :     attTup->attidentity = '\0';
    8484          74 :     CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
    8485             : 
    8486          74 :     InvokeObjectPostAlterHook(RelationRelationId,
    8487             :                               RelationGetRelid(rel),
    8488             :                               attTup->attnum);
    8489          74 :     ObjectAddressSubSet(address, RelationRelationId,
    8490             :                         RelationGetRelid(rel), attnum);
    8491          74 :     heap_freetuple(tuple);
    8492             : 
    8493          74 :     table_close(attrelation, RowExclusiveLock);
    8494             : 
    8495             :     /*
    8496             :      * Recurse to drop the identity from column in partitions.  Identity is
    8497             :      * not inherited in regular inheritance children so ignore them.
    8498             :      */
    8499          74 :     if (recurse && ispartitioned)
    8500             :     {
    8501             :         List       *children;
    8502             :         ListCell   *lc;
    8503             : 
    8504           6 :         children = find_inheritance_children(RelationGetRelid(rel), lockmode);
    8505             : 
    8506          12 :         foreach(lc, children)
    8507             :         {
    8508             :             Relation    childrel;
    8509             : 
    8510           6 :             childrel = table_open(lfirst_oid(lc), NoLock);
    8511           6 :             ATExecDropIdentity(childrel, colName, false, lockmode, recurse, true);
    8512           6 :             table_close(childrel, NoLock);
    8513             :         }
    8514             :     }
    8515             : 
    8516          74 :     if (!recursing)
    8517             :     {
    8518             :         /* drop the internal sequence */
    8519          38 :         seqid = getIdentitySequence(RelationGetRelid(rel), attnum, false);
    8520          38 :         deleteDependencyRecordsForClass(RelationRelationId, seqid,
    8521             :                                         RelationRelationId, DEPENDENCY_INTERNAL);
    8522          38 :         CommandCounterIncrement();
    8523          38 :         seqaddress.classId = RelationRelationId;
    8524          38 :         seqaddress.objectId = seqid;
    8525          38 :         seqaddress.objectSubId = 0;
    8526          38 :         performDeletion(&seqaddress, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
    8527             :     }
    8528             : 
    8529          74 :     return address;
    8530             : }
    8531             : 
    8532             : /*
    8533             :  * ALTER TABLE ALTER COLUMN SET EXPRESSION
    8534             :  *
    8535             :  * Return the address of the affected column.
    8536             :  */
    8537             : static ObjectAddress
    8538          84 : ATExecSetExpression(AlteredTableInfo *tab, Relation rel, const char *colName,
    8539             :                     Node *newExpr, LOCKMODE lockmode)
    8540             : {
    8541             :     HeapTuple   tuple;
    8542             :     Form_pg_attribute attTup;
    8543             :     AttrNumber  attnum;
    8544             :     Oid         attrdefoid;
    8545             :     ObjectAddress address;
    8546             :     Expr       *defval;
    8547             :     NewColumnValue *newval;
    8548             :     RawColumnDefault *rawEnt;
    8549             : 
    8550          84 :     tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
    8551          84 :     if (!HeapTupleIsValid(tuple))
    8552           0 :         ereport(ERROR,
    8553             :                 (errcode(ERRCODE_UNDEFINED_COLUMN),
    8554             :                  errmsg("column \"%s\" of relation \"%s\" does not exist",
    8555             :                         colName, RelationGetRelationName(rel))));
    8556             : 
    8557          84 :     attTup = (Form_pg_attribute) GETSTRUCT(tuple);
    8558          84 :     attnum = attTup->attnum;
    8559             : 
    8560          84 :     if (attnum <= 0)
    8561           0 :         ereport(ERROR,
    8562             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    8563             :                  errmsg("cannot alter system column \"%s\"",
    8564             :                         colName)));
    8565             : 
    8566          84 :     if (attTup->attgenerated != ATTRIBUTE_GENERATED_STORED)
    8567           6 :         ereport(ERROR,
    8568             :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    8569             :                  errmsg("column \"%s\" of relation \"%s\" is not a generated column",
    8570             :                         colName, RelationGetRelationName(rel))));
    8571          78 :     ReleaseSysCache(tuple);
    8572             : 
    8573             :     /*
    8574             :      * Clear all the missing values if we're rewriting the table, since this
    8575             :      * renders them pointless.
    8576             :      */
    8577          78 :     RelationClearMissing(rel);
    8578             : 
    8579             :     /* make sure we don't conflict with later attribute modifications */
    8580          78 :     CommandCounterIncrement();
    8581             : 
    8582             :     /*
    8583             :      * Find everything that depends on the column (constraints, indexes, etc),
    8584             :      * and record enough information to let us recreate the objects after
    8585             :      * rewrite.
    8586             :      */
    8587          78 :     RememberAllDependentForRebuilding(tab, AT_SetExpression, rel, attnum, colName);
    8588             : 
    8589             :     /*
    8590             :      * Drop the dependency records of the GENERATED expression, in particular
    8591             :      * its INTERNAL dependency on the column, which would otherwise cause
    8592             :      * dependency.c to refuse to perform the deletion.
    8593             :      */
    8594          78 :     attrdefoid = GetAttrDefaultOid(RelationGetRelid(rel), attnum);
    8595          78 :     if (!OidIsValid(attrdefoid))
    8596           0 :         elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
    8597             :              RelationGetRelid(rel), attnum);
    8598          78 :     (void) deleteDependencyRecordsFor(AttrDefaultRelationId, attrdefoid, false);
    8599             : 
    8600             :     /* Make above changes visible */
    8601          78 :     CommandCounterIncrement();
    8602             : 
    8603             :     /*
    8604             :      * Get rid of the GENERATED expression itself.  We use RESTRICT here for
    8605             :      * safety, but at present we do not expect anything to depend on the
    8606             :      * expression.
    8607             :      */
    8608          78 :     RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT,
    8609             :                       false, false);
    8610             : 
    8611             :     /* Prepare to store the new expression, in the catalogs */
    8612          78 :     rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
    8613          78 :     rawEnt->attnum = attnum;
    8614          78 :     rawEnt->raw_default = newExpr;
    8615          78 :     rawEnt->missingMode = false;
    8616          78 :     rawEnt->generated = ATTRIBUTE_GENERATED_STORED;
    8617             : 
    8618             :     /* Store the generated expression */
    8619          78 :     AddRelationNewConstraints(rel, list_make1(rawEnt), NIL,
    8620             :                               false, true, false, NULL);
    8621             : 
    8622             :     /* Make above new expression visible */
    8623          78 :     CommandCounterIncrement();
    8624             : 
    8625             :     /* Prepare for table rewrite */
    8626          78 :     defval = (Expr *) build_column_default(rel, attnum);
    8627             : 
    8628          78 :     newval = (NewColumnValue *) palloc0(sizeof(NewColumnValue));
    8629          78 :     newval->attnum = attnum;
    8630          78 :     newval->expr = expression_planner(defval);
    8631          78 :     newval->is_generated = true;
    8632             : 
    8633          78 :     tab->newvals = lappend(tab->newvals, newval);
    8634          78 :     tab->rewrite |= AT_REWRITE_DEFAULT_VAL;
    8635             : 
    8636             :     /* Drop any pg_statistic entry for the column */
    8637          78 :     RemoveStatistics(RelationGetRelid(rel), attnum);
    8638             : 
    8639          78 :     InvokeObjectPostAlterHook(RelationRelationId,
    8640             :                               RelationGetRelid(rel), attnum);
    8641             : 
    8642          78 :     ObjectAddressSubSet(address, RelationRelationId,
    8643             :                         RelationGetRelid(rel), attnum);
    8644          78 :     return address;
    8645             : }
    8646             : 
    8647             : /*
    8648             :  * ALTER TABLE ALTER COLUMN DROP EXPRESSION
    8649             :  */
    8650             : static void
    8651          44 : ATPrepDropExpression(Relation rel, AlterTableCmd *cmd, bool recurse, bool recursing, LOCKMODE lockmode)
    8652             : {
    8653             :     /*
    8654             :      * Reject ONLY if there are child tables.  We could implement this, but it
    8655             :      * is a bit complicated.  GENERATED clauses must be attached to the column
    8656             :      * definition and cannot be added later like DEFAULT, so if a child table
    8657             :      * has a generation expression that the parent does not have, the child
    8658             :      * column will necessarily be an attislocal column.  So to implement ONLY
    8659             :      * here, we'd need extra code to update attislocal of the direct child
    8660             :      * tables, somewhat similar to how DROP COLUMN does it, so that the
    8661             :      * resulting state can be properly dumped and restored.
    8662             :      */
    8663          56 :     if (!recurse &&
    8664          12 :         find_inheritance_children(RelationGetRelid(rel), lockmode))
    8665           6 :         ereport(ERROR,
    8666             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    8667             :                  errmsg("ALTER TABLE / DROP EXPRESSION must be applied to child tables too")));
    8668             : 
    8669             :     /*
    8670             :      * Cannot drop generation expression from inherited columns.
    8671             :      */
    8672          38 :     if (!recursing)
    8673             :     {
    8674             :         HeapTuple   tuple;
    8675             :         Form_pg_attribute attTup;
    8676             : 
    8677          32 :         tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), cmd->name);
    8678          32 :         if (!HeapTupleIsValid(tuple))
    8679           0 :             ereport(ERROR,
    8680             :                     (errcode(ERRCODE_UNDEFINED_COLUMN),
    8681             :                      errmsg("column \"%s\" of relation \"%s\" does not exist",
    8682             :                             cmd->name, RelationGetRelationName(rel))));
    8683             : 
    8684          32 :         attTup = (Form_pg_attribute) GETSTRUCT(tuple);
    8685             : 
    8686          32 :         if (attTup->attinhcount > 0)
    8687           6 :             ereport(ERROR,
    8688             :                     (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    8689             :                      errmsg("cannot drop generation expression from inherited column")));
    8690             :     }
    8691          32 : }
    8692             : 
    8693             : /*
    8694             :  * Return the address of the affected column.
    8695             :  */
    8696             : static ObjectAddress
    8697          32 : ATExecDropExpression(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode)
    8698             : {
    8699             :     HeapTuple   tuple;
    8700             :     Form_pg_attribute attTup;
    8701             :     AttrNumber  attnum;
    8702             :     Relation    attrelation;
    8703             :     Oid         attrdefoid;
    8704             :     ObjectAddress address;
    8705             : 
    8706          32 :     attrelation = table_open(AttributeRelationId, RowExclusiveLock);
    8707          32 :     tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
    8708          32 :     if (!HeapTupleIsValid(tuple))
    8709           0 :         ereport(ERROR,
    8710             :                 (errcode(ERRCODE_UNDEFINED_COLUMN),
    8711             :                  errmsg("column \"%s\" of relation \"%s\" does not exist",
    8712             :                         colName, RelationGetRelationName(rel))));
    8713             : 
    8714          32 :     attTup = (Form_pg_attribute) GETSTRUCT(tuple);
    8715          32 :     attnum = attTup->attnum;
    8716             : 
    8717          32 :     if (attnum <= 0)
    8718           0 :         ereport(ERROR,
    8719             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    8720             :                  errmsg("cannot alter system column \"%s\"",
    8721             :                         colName)));
    8722             : 
    8723          32 :     if (attTup->attgenerated != ATTRIBUTE_GENERATED_STORED)
    8724             :     {
    8725          12 :         if (!missing_ok)
    8726           6 :             ereport(ERROR,
    8727             :                     (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    8728             :                      errmsg("column \"%s\" of relation \"%s\" is not a stored generated column",
    8729             :                             colName, RelationGetRelationName(rel))));
    8730             :         else
    8731             :         {
    8732           6 :             ereport(NOTICE,
    8733             :                     (errmsg("column \"%s\" of relation \"%s\" is not a stored generated column, skipping",
    8734             :                             colName, RelationGetRelationName(rel))));
    8735           6 :             heap_freetuple(tuple);
    8736           6 :             table_close(attrelation, RowExclusiveLock);
    8737           6 :             return InvalidObjectAddress;
    8738             :         }
    8739             :     }
    8740             : 
    8741             :     /*
    8742             :      * Mark the column as no longer generated.  (The atthasdef flag needs to
    8743             :      * get cleared too, but RemoveAttrDefault will handle that.)
    8744             :      */
    8745          20 :     attTup->attgenerated = '\0';
    8746          20 :     CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
    8747             : 
    8748          20 :     InvokeObjectPostAlterHook(RelationRelationId,
    8749             :                               RelationGetRelid(rel),
    8750             :                               attnum);
    8751          20 :     heap_freetuple(tuple);
    8752             : 
    8753          20 :     table_close(attrelation, RowExclusiveLock);
    8754             : 
    8755             :     /*
    8756             :      * Drop the dependency records of the GENERATED expression, in particular
    8757             :      * its INTERNAL dependency on the column, which would otherwise cause
    8758             :      * dependency.c to refuse to perform the deletion.
    8759             :      */
    8760          20 :     attrdefoid = GetAttrDefaultOid(RelationGetRelid(rel), attnum);
    8761          20 :     if (!OidIsValid(attrdefoid))
    8762           0 :         elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
    8763             :              RelationGetRelid(rel), attnum);
    8764          20 :     (void) deleteDependencyRecordsFor(AttrDefaultRelationId, attrdefoid, false);
    8765             : 
    8766             :     /* Make above changes visible */
    8767          20 :     CommandCounterIncrement();
    8768             : 
    8769             :     /*
    8770             :      * Get rid of the GENERATED expression itself.  We use RESTRICT here for
    8771             :      * safety, but at present we do not expect anything to depend on the
    8772             :      * default.
    8773             :      */
    8774          20 :     RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT,
    8775             :                       false, false);
    8776             : 
    8777          20 :     ObjectAddressSubSet(address, RelationRelationId,
    8778             :                         RelationGetRelid(rel), attnum);
    8779          20 :     return address;
    8780             : }
    8781             : 
    8782             : /*
    8783             :  * ALTER TABLE ALTER COLUMN SET STATISTICS
    8784             :  *
    8785             :  * Return value is the address of the modified column
    8786             :  */
    8787             : static ObjectAddress
    8788         164 : ATExecSetStatistics(Relation rel, const char *colName, int16 colNum, Node *newValue, LOCKMODE lockmode)
    8789             : {
    8790         164 :     int         newtarget = 0;
    8791             :     bool        newtarget_default;
    8792             :     Relation    attrelation;
    8793             :     HeapTuple   tuple,
    8794             :                 newtuple;
    8795             :     Form_pg_attribute attrtuple;
    8796             :     AttrNumber  attnum;
    8797             :     ObjectAddress address;
    8798             :     Datum       repl_val[Natts_pg_attribute];
    8799             :     bool        repl_null[Natts_pg_attribute];
    8800             :     bool        repl_repl[Natts_pg_attribute];
    8801             : 
    8802             :     /*
    8803             :      * We allow referencing columns by numbers only for indexes, since table
    8804             :      * column numbers could contain gaps if columns are later dropped.
    8805             :      */
    8806         164 :     if (rel->rd_rel->relkind != RELKIND_INDEX &&
    8807         100 :         rel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX &&
    8808             :         !colName)
    8809           0 :         ereport(ERROR,
    8810             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    8811             :                  errmsg("cannot refer to non-index column by number")));
    8812             : 
    8813             :     /* -1 was used in previous versions for the default setting */
    8814         164 :     if (newValue && intVal(newValue) != -1)
    8815             :     {
    8816         120 :         newtarget = intVal(newValue);
    8817         120 :         newtarget_default = false;
    8818             :     }
    8819             :     else
    8820          44 :         newtarget_default = true;
    8821             : 
    8822         164 :     if (!newtarget_default)
    8823             :     {
    8824             :         /*
    8825             :          * Limit target to a sane range
    8826             :          */
    8827         120 :         if (newtarget < 0)
    8828             :         {
    8829           0 :             ereport(ERROR,
    8830             :                     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    8831             :                      errmsg("statistics target %d is too low",
    8832             :                             newtarget)));
    8833             :         }
    8834         120 :         else if (newtarget > MAX_STATISTICS_TARGET)
    8835             :         {
    8836           0 :             newtarget = MAX_STATISTICS_TARGET;
    8837           0 :             ereport(WARNING,
    8838             :                     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    8839             :                      errmsg("lowering statistics target to %d",
    8840             :                             newtarget)));
    8841             :         }
    8842             :     }
    8843             : 
    8844         164 :     attrelation = table_open(AttributeRelationId, RowExclusiveLock);
    8845             : 
    8846         164 :     if (colName)
    8847             :     {
    8848         100 :         tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
    8849             : 
    8850         100 :         if (!HeapTupleIsValid(tuple))
    8851          12 :             ereport(ERROR,
    8852             :                     (errcode(ERRCODE_UNDEFINED_COLUMN),
    8853             :                      errmsg("column \"%s\" of relation \"%s\" does not exist",
    8854             :                             colName, RelationGetRelationName(rel))));
    8855             :     }
    8856             :     else
    8857             :     {
    8858          64 :         tuple = SearchSysCacheAttNum(RelationGetRelid(rel), colNum);
    8859             : 
    8860          64 :         if (!HeapTupleIsValid(tuple))
    8861          12 :             ereport(ERROR,
    8862             :                     (errcode(ERRCODE_UNDEFINED_COLUMN),
    8863             :                      errmsg("column number %d of relation \"%s\" does not exist",
    8864             :                             colNum, RelationGetRelationName(rel))));
    8865             :     }
    8866             : 
    8867         140 :     attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
    8868             : 
    8869         140 :     attnum = attrtuple->attnum;
    8870         140 :     if (attnum <= 0)
    8871           0 :         ereport(ERROR,
    8872             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    8873             :                  errmsg("cannot alter system column \"%s\"",
    8874             :                         colName)));
    8875             : 
    8876         140 :     if (rel->rd_rel->relkind == RELKIND_INDEX ||
    8877          88 :         rel->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
    8878             :     {
    8879          52 :         if (attnum > rel->rd_index->indnkeyatts)
    8880           6 :             ereport(ERROR,
    8881             :                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    8882             :                      errmsg("cannot alter statistics on included column \"%s\" of index \"%s\"",
    8883             :                             NameStr(attrtuple->attname), RelationGetRelationName(rel))));
    8884          46 :         else if (rel->rd_index->indkey.values[attnum - 1] != 0)
    8885          18 :             ereport(ERROR,
    8886             :                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    8887             :                      errmsg("cannot alter statistics on non-expression column \"%s\" of index \"%s\"",
    8888             :                             NameStr(attrtuple->attname), RelationGetRelationName(rel)),
    8889             :                      errhint("Alter statistics on table column instead.")));
    8890             :     }
    8891             : 
    8892             :     /* Build new tuple. */
    8893         116 :     memset(repl_null, false, sizeof(repl_null));
    8894         116 :     memset(repl_repl, false, sizeof(repl_repl));
    8895         116 :     if (!newtarget_default)
    8896          72 :         repl_val[Anum_pg_attribute_attstattarget - 1] = newtarget;
    8897             :     else
    8898          44 :         repl_null[Anum_pg_attribute_attstattarget - 1] = true;
    8899         116 :     repl_repl[Anum_pg_attribute_attstattarget - 1] = true;
    8900         116 :     newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrelation),
    8901             :                                  repl_val, repl_null, repl_repl);
    8902         116 :     CatalogTupleUpdate(attrelation, &tuple->t_self, newtuple);
    8903             : 
    8904         116 :     InvokeObjectPostAlterHook(RelationRelationId,
    8905             :                               RelationGetRelid(rel),
    8906             :                               attrtuple->attnum);
    8907         116 :     ObjectAddressSubSet(address, RelationRelationId,
    8908             :                         RelationGetRelid(rel), attnum);
    8909             : 
    8910         116 :     heap_freetuple(newtuple);
    8911             : 
    8912         116 :     ReleaseSysCache(tuple);
    8913             : 
    8914         116 :     table_close(attrelation, RowExclusiveLock);
    8915             : 
    8916         116 :     return address;
    8917             : }
    8918             : 
    8919             : /*
    8920             :  * Return value is the address of the modified column
    8921             :  */
    8922             : static ObjectAddress
    8923          32 : ATExecSetOptions(Relation rel, const char *colName, Node *options,
    8924             :                  bool isReset, LOCKMODE lockmode)
    8925             : {
    8926             :     Relation    attrelation;
    8927             :     HeapTuple   tuple,
    8928             :                 newtuple;
    8929             :     Form_pg_attribute attrtuple;
    8930             :     AttrNumber  attnum;
    8931             :     Datum       datum,
    8932             :                 newOptions;
    8933             :     bool        isnull;
    8934             :     ObjectAddress address;
    8935             :     Datum       repl_val[Natts_pg_attribute];
    8936             :     bool        repl_null[Natts_pg_attribute];
    8937             :     bool        repl_repl[Natts_pg_attribute];
    8938             : 
    8939          32 :     attrelation = table_open(AttributeRelationId, RowExclusiveLock);
    8940             : 
    8941          32 :     tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
    8942             : 
    8943          32 :     if (!HeapTupleIsValid(tuple))
    8944           0 :         ereport(ERROR,
    8945             :                 (errcode(ERRCODE_UNDEFINED_COLUMN),
    8946             :                  errmsg("column \"%s\" of relation \"%s\" does not exist",
    8947             :                         colName, RelationGetRelationName(rel))));
    8948          32 :     attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
    8949             : 
    8950          32 :     attnum = attrtuple->attnum;
    8951          32 :     if (attnum <= 0)
    8952           0 :         ereport(ERROR,
    8953             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    8954             :                  errmsg("cannot alter system column \"%s\"",
    8955             :                         colName)));
    8956             : 
    8957             :     /* Generate new proposed attoptions (text array) */
    8958          32 :     datum = SysCacheGetAttr(ATTNAME, tuple, Anum_pg_attribute_attoptions,
    8959             :                             &isnull);
    8960          32 :     newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
    8961             :                                      castNode(List, options), NULL, NULL,
    8962             :                                      false, isReset);
    8963             :     /* Validate new options */
    8964          32 :     (void) attribute_reloptions(newOptions, true);
    8965             : 
    8966             :     /* Build new tuple. */
    8967          32 :     memset(repl_null, false, sizeof(repl_null));
    8968          32 :     memset(repl_repl, false, sizeof(repl_repl));
    8969          32 :     if (newOptions != (Datum) 0)
    8970          32 :         repl_val[Anum_pg_attribute_attoptions - 1] = newOptions;
    8971             :     else
    8972           0 :         repl_null[Anum_pg_attribute_attoptions - 1] = true;
    8973          32 :     repl_repl[Anum_pg_attribute_attoptions - 1] = true;
    8974          32 :     newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrelation),
    8975             :                                  repl_val, repl_null, repl_repl);
    8976             : 
    8977             :     /* Update system catalog. */
    8978          32 :     CatalogTupleUpdate(attrelation, &newtuple->t_self, newtuple);
    8979             : 
    8980          32 :     InvokeObjectPostAlterHook(RelationRelationId,
    8981             :                               RelationGetRelid(rel),
    8982             :                               attrtuple->attnum);
    8983          32 :     ObjectAddressSubSet(address, RelationRelationId,
    8984             :                         RelationGetRelid(rel), attnum);
    8985             : 
    8986          32 :     heap_freetuple(newtuple);
    8987             : 
    8988          32 :     ReleaseSysCache(tuple);
    8989             : 
    8990          32 :     table_close(attrelation, RowExclusiveLock);
    8991             : 
    8992          32 :     return address;
    8993             : }
    8994             : 
    8995             : /*
    8996             :  * Helper function for ATExecSetStorage and ATExecSetCompression
    8997             :  *
    8998             :  * Set the attstorage and/or attcompression fields for index columns
    8999             :  * associated with the specified table column.
    9000             :  */
    9001             : static void
    9002         282 : SetIndexStorageProperties(Relation rel, Relation attrelation,
    9003             :                           AttrNumber attnum,
    9004             :                           bool setstorage, char newstorage,
    9005             :                           bool setcompression, char newcompression,
    9006             :                           LOCKMODE lockmode)
    9007             : {
    9008             :     ListCell   *lc;
    9009             : 
    9010         354 :     foreach(lc, RelationGetIndexList(rel))
    9011             :     {
    9012          72 :         Oid         indexoid = lfirst_oid(lc);
    9013             :         Relation    indrel;
    9014          72 :         AttrNumber  indattnum = 0;
    9015             :         HeapTuple   tuple;
    9016             : 
    9017          72 :         indrel = index_open(indexoid, lockmode);
    9018             : 
    9019         120 :         for (int i = 0; i < indrel->rd_index->indnatts; i++)
    9020             :         {
    9021          78 :             if (indrel->rd_index->indkey.values[i] == attnum)
    9022             :             {
    9023          30 :                 indattnum = i + 1;
    9024          30 :                 break;
    9025             :             }
    9026             :         }
    9027             : 
    9028          72 :         if (indattnum == 0)
    9029             :         {
    9030          42 :             index_close(indrel, lockmode);
    9031          42 :             continue;
    9032             :         }
    9033             : 
    9034          30 :         tuple = SearchSysCacheCopyAttNum(RelationGetRelid(indrel), indattnum);
    9035             : 
    9036          30 :         if (HeapTupleIsValid(tuple))
    9037             :         {
    9038          30 :             Form_pg_attribute attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
    9039             : 
    9040          30 :             if (setstorage)
    9041          24 :                 attrtuple->attstorage = newstorage;
    9042             : 
    9043          30 :             if (setcompression)
    9044           6 :                 attrtuple->attcompression = newcompression;
    9045             : 
    9046          30 :             CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
    9047             : 
    9048          30 :             InvokeObjectPostAlterHook(RelationRelationId,
    9049             :                                       RelationGetRelid(rel),
    9050             :                                       attrtuple->attnum);
    9051             : 
    9052          30 :             heap_freetuple(tuple);
    9053             :         }
    9054             : 
    9055          30 :         index_close(indrel, lockmode);
    9056             :     }
    9057         282 : }
    9058             : 
    9059             : /*
    9060             :  * ALTER TABLE ALTER COLUMN SET STORAGE
    9061             :  *
    9062             :  * Return value is the address of the modified column
    9063             :  */
    9064             : static ObjectAddress
    9065         234 : ATExecSetStorage(Relation rel, const char *colName, Node *newValue, LOCKMODE lockmode)
    9066             : {
    9067             :     Relation    attrelation;
    9068             :     HeapTuple   tuple;
    9069             :     Form_pg_attribute attrtuple;
    9070             :     AttrNumber  attnum;
    9071             :     ObjectAddress address;
    9072             : 
    9073         234 :     attrelation = table_open(AttributeRelationId, RowExclusiveLock);
    9074             : 
    9075         234 :     tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
    9076             : 
    9077         234 :     if (!HeapTupleIsValid(tuple))
    9078          12 :         ereport(ERROR,
    9079             :                 (errcode(ERRCODE_UNDEFINED_COLUMN),
    9080             :                  errmsg("column \"%s\" of relation \"%s\" does not exist",
    9081             :                         colName, RelationGetRelationName(rel))));
    9082         222 :     attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
    9083             : 
    9084         222 :     attnum = attrtuple->attnum;
    9085         222 :     if (attnum <= 0)
    9086           0 :         ereport(ERROR,
    9087             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    9088             :                  errmsg("cannot alter system column \"%s\"",
    9089             :                         colName)));
    9090             : 
    9091         222 :     attrtuple->attstorage = GetAttributeStorage(attrtuple->atttypid, strVal(newValue));
    9092             : 
    9093         222 :     CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
    9094             : 
    9095         222 :     InvokeObjectPostAlterHook(RelationRelationId,
    9096             :                               RelationGetRelid(rel),
    9097             :                               attrtuple->attnum);
    9098             : 
    9099             :     /*
    9100             :      * Apply the change to indexes as well (only for simple index columns,
    9101             :      * matching behavior of index.c ConstructTupleDescriptor()).
    9102             :      */
    9103         222 :     SetIndexStorageProperties(rel, attrelation, attnum,
    9104         222 :                               true, attrtuple->attstorage,
    9105             :                               false, 0,
    9106             :                               lockmode);
    9107             : 
    9108         222 :     heap_freetuple(tuple);
    9109             : 
    9110         222 :     table_close(attrelation, RowExclusiveLock);
    9111             : 
    9112         222 :     ObjectAddressSubSet(address, RelationRelationId,
    9113             :                         RelationGetRelid(rel), attnum);
    9114         222 :     return address;
    9115             : }
    9116             : 
    9117             : 
    9118             : /*
    9119             :  * ALTER TABLE DROP COLUMN
    9120             :  *
    9121             :  * DROP COLUMN cannot use the normal ALTER TABLE recursion mechanism,
    9122             :  * because we have to decide at runtime whether to recurse or not depending
    9123             :  * on whether attinhcount goes to zero or not.  (We can't check this in a
    9124             :  * static pre-pass because it won't handle multiple inheritance situations
    9125             :  * correctly.)
    9126             :  */
    9127             : static void
    9128        1618 : ATPrepDropColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
    9129             :                  AlterTableCmd *cmd, LOCKMODE lockmode,
    9130             :                  AlterTableUtilityContext *context)
    9131             : {
    9132        1618 :     if (rel->rd_rel->reloftype && !recursing)
    9133           6 :         ereport(ERROR,
    9134             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    9135             :                  errmsg("cannot drop column from typed table")));
    9136             : 
    9137        1612 :     if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
    9138          82 :         ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
    9139             : 
    9140        1606 :     if (recurse)
    9141        1336 :         cmd->recurse = true;
    9142        1606 : }
    9143             : 
    9144             : /*
    9145             :  * Drops column 'colName' from relation 'rel' and returns the address of the
    9146             :  * dropped column.  The column is also dropped (or marked as no longer
    9147             :  * inherited from relation) from the relation's inheritance children, if any.
    9148             :  *
    9149             :  * In the recursive invocations for inheritance child relations, instead of
    9150             :  * dropping the column directly (if to be dropped at all), its object address
    9151             :  * is added to 'addrs', which must be non-NULL in such invocations.  All
    9152             :  * columns are dropped at the same time after all the children have been
    9153             :  * checked recursively.
    9154             :  */
    9155             : static ObjectAddress
    9156        2162 : ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
    9157             :                  DropBehavior behavior,
    9158             :                  bool recurse, bool recursing,
    9159             :                  bool missing_ok, LOCKMODE lockmode,
    9160             :                  ObjectAddresses *addrs)
    9161             : {
    9162             :     HeapTuple   tuple;
    9163             :     Form_pg_attribute targetatt;
    9164             :     AttrNumber  attnum;
    9165             :     List       *children;
    9166             :     ObjectAddress object;
    9167             :     bool        is_expr;
    9168             : 
    9169             :     /* At top level, permission check was done in ATPrepCmd, else do it */
    9170        2162 :     if (recursing)
    9171         556 :         ATSimplePermissions(AT_DropColumn, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
    9172             : 
    9173             :     /* Initialize addrs on the first invocation */
    9174             :     Assert(!recursing || addrs != NULL);
    9175             : 
    9176             :     /* since this function recurses, it could be driven to stack overflow */
    9177        2162 :     check_stack_depth();
    9178             : 
    9179        2162 :     if (!recursing)
    9180        1606 :         addrs = new_object_addresses();
    9181             : 
    9182             :     /*
    9183             :      * get the number of the attribute
    9184             :      */
    9185        2162 :     tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
    9186        2162 :     if (!HeapTupleIsValid(tuple))
    9187             :     {
    9188          54 :         if (!missing_ok)
    9189             :         {
    9190          36 :             ereport(ERROR,
    9191             :                     (errcode(ERRCODE_UNDEFINED_COLUMN),
    9192             :                      errmsg("column \"%s\" of relation \"%s\" does not exist",
    9193             :                             colName, RelationGetRelationName(rel))));
    9194             :         }
    9195             :         else
    9196             :         {
    9197          18 :             ereport(NOTICE,
    9198             :                     (errmsg("column \"%s\" of relation \"%s\" does not exist, skipping",
    9199             :                             colName, RelationGetRelationName(rel))));
    9200          18 :             return InvalidObjectAddress;
    9201             :         }
    9202             :     }
    9203        2108 :     targetatt = (Form_pg_attribute) GETSTRUCT(tuple);
    9204             : 
    9205        2108 :     attnum = targetatt->attnum;
    9206             : 
    9207             :     /* Can't drop a system attribute */
    9208        2108 :     if (attnum <= 0)
    9209           6 :         ereport(ERROR,
    9210             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    9211             :                  errmsg("cannot drop system column \"%s\"",
    9212             :                         colName)));
    9213             : 
    9214             :     /*
    9215             :      * Don't drop inherited columns, unless recursing (presumably from a drop
    9216             :      * of the parent column)
    9217             :      */
    9218        2102 :     if (targetatt->attinhcount > 0 && !recursing)
    9219          48 :         ereport(ERROR,
    9220             :                 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    9221             :                  errmsg("cannot drop inherited column \"%s\"",
    9222             :                         colName)));
    9223             : 
    9224             :     /*
    9225             :      * Don't drop columns used in the partition key, either.  (If we let this
    9226             :      * go through, the key column's dependencies would cause a cascaded drop
    9227             :      * of the whole table, which is surely not what the user expected.)
    9228             :      */
    9229        2054 :     if (has_partition_attrs(rel,
    9230             :                             bms_make_singleton(attnum - FirstLowInvalidHeapAttributeNumber),
    9231             :                             &is_expr))
    9232          30 :         ereport(ERROR,
    9233             :                 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    9234             :                  errmsg("cannot drop column \"%s\" because it is part of the partition key of relation \"%s\"",
    9235             :                         colName, RelationGetRelationName(rel))));
    9236             : 
    9237        2024 :     ReleaseSysCache(tuple);
    9238             : 
    9239             :     /*
    9240             :      * Propagate to children as appropriate.  Unlike most other ALTER
    9241             :      * routines, we have to do this one level of recursion at a time; we can't
    9242             :      * use find_all_inheritors to do it in one pass.
    9243             :      */
    9244             :     children =
    9245        2024 :         find_inheritance_children(RelationGetRelid(rel), lockmode);
    9246             : 
    9247        2024 :     if (children)
    9248             :     {
    9249             :         Relation    attr_rel;
    9250             :         ListCell   *child;
    9251             : 
    9252             :         /*
    9253             :          * In case of a partitioned table, the column must be dropped from the
    9254             :          * partitions as well.
    9255             :          */
    9256         302 :         if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && !recurse)
    9257           6 :             ereport(ERROR,
    9258             :                     (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    9259             :                      errmsg("cannot drop column from only the partitioned table when partitions exist"),
    9260             :                      errhint("Do not specify the ONLY keyword.")));
    9261             : 
    9262         296 :         attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
    9263         882 :         foreach(child, children)
    9264             :         {
    9265         592 :             Oid         childrelid = lfirst_oid(child);
    9266             :             Relation    childrel;
    9267             :             Form_pg_attribute childatt;
    9268             : 
    9269             :             /* find_inheritance_children already got lock */
    9270         592 :             childrel = table_open(childrelid, NoLock);
    9271         592 :             CheckTableNotInUse(childrel, "ALTER TABLE");
    9272             : 
    9273         592 :             tuple = SearchSysCacheCopyAttName(childrelid, colName);
    9274         592 :             if (!HeapTupleIsValid(tuple))   /* shouldn't happen */
    9275           0 :                 elog(ERROR, "cache lookup failed for attribute \"%s\" of relation %u",
    9276             :                      colName, childrelid);
    9277         592 :             childatt = (Form_pg_attribute) GETSTRUCT(tuple);
    9278             : 
    9279         592 :             if (childatt->attinhcount <= 0) /* shouldn't happen */
    9280           0 :                 elog(ERROR, "relation %u has non-inherited attribute \"%s\"",
    9281             :                      childrelid, colName);
    9282             : 
    9283         592 :             if (recurse)
    9284             :             {
    9285             :                 /*
    9286             :                  * If the child column has other definition sources, just
    9287             :                  * decrement its inheritance count; if not, recurse to delete
    9288             :                  * it.
    9289             :                  */
    9290         568 :                 if (childatt->attinhcount == 1 && !childatt->attislocal)
    9291             :                 {
    9292             :                     /* Time to delete this child column, too */
    9293         556 :                     ATExecDropColumn(wqueue, childrel, colName,
    9294             :                                      behavior, true, true,
    9295             :                                      false, lockmode, addrs);
    9296             :                 }
    9297             :                 else
    9298             :                 {
    9299             :                     /* Child column must survive my deletion */
    9300          12 :                     childatt->attinhcount--;
    9301             : 
    9302          12 :                     CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
    9303             : 
    9304             :                     /* Make update visible */
    9305          12 :                     CommandCounterIncrement();
    9306             :                 }
    9307             :             }
    9308             :             else
    9309             :             {
    9310             :                 /*
    9311             :                  * If we were told to drop ONLY in this table (no recursion),
    9312             :                  * we need to mark the inheritors' attributes as locally
    9313             :                  * defined rather than inherited.
    9314             :                  */
    9315          24 :                 childatt->attinhcount--;
    9316          24 :                 childatt->attislocal = true;
    9317             : 
    9318          24 :                 CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
    9319             : 
    9320             :                 /* Make update visible */
    9321          24 :                 CommandCounterIncrement();
    9322             :             }
    9323             : 
    9324         586 :             heap_freetuple(tuple);
    9325             : 
    9326         586 :             table_close(childrel, NoLock);
    9327             :         }
    9328         290 :         table_close(attr_rel, RowExclusiveLock);
    9329             :     }
    9330             : 
    9331             :     /* Add object to delete */
    9332        2012 :     object.classId = RelationRelationId;
    9333        2012 :     object.objectId = RelationGetRelid(rel);
    9334        2012 :     object.objectSubId = attnum;
    9335        2012 :     add_exact_object_address(&object, addrs);
    9336             : 
    9337        2012 :     if (!recursing)
    9338             :     {
    9339             :         /* Recursion has ended, drop everything that was collected */
    9340        1462 :         performMultipleDeletions(addrs, behavior, 0);
    9341        1414 :         free_object_addresses(addrs);
    9342             :     }
    9343             : 
    9344        1964 :     return object;
    9345             : }
    9346             : 
    9347             : /*
    9348             :  * Prepare to add a primary key on an inheritance parent, by adding NOT NULL
    9349             :  * constraint on its children.
    9350             :  */
    9351             : static void
    9352        9476 : ATPrepAddPrimaryKey(List **wqueue, Relation rel, AlterTableCmd *cmd,
    9353             :                     LOCKMODE lockmode, AlterTableUtilityContext *context)
    9354             : {
    9355             :     List       *children;
    9356        9476 :     List       *newconstrs = NIL;
    9357             :     IndexStmt  *indexstmt;
    9358             : 
    9359             :     /* No work if not creating a primary key */
    9360        9476 :     if (!IsA(cmd->def, IndexStmt))
    9361           0 :         return;
    9362        9476 :     indexstmt = castNode(IndexStmt, cmd->def);
    9363        9476 :     if (!indexstmt->primary)
    9364        4004 :         return;
    9365             : 
    9366             :     /* No work if no legacy inheritance children are present */
    9367        5472 :     if (rel->rd_rel->relkind != RELKIND_RELATION ||
    9368        5334 :         !rel->rd_rel->relhassubclass)
    9369        5432 :         return;
    9370             : 
    9371             :     /*
    9372             :      * Acquire locks all the way down the hierarchy.  The recursion to lower
    9373             :      * levels occurs at execution time as necessary, so we don't need to do it
    9374             :      * here, and we don't need the returned list either.
    9375             :      */
    9376          40 :     (void) find_all_inheritors(RelationGetRelid(rel), lockmode, NULL);
    9377             : 
    9378             :     /*
    9379             :      * Construct the list of constraints that we need to add to each child
    9380             :      * relation.
    9381             :      */
    9382         120 :     foreach_node(IndexElem, elem, indexstmt->indexParams)
    9383             :     {
    9384             :         Constraint *nnconstr;
    9385             : 
    9386             :         Assert(elem->expr == NULL);
    9387             : 
    9388          40 :         nnconstr = makeNode(Constraint);
    9389          40 :         nnconstr->contype = CONSTR_NOTNULL;
    9390          40 :         nnconstr->conname = NULL;    /* XXX use PK name? */
    9391          40 :         nnconstr->inhcount = 1;
    9392          40 :         nnconstr->deferrable = false;
    9393          40 :         nnconstr->initdeferred = false;
    9394          40 :         nnconstr->location = -1;
    9395          40 :         nnconstr->keys = list_make1(makeString(elem->name));
    9396          40 :         nnconstr->skip_validation = false;
    9397          40 :         nnconstr->initially_valid = true;
    9398             : 
    9399          40 :         newconstrs = lappend(newconstrs, nnconstr);
    9400             :     }
    9401             : 
    9402             :     /* Finally, add AT subcommands to add each constraint to each child. */
    9403          40 :     children = find_inheritance_children(RelationGetRelid(rel), NoLock);
    9404         160 :     foreach_oid(childrelid, children)
    9405             :     {
    9406          80 :         Relation    childrel = table_open(childrelid, NoLock);
    9407          80 :         AlterTableCmd *newcmd = makeNode(AlterTableCmd);
    9408             :         ListCell   *lc2;
    9409             : 
    9410          80 :         newcmd->subtype = AT_AddConstraint;
    9411          80 :         newcmd->recurse = true;
    9412             : 
    9413         160 :         foreach(lc2, newconstrs)
    9414             :         {
    9415             :             /* ATPrepCmd copies newcmd, so we can scribble on it here */
    9416          80 :             newcmd->def = lfirst(lc2);
    9417             : 
    9418          80 :             ATPrepCmd(wqueue, childrel, newcmd,
    9419             :                       true, false, lockmode, context);
    9420             :         }
    9421             : 
    9422          80 :         table_close(childrel, NoLock);
    9423             :     }
    9424             : }
    9425             : 
    9426             : /*
    9427             :  * ALTER TABLE ADD INDEX
    9428             :  *
    9429             :  * There is no such command in the grammar, but parse_utilcmd.c converts
    9430             :  * UNIQUE and PRIMARY KEY constraints into AT_AddIndex subcommands.  This lets
    9431             :  * us schedule creation of the index at the appropriate time during ALTER.
    9432             :  *
    9433             :  * Return value is the address of the new index.
    9434             :  */
    9435             : static ObjectAddress
    9436        1458 : ATExecAddIndex(AlteredTableInfo *tab, Relation rel,
    9437             :                IndexStmt *stmt, bool is_rebuild, LOCKMODE lockmode)
    9438             : {
    9439             :     bool        check_rights;
    9440             :     bool        skip_build;
    9441             :     bool        quiet;
    9442             :     ObjectAddress address;
    9443             : 
    9444             :     Assert(IsA(stmt, IndexStmt));
    9445             :     Assert(!stmt->concurrent);
    9446             : 
    9447             :     /* The IndexStmt has already been through transformIndexStmt */
    9448             :     Assert(stmt->transformed);
    9449             : 
    9450             :     /* suppress schema rights check when rebuilding existing index */
    9451        1458 :     check_rights = !is_rebuild;
    9452             :     /* skip index build if phase 3 will do it or we're reusing an old one */
    9453        1458 :     skip_build = tab->rewrite > 0 || RelFileNumberIsValid(stmt->oldNumber);
    9454             :     /* suppress notices when rebuilding existing index */
    9455        1458 :     quiet = is_rebuild;
    9456             : 
    9457        1458 :     address = DefineIndex(RelationGetRelid(rel),
    9458             :                           stmt,
    9459             :                           InvalidOid,   /* no predefined OID */
    9460             :                           InvalidOid,   /* no parent index */
    9461             :                           InvalidOid,   /* no parent constraint */
    9462             :                           -1,   /* total_parts unknown */
    9463             :                           true, /* is_alter_table */
    9464             :                           check_rights,
    9465             :                           false,    /* check_not_in_use - we did it already */
    9466             :                           skip_build,
    9467             :                           quiet);
    9468             : 
    9469             :     /*
    9470             :      * If TryReuseIndex() stashed a relfilenumber for us, we used it for the
    9471             :      * new index instead of building from scratch.  Restore associated fields.
    9472             :      * This may store InvalidSubTransactionId in both fields, in which case
    9473             :      * relcache.c will assume it can rebuild the relcache entry.  Hence, do
    9474             :      * this after the CCI that made catalog rows visible to any rebuild.  The
    9475             :      * DROP of the old edition of this index will have scheduled the storage
    9476             :      * for deletion at commit, so cancel that pending deletion.
    9477             :      */
    9478        1330 :     if (RelFileNumberIsValid(stmt->oldNumber))
    9479             :     {
    9480          72 :         Relation    irel = index_open(address.objectId, NoLock);
    9481             : 
    9482          72 :         irel->rd_createSubid = stmt->oldCreateSubid;
    9483          72 :         irel->rd_firstRelfilelocatorSubid = stmt->oldFirstRelfilelocatorSubid;
    9484          72 :         RelationPreserveStorage(irel->rd_locator, true);
    9485          72 :         index_close(irel, NoLock);
    9486             :     }
    9487             : 
    9488        1330 :     return address;
    9489             : }
    9490             : 
    9491             : /*
    9492             :  * ALTER TABLE ADD STATISTICS
    9493             :  *
    9494             :  * This is no such command in the grammar, but we use this internally to add
    9495             :  * AT_ReAddStatistics subcommands to rebuild extended statistics after a table
    9496             :  * column type change.
    9497             :  */
    9498             : static ObjectAddress
    9499          14 : ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
    9500             :                     CreateStatsStmt *stmt, bool is_rebuild, LOCKMODE lockmode)
    9501             : {
    9502             :     ObjectAddress address;
    9503             : 
    9504             :     Assert(IsA(stmt, CreateStatsStmt));
    9505             : 
    9506             :     /* The CreateStatsStmt has already been through transformStatsStmt */
    9507             :     Assert(stmt->transformed);
    9508             : 
    9509          14 :     address = CreateStatistics(stmt);
    9510             : 
    9511          14 :     return address;
    9512             : }
    9513             : 
    9514             : /*
    9515             :  * ALTER TABLE ADD CONSTRAINT USING INDEX
    9516             :  *
    9517             :  * Returns the address of the new constraint.
    9518             :  */
    9519             : static ObjectAddress
    9520        8434 : ATExecAddIndexConstraint(AlteredTableInfo *tab, Relation rel,
    9521             :                          IndexStmt *stmt, LOCKMODE lockmode)
    9522             : {
    9523        8434 :     Oid         index_oid = stmt->indexOid;
    9524             :     Relation    indexRel;
    9525             :     char       *indexName;
    9526             :     IndexInfo  *indexInfo;
    9527             :     char       *constraintName;
    9528             :     char        constraintType;
    9529             :     ObjectAddress address;
    9530             :     bits16      flags;
    9531             : 
    9532             :     Assert(IsA(stmt, IndexStmt));
    9533             :     Assert(OidIsValid(index_oid));
    9534             :     Assert(stmt->isconstraint);
    9535             : 
    9536             :     /*
    9537             :      * Doing this on partitioned tables is not a simple feature to implement,
    9538             :      * so let's punt for now.
    9539             :      */
    9540        8434 :     if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
    9541           6 :         ereport(ERROR,
    9542             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    9543             :                  errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX is not supported on partitioned tables")));
    9544             : 
    9545        8428 :     indexRel = index_open(index_oid, AccessShareLock);
    9546             : 
    9547        8428 :     indexName = pstrdup(RelationGetRelationName(indexRel));
    9548             : 
    9549        8428 :     indexInfo = BuildIndexInfo(indexRel);
    9550             : 
    9551             :     /* this should have been checked at parse time */
    9552        8428 :     if (!indexInfo->ii_Unique)
    9553           0 :         elog(ERROR, "index \"%s\" is not unique", indexName);
    9554             : 
    9555             :     /*
    9556             :      * Determine name to assign to constraint.  We require a constraint to
    9557             :      * have the same name as the underlying index; therefore, use the index's
    9558             :      * existing name as the default constraint name, and if the user
    9559             :      * explicitly gives some other name for the constraint, rename the index
    9560             :      * to match.
    9561             :      */
    9562        8428 :     constraintName = stmt->idxname;
    9563        8428 :     if (constraintName == NULL)
    9564        8408 :         constraintName = indexName;
    9565          20 :     else if (strcmp(constraintName, indexName) != 0)
    9566             :     {
    9567          14 :         ereport(NOTICE,
    9568             :                 (errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX will rename index \"%s\" to \"%s\"",
    9569             :                         indexName, constraintName)));
    9570          14 :         RenameRelationInternal(index_oid, constraintName, false, true);
    9571             :     }
    9572             : 
    9573             :     /* Extra checks needed if making primary key */
    9574        8428 :     if (stmt->primary)
    9575        4766 :         index_check_primary_key(rel, indexInfo, true, stmt);
    9576             : 
    9577             :     /* Note we currently don't support EXCLUSION constraints here */
    9578        8422 :     if (stmt->primary)
    9579        4760 :         constraintType = CONSTRAINT_PRIMARY;
    9580             :     else
    9581        3662 :         constraintType = CONSTRAINT_UNIQUE;
    9582             : 
    9583             :     /* Create the catalog entries for the constraint */
    9584        8422 :     flags = INDEX_CONSTR_CREATE_UPDATE_INDEX |
    9585             :         INDEX_CONSTR_CREATE_REMOVE_OLD_DEPS |
    9586       16844 :         (stmt->initdeferred ? INDEX_CONSTR_CREATE_INIT_DEFERRED : 0) |
    9587        8422 :         (stmt->deferrable ? INDEX_CONSTR_CREATE_DEFERRABLE : 0) |
    9588        8422 :         (stmt->primary ? INDEX_CONSTR_CREATE_MARK_AS_PRIMARY : 0);
    9589             : 
    9590        8422 :     address = index_constraint_create(rel,
    9591             :                                       index_oid,
    9592             :                                       InvalidOid,
    9593             :                                       indexInfo,
    9594             :                                       constraintName,
    9595             :                                       constraintType,
    9596             :                                       flags,
    9597             :                                       allowSystemTableMods,
    9598             :                                       false);   /* is_internal */
    9599             : 
    9600        8422 :     index_close(indexRel, NoLock);
    9601             : 
    9602        8422 :     return address;
    9603             : }
    9604             : 
    9605             : /*
    9606             :  * ALTER TABLE ADD CONSTRAINT
    9607             :  *
    9608             :  * Return value is the address of the new constraint; if no constraint was
    9609             :  * added, InvalidObjectAddress is returned.
    9610             :  */
    9611             : static ObjectAddress
    9612        3858 : ATExecAddConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
    9613             :                     Constraint *newConstraint, bool recurse, bool is_readd,
    9614             :                     LOCKMODE lockmode)
    9615             : {
    9616        3858 :     ObjectAddress address = InvalidObjectAddress;
    9617             : 
    9618             :     Assert(IsA(newConstraint, Constraint));
    9619             : 
    9620             :     /*
    9621             :      * Currently, we only expect to see CONSTR_CHECK, CONSTR_NOTNULL and
    9622             :      * CONSTR_FOREIGN nodes arriving here (see the preprocessing done in
    9623             :      * parse_utilcmd.c).
    9624             :      */
    9625        3858 :     switch (newConstraint->contype)
    9626             :     {
    9627        1394 :         case CONSTR_CHECK:
    9628             :         case CONSTR_NOTNULL:
    9629             :             address =
    9630        1394 :                 ATAddCheckNNConstraint(wqueue, tab, rel,
    9631             :                                        newConstraint, recurse, false, is_readd,
    9632             :                                        lockmode);
    9633        1316 :             break;
    9634             : 
    9635        2464 :         case CONSTR_FOREIGN:
    9636             : 
    9637             :             /*
    9638             :              * Assign or validate constraint name
    9639             :              */
    9640        2464 :             if (newConstraint->conname)
    9641             :             {
    9642        1148 :                 if (ConstraintNameIsUsed(CONSTRAINT_RELATION,
    9643             :                                          RelationGetRelid(rel),
    9644        1148 :                                          newConstraint->conname))
    9645           0 :                     ereport(ERROR,
    9646             :                             (errcode(ERRCODE_DUPLICATE_OBJECT),
    9647             :                              errmsg("constraint \"%s\" for relation \"%s\" already exists",
    9648             :                                     newConstraint->conname,
    9649             :                                     RelationGetRelationName(rel))));
    9650             :             }
    9651             :             else
    9652        1316 :                 newConstraint->conname =
    9653        1316 :                     ChooseConstraintName(RelationGetRelationName(rel),
    9654        1316 :                                          ChooseForeignKeyConstraintNameAddition(newConstraint->fk_attrs),
    9655             :                                          "fkey",
    9656        1316 :                                          RelationGetNamespace(rel),
    9657             :                                          NIL);
    9658             : 
    9659        2464 :             address = ATAddForeignKeyConstraint(wqueue, tab, rel,
    9660             :                                                 newConstraint,
    9661             :                                                 recurse, false,
    9662             :                                                 lockmode);
    9663        1976 :             break;
    9664             : 
    9665           0 :         default:
    9666           0 :             elog(ERROR, "unrecognized constraint type: %d",
    9667             :                  (int) newConstraint->contype);
    9668             :     }
    9669             : 
    9670        3292 :     return address;
    9671             : }
    9672             : 
    9673             : /*
    9674             :  * Generate the column-name portion of the constraint name for a new foreign
    9675             :  * key given the list of column names that reference the referenced
    9676             :  * table.  This will be passed to ChooseConstraintName along with the parent
    9677             :  * table name and the "fkey" suffix.
    9678             :  *
    9679             :  * We know that less than NAMEDATALEN characters will actually be used, so we
    9680             :  * can truncate the result once we've generated that many.
    9681             :  *
    9682             :  * XXX see also ChooseExtendedStatisticNameAddition and
    9683             :  * ChooseIndexNameAddition.
    9684             :  */
    9685             : static char *
    9686        2042 : ChooseForeignKeyConstraintNameAddition(List *colnames)
    9687             : {
    9688             :     char        buf[NAMEDATALEN * 2];
    9689        2042 :     int         buflen = 0;
    9690             :     ListCell   *lc;
    9691             : 
    9692        2042 :     buf[0] = '\0';
    9693        4506 :     foreach(lc, colnames)
    9694             :     {
    9695        2464 :         const char *name = strVal(lfirst(lc));
    9696             : 
    9697        2464 :         if (buflen > 0)
    9698         422 :             buf[buflen++] = '_';    /* insert _ between names */
    9699             : 
    9700             :         /*
    9701             :          * At this point we have buflen <= NAMEDATALEN.  name should be less
    9702             :          * than NAMEDATALEN already, but use strlcpy for paranoia.
    9703             :          */
    9704        2464 :         strlcpy(buf + buflen, name, NAMEDATALEN);
    9705        2464 :         buflen += strlen(buf + buflen);
    9706        2464 :         if (buflen >= NAMEDATALEN)
    9707           0 :             break;
    9708             :     }
    9709        2042 :     return pstrdup(buf);
    9710             : }
    9711             : 
    9712             : /*
    9713             :  * Add a check or not-null constraint to a single table and its children.
    9714             :  * Returns the address of the constraint added to the parent relation,
    9715             :  * if one gets added, or InvalidObjectAddress otherwise.
    9716             :  *
    9717             :  * Subroutine for ATExecAddConstraint.
    9718             :  *
    9719             :  * We must recurse to child tables during execution, rather than using
    9720             :  * ALTER TABLE's normal prep-time recursion.  The reason is that all the
    9721             :  * constraints *must* be given the same name, else they won't be seen as
    9722             :  * related later.  If the user didn't explicitly specify a name, then
    9723             :  * AddRelationNewConstraints would normally assign different names to the
    9724             :  * child constraints.  To fix that, we must capture the name assigned at
    9725             :  * the parent table and pass that down.
    9726             :  */
    9727             : static ObjectAddress
    9728        2032 : ATAddCheckNNConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
    9729             :                        Constraint *constr, bool recurse, bool recursing,
    9730             :                        bool is_readd, LOCKMODE lockmode)
    9731             : {
    9732             :     List       *newcons;
    9733             :     ListCell   *lcon;
    9734             :     List       *children;
    9735             :     ListCell   *child;
    9736        2032 :     ObjectAddress address = InvalidObjectAddress;
    9737             : 
    9738             :     /* Guard against stack overflow due to overly deep inheritance tree. */
    9739        2032 :     check_stack_depth();
    9740             : 
    9741             :     /* At top level, permission check was done in ATPrepCmd, else do it */
    9742        2032 :     if (recursing)
    9743         504 :         ATSimplePermissions(AT_AddConstraint, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
    9744             : 
    9745             :     /*
    9746             :      * Call AddRelationNewConstraints to do the work, making sure it works on
    9747             :      * a copy of the Constraint so transformExpr can't modify the original. It
    9748             :      * returns a list of cooked constraints.
    9749             :      *
    9750             :      * If the constraint ends up getting merged with a pre-existing one, it's
    9751             :      * omitted from the returned list, which is what we want: we do not need
    9752             :      * to do any validation work.  That can only happen at child tables,
    9753             :      * though, since we disallow merging at the top level.
    9754             :      */
    9755        2032 :     newcons = AddRelationNewConstraints(rel, NIL,
    9756        2032 :                                         list_make1(copyObject(constr)),
    9757        2032 :                                         recursing || is_readd,  /* allow_merge */
    9758        2032 :                                         !recursing, /* is_local */
    9759             :                                         is_readd,   /* is_internal */
    9760        2032 :                                         NULL);  /* queryString not available
    9761             :                                                  * here */
    9762             : 
    9763             :     /* we don't expect more than one constraint here */
    9764             :     Assert(list_length(newcons) <= 1);
    9765             : 
    9766             :     /* Add each to-be-validated constraint to Phase 3's queue */
    9767        3782 :     foreach(lcon, newcons)
    9768             :     {
    9769        1822 :         CookedConstraint *ccon = (CookedConstraint *) lfirst(lcon);
    9770             : 
    9771        1822 :         if (!ccon->skip_validation && ccon->contype != CONSTR_NOTNULL)
    9772             :         {
    9773             :             NewConstraint *newcon;
    9774             : 
    9775         796 :             newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
    9776         796 :             newcon->name = ccon->name;
    9777         796 :             newcon->contype = ccon->contype;
    9778         796 :             newcon->qual = ccon->expr;
    9779             : 
    9780         796 :             tab->constraints = lappend(tab->constraints, newcon);
    9781             :         }
    9782             : 
    9783             :         /* Save the actually assigned name if it was defaulted */
    9784        1822 :         if (constr->conname == NULL)
    9785         554 :             constr->conname = ccon->name;
    9786             : 
    9787             :         /*
    9788             :          * If adding a not-null constraint, set the pg_attribute flag and tell
    9789             :          * phase 3 to verify existing rows, if needed.
    9790             :          */
    9791        1822 :         if (constr->contype == CONSTR_NOTNULL)
    9792         632 :             set_attnotnull(wqueue, rel, ccon->attnum,
    9793         632 :                            !ccon->is_no_inherit, lockmode);
    9794             : 
    9795        1822 :         ObjectAddressSet(address, ConstraintRelationId, ccon->conoid);
    9796             :     }
    9797             : 
    9798             :     /* At this point we must have a locked-down name to use */
    9799             :     Assert(newcons == NIL || constr->conname != NULL);
    9800             : 
    9801             :     /* Advance command counter in case same table is visited multiple times */
    9802        1960 :     CommandCounterIncrement();
    9803             : 
    9804             :     /*
    9805             :      * If the constraint got merged with an existing constraint, we're done.
    9806             :      * We mustn't recurse to child tables in this case, because they've
    9807             :      * already got the constraint, and visiting them again would lead to an
    9808             :      * incorrect value for coninhcount.
    9809             :      */
    9810        1960 :     if (newcons == NIL)
    9811         138 :         return address;
    9812             : 
    9813             :     /*
    9814             :      * If adding a NO INHERIT constraint, no need to find our children.
    9815             :      */
    9816        1822 :     if (constr->is_no_inherit)
    9817          72 :         return address;
    9818             : 
    9819             :     /*
    9820             :      * Propagate to children as appropriate.  Unlike most other ALTER
    9821             :      * routines, we have to do this one level of recursion at a time; we can't
    9822             :      * use find_all_inheritors to do it in one pass.
    9823             :      */
    9824             :     children =
    9825        1750 :         find_inheritance_children(RelationGetRelid(rel), lockmode);
    9826             : 
    9827             :     /*
    9828             :      * Check if ONLY was specified with ALTER TABLE.  If so, allow the
    9829             :      * constraint creation only if there are no children currently.  Error out
    9830             :      * otherwise.
    9831             :      */
    9832        1750 :     if (!recurse && children != NIL)
    9833           6 :         ereport(ERROR,
    9834             :                 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    9835             :                  errmsg("constraint must be added to child tables too")));
    9836             : 
    9837             :     /*
    9838             :      * The constraint must appear as inherited in children, so create a
    9839             :      * modified constraint object to use.
    9840             :      */
    9841        1744 :     constr = copyObject(constr);
    9842        1744 :     constr->inhcount = 1;
    9843        2242 :     foreach(child, children)
    9844             :     {
    9845         504 :         Oid         childrelid = lfirst_oid(child);
    9846             :         Relation    childrel;
    9847             :         AlteredTableInfo *childtab;
    9848             : 
    9849             :         /* find_inheritance_children already got lock */
    9850         504 :         childrel = table_open(childrelid, NoLock);
    9851         504 :         CheckTableNotInUse(childrel, "ALTER TABLE");
    9852             : 
    9853             :         /* Find or create work queue entry for this table */
    9854         504 :         childtab = ATGetQueueEntry(wqueue, childrel);
    9855             : 
    9856             :         /*
    9857             :          * Recurse to child.  XXX if we didn't create a constraint on the
    9858             :          * parent because it already existed, and we do create one on a child,
    9859             :          * should we return that child's constraint ObjectAddress here?
    9860             :          */
    9861         504 :         ATAddCheckNNConstraint(wqueue, childtab, childrel,
    9862             :                                constr, recurse, true, is_readd, lockmode);
    9863             : 
    9864         498 :         table_close(childrel, NoLock);
    9865             :     }
    9866             : 
    9867        1738 :     return address;
    9868             : }
    9869             : 
    9870             : /*
    9871             :  * Add a foreign-key constraint to a single table; return the new constraint's
    9872             :  * address.
    9873             :  *
    9874             :  * Subroutine for ATExecAddConstraint.  Must already hold exclusive
    9875             :  * lock on the rel, and have done appropriate validity checks for it.
    9876             :  * We do permissions checks here, however.
    9877             :  *
    9878             :  * When the referenced or referencing tables (or both) are partitioned,
    9879             :  * multiple pg_constraint rows are required -- one for each partitioned table
    9880             :  * and each partition on each side (fortunately, not one for every combination
    9881             :  * thereof).  We also need action triggers on each leaf partition on the
    9882             :  * referenced side, and check triggers on each leaf partition on the
    9883             :  * referencing side.
    9884             :  */
    9885             : static ObjectAddress
    9886        2464 : ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
    9887             :                           Constraint *fkconstraint,
    9888             :                           bool recurse, bool recursing, LOCKMODE lockmode)
    9889             : {
    9890             :     Relation    pkrel;
    9891        2464 :     int16       pkattnum[INDEX_MAX_KEYS] = {0};
    9892        2464 :     int16       fkattnum[INDEX_MAX_KEYS] = {0};
    9893        2464 :     Oid         pktypoid[INDEX_MAX_KEYS] = {0};
    9894        2464 :     Oid         fktypoid[INDEX_MAX_KEYS] = {0};
    9895        2464 :     Oid         opclasses[INDEX_MAX_KEYS] = {0};
    9896        2464 :     Oid         pfeqoperators[INDEX_MAX_KEYS] = {0};
    9897        2464 :     Oid         ppeqoperators[INDEX_MAX_KEYS] = {0};
    9898        2464 :     Oid         ffeqoperators[INDEX_MAX_KEYS] = {0};
    9899        2464 :     int16       fkdelsetcols[INDEX_MAX_KEYS] = {0};
    9900             :     bool        with_period;
    9901             :     bool        pk_has_without_overlaps;
    9902             :     int         i;
    9903             :     int         numfks,
    9904             :                 numpks,
    9905             :                 numfkdelsetcols;
    9906             :     Oid         indexOid;
    9907             :     bool        old_check_ok;
    9908             :     ObjectAddress address;
    9909        2464 :     ListCell   *old_pfeqop_item = list_head(fkconstraint->old_conpfeqop);
    9910             : 
    9911             :     /*
    9912             :      * Grab ShareRowExclusiveLock on the pk table, so that someone doesn't
    9913             :      * delete rows out from under us.
    9914             :      */
    9915        2464 :     if (OidIsValid(fkconstraint->old_pktable_oid))
    9916          72 :         pkrel = table_open(fkconstraint->old_pktable_oid, ShareRowExclusiveLock);
    9917             :     else
    9918        2392 :         pkrel = table_openrv(fkconstraint->pktable, ShareRowExclusiveLock);
    9919             : 
    9920             :     /*
    9921             :      * Validity checks (permission checks wait till we have the column
    9922             :      * numbers)
    9923             :      */
    9924        2464 :     if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
    9925             :     {
    9926         330 :         if (!recurse)
    9927           6 :             ereport(ERROR,
    9928             :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    9929             :                      errmsg("cannot use ONLY for foreign key on partitioned table \"%s\" referencing relation \"%s\"",
    9930             :                             RelationGetRelationName(rel),
    9931             :                             RelationGetRelationName(pkrel))));
    9932         324 :         if (fkconstraint->skip_validation && !fkconstraint->initially_valid)
    9933           6 :             ereport(ERROR,
    9934             :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    9935             :                      errmsg("cannot add NOT VALID foreign key on partitioned table \"%s\" referencing relation \"%s\"",
    9936             :                             RelationGetRelationName(rel),
    9937             :                             RelationGetRelationName(pkrel)),
    9938             :                      errdetail("This feature is not yet supported on partitioned tables.")));
    9939             :     }
    9940             : 
    9941        2452 :     if (pkrel->rd_rel->relkind != RELKIND_RELATION &&
    9942         296 :         pkrel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
    9943           0 :         ereport(ERROR,
    9944             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    9945             :                  errmsg("referenced relation \"%s\" is not a table",
    9946             :                         RelationGetRelationName(pkrel))));
    9947             : 
    9948        2452 :     if (!allowSystemTableMods && IsSystemRelation(pkrel))
    9949           2 :         ereport(ERROR,
    9950             :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
    9951             :                  errmsg("permission denied: \"%s\" is a system catalog",
    9952             :                         RelationGetRelationName(pkrel))));
    9953             : 
    9954             :     /*
    9955             :      * References from permanent or unlogged tables to temp tables, and from
    9956             :      * permanent tables to unlogged tables, are disallowed because the
    9957             :      * referenced data can vanish out from under us.  References from temp
    9958             :      * tables to any other table type are also disallowed, because other
    9959             :      * backends might need to run the RI triggers on the perm table, but they
    9960             :      * can't reliably see tuples in the local buffers of other backends.
    9961             :      */
    9962        2450 :     switch (rel->rd_rel->relpersistence)
    9963             :     {
    9964        2140 :         case RELPERSISTENCE_PERMANENT:
    9965        2140 :             if (!RelationIsPermanent(pkrel))
    9966           0 :                 ereport(ERROR,
    9967             :                         (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    9968             :                          errmsg("constraints on permanent tables may reference only permanent tables")));
    9969        2140 :             break;
    9970          32 :         case RELPERSISTENCE_UNLOGGED:
    9971          32 :             if (!RelationIsPermanent(pkrel)
    9972          32 :                 && pkrel->rd_rel->relpersistence != RELPERSISTENCE_UNLOGGED)
    9973           0 :                 ereport(ERROR,
    9974             :                         (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    9975             :                          errmsg("constraints on unlogged tables may reference only permanent or unlogged tables")));
    9976          32 :             break;
    9977         278 :         case RELPERSISTENCE_TEMP:
    9978         278 :             if (pkrel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
    9979           0 :                 ereport(ERROR,
    9980             :                         (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    9981             :                          errmsg("constraints on temporary tables may reference only temporary tables")));
    9982         278 :             if (!pkrel->rd_islocaltemp || !rel->rd_islocaltemp)
    9983           0 :                 ereport(ERROR,
    9984             :                         (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    9985             :                          errmsg("constraints on temporary tables must involve temporary tables of this session")));
    9986         278 :             break;
    9987             :     }
    9988             : 
    9989             :     /*
    9990             :      * Look up the referencing attributes to make sure they exist, and record
    9991             :      * their attnums and type OIDs.
    9992             :      */
    9993        2450 :     numfks = transformColumnNameList(RelationGetRelid(rel),
    9994             :                                      fkconstraint->fk_attrs,
    9995             :                                      fkattnum, fktypoid);
    9996        2420 :     with_period = fkconstraint->fk_with_period || fkconstraint->pk_with_period;
    9997        2420 :     if (with_period && !fkconstraint->fk_with_period)
    9998          24 :         ereport(ERROR,
    9999             :                 errcode(ERRCODE_INVALID_FOREIGN_KEY),
   10000             :                 errmsg("foreign key uses PERIOD on the referenced table but not the referencing table"));
   10001             : 
   10002        2396 :     numfkdelsetcols = transformColumnNameList(RelationGetRelid(rel),
   10003             :                                               fkconstraint->fk_del_set_cols,
   10004             :                                               fkdelsetcols, NULL);
   10005        2390 :     validateFkOnDeleteSetColumns(numfks, fkattnum,
   10006             :                                  numfkdelsetcols, fkdelsetcols,
   10007             :                                  fkconstraint->fk_del_set_cols);
   10008             : 
   10009             :     /*
   10010             :      * If the attribute list for the referenced table was omitted, lookup the
   10011             :      * definition of the primary key and use it.  Otherwise, validate the
   10012             :      * supplied attribute list.  In either case, discover the index OID and
   10013             :      * index opclasses, and the attnums and type OIDs of the attributes.
   10014             :      */
   10015        2384 :     if (fkconstraint->pk_attrs == NIL)
   10016             :     {
   10017        1070 :         numpks = transformFkeyGetPrimaryKey(pkrel, &indexOid,
   10018             :                                             &fkconstraint->pk_attrs,
   10019             :                                             pkattnum, pktypoid,
   10020             :                                             opclasses, &pk_has_without_overlaps);
   10021             : 
   10022             :         /* If the primary key uses WITHOUT OVERLAPS, the fk must use PERIOD */
   10023        1070 :         if (pk_has_without_overlaps && !fkconstraint->fk_with_period)
   10024          24 :             ereport(ERROR,
   10025             :                     errcode(ERRCODE_INVALID_FOREIGN_KEY),
   10026             :                     errmsg("foreign key uses PERIOD on the referenced table but not the referencing table"));
   10027             :     }
   10028             :     else
   10029             :     {
   10030        1314 :         numpks = transformColumnNameList(RelationGetRelid(pkrel),
   10031             :                                          fkconstraint->pk_attrs,
   10032             :                                          pkattnum, pktypoid);
   10033             : 
   10034             :         /* Since we got pk_attrs, one should be a period. */
   10035        1284 :         if (with_period && !fkconstraint->pk_with_period)
   10036          24 :             ereport(ERROR,
   10037             :                     errcode(ERRCODE_INVALID_FOREIGN_KEY),
   10038             :                     errmsg("foreign key uses PERIOD on the referencing table but not the referenced table"));
   10039             : 
   10040             :         /* Look for an index matching the column list */
   10041        1260 :         indexOid = transformFkeyCheckAttrs(pkrel, numpks, pkattnum,
   10042             :                                            with_period, opclasses, &pk_has_without_overlaps);
   10043             :     }
   10044             : 
   10045             :     /*
   10046             :      * If the referenced primary key has WITHOUT OVERLAPS, the foreign key
   10047             :      * must use PERIOD.
   10048             :      */
   10049        2270 :     if (pk_has_without_overlaps && !with_period)
   10050          12 :         ereport(ERROR,
   10051             :                 errcode(ERRCODE_INVALID_FOREIGN_KEY),
   10052             :                 errmsg("foreign key must use PERIOD when referencing a primary using WITHOUT OVERLAPS"));
   10053             : 
   10054             :     /*
   10055             :      * Now we can check permissions.
   10056             :      */
   10057        2258 :     checkFkeyPermissions(pkrel, pkattnum, numpks);
   10058             : 
   10059             :     /*
   10060             :      * Check some things for generated columns.
   10061             :      */
   10062        5286 :     for (i = 0; i < numfks; i++)
   10063             :     {
   10064        3040 :         char        attgenerated = TupleDescAttr(RelationGetDescr(rel), fkattnum[i] - 1)->attgenerated;
   10065             : 
   10066        3040 :         if (attgenerated)
   10067             :         {
   10068             :             /*
   10069             :              * Check restrictions on UPDATE/DELETE actions, per SQL standard
   10070             :              */
   10071          30 :             if (fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETNULL ||
   10072          30 :                 fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETDEFAULT ||
   10073          30 :                 fkconstraint->fk_upd_action == FKCONSTR_ACTION_CASCADE)
   10074           6 :                 ereport(ERROR,
   10075             :                         (errcode(ERRCODE_SYNTAX_ERROR),
   10076             :                          errmsg("invalid %s action for foreign key constraint containing generated column",
   10077             :                                 "ON UPDATE")));
   10078          24 :             if (fkconstraint->fk_del_action == FKCONSTR_ACTION_SETNULL ||
   10079          18 :                 fkconstraint->fk_del_action == FKCONSTR_ACTION_SETDEFAULT)
   10080           6 :                 ereport(ERROR,
   10081             :                         (errcode(ERRCODE_SYNTAX_ERROR),
   10082             :                          errmsg("invalid %s action for foreign key constraint containing generated column",
   10083             :                                 "ON DELETE")));
   10084             :         }
   10085             :     }
   10086             : 
   10087             :     /*
   10088             :      * Some actions are currently unsupported for foreign keys using PERIOD.
   10089             :      */
   10090        2246 :     if (fkconstraint->fk_with_period)
   10091             :     {
   10092         238 :         if (fkconstraint->fk_upd_action == FKCONSTR_ACTION_CASCADE ||
   10093         226 :             fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETNULL ||
   10094         214 :             fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETDEFAULT)
   10095          36 :             ereport(ERROR,
   10096             :                     errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   10097             :                     errmsg("unsupported %s action for foreign key constraint using PERIOD",
   10098             :                            "ON UPDATE"));
   10099             : 
   10100         202 :         if (fkconstraint->fk_del_action == FKCONSTR_ACTION_CASCADE ||
   10101         202 :             fkconstraint->fk_del_action == FKCONSTR_ACTION_SETNULL ||
   10102         202 :             fkconstraint->fk_del_action == FKCONSTR_ACTION_SETDEFAULT)
   10103           0 :             ereport(ERROR,
   10104             :                     errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   10105             :                     errmsg("unsupported %s action for foreign key constraint using PERIOD",
   10106             :                            "ON DELETE"));
   10107             :     }
   10108             : 
   10109             :     /*
   10110             :      * Look up the equality operators to use in the constraint.
   10111             :      *
   10112             :      * Note that we have to be careful about the difference between the actual
   10113             :      * PK column type and the opclass' declared input type, which might be
   10114             :      * only binary-compatible with it.  The declared opcintype is the right
   10115             :      * thing to probe pg_amop with.
   10116             :      */
   10117        2210 :     if (numfks != numpks)
   10118           0 :         ereport(ERROR,
   10119             :                 (errcode(ERRCODE_INVALID_FOREIGN_KEY),
   10120             :                  errmsg("number of referencing and referenced columns for foreign key disagree")));
   10121             : 
   10122             :     /*
   10123             :      * On the strength of a previous constraint, we might avoid scanning
   10124             :      * tables to validate this one.  See below.
   10125             :      */
   10126        2210 :     old_check_ok = (fkconstraint->old_conpfeqop != NIL);
   10127             :     Assert(!old_check_ok || numfks == list_length(fkconstraint->old_conpfeqop));
   10128             : 
   10129        4794 :     for (i = 0; i < numpks; i++)
   10130             :     {
   10131        2812 :         Oid         pktype = pktypoid[i];
   10132        2812 :         Oid         fktype = fktypoid[i];
   10133             :         Oid         fktyped;
   10134             :         HeapTuple   cla_ht;
   10135             :         Form_pg_opclass cla_tup;
   10136             :         Oid         amid;
   10137             :         Oid         opfamily;
   10138             :         Oid         opcintype;
   10139             :         Oid         pfeqop;
   10140             :         Oid         ppeqop;
   10141             :         Oid         ffeqop;
   10142             :         int16       eqstrategy;
   10143             :         Oid         pfeqop_right;
   10144             : 
   10145             :         /* We need several fields out of the pg_opclass entry */
   10146        2812 :         cla_ht = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclasses[i]));
   10147        2812 :         if (!HeapTupleIsValid(cla_ht))
   10148           0 :             elog(ERROR, "cache lookup failed for opclass %u", opclasses[i]);
   10149        2812 :         cla_tup = (Form_pg_opclass) GETSTRUCT(cla_ht);
   10150        2812 :         amid = cla_tup->opcmethod;
   10151        2812 :         opfamily = cla_tup->opcfamily;
   10152        2812 :         opcintype = cla_tup->opcintype;
   10153        2812 :         ReleaseSysCache(cla_ht);
   10154             : 
   10155        2812 :         if (with_period)
   10156             :         {
   10157             :             StrategyNumber rtstrategy;
   10158         432 :             bool        for_overlaps = with_period && i == numpks - 1;
   10159             : 
   10160             :             /*
   10161             :              * GiST indexes are required to support temporal foreign keys
   10162             :              * because they combine equals and overlaps.
   10163             :              */
   10164         432 :             if (amid != GIST_AM_OID)
   10165           0 :                 elog(ERROR, "only GiST indexes are supported for temporal foreign keys");
   10166             : 
   10167         432 :             rtstrategy = for_overlaps ? RTOverlapStrategyNumber : RTEqualStrategyNumber;
   10168             : 
   10169             :             /*
   10170             :              * An opclass can use whatever strategy numbers it wants, so we
   10171             :              * ask the opclass what number it actually uses instead of our RT*
   10172             :              * constants.
   10173             :              */
   10174         432 :             eqstrategy = GistTranslateStratnum(opclasses[i], rtstrategy);
   10175         432 :             if (eqstrategy == InvalidStrategy)
   10176             :             {
   10177             :                 HeapTuple   tuple;
   10178             : 
   10179           0 :                 tuple = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclasses[i]));
   10180           0 :                 if (!HeapTupleIsValid(tuple))
   10181           0 :                     elog(ERROR, "cache lookup failed for operator class %u", opclasses[i]);
   10182             : 
   10183           0 :                 ereport(ERROR,
   10184             :                         errcode(ERRCODE_UNDEFINED_OBJECT),
   10185             :                         for_overlaps
   10186             :                         ? errmsg("could not identify an overlaps operator for foreign key")
   10187             :                         : errmsg("could not identify an equality operator for foreign key"),
   10188             :                         errdetail("Could not translate strategy number %d for operator class \"%s\" for access method \"%s\".",
   10189             :                                   rtstrategy, NameStr(((Form_pg_opclass) GETSTRUCT(tuple))->opcname), "gist"));
   10190             :             }
   10191             :         }
   10192             :         else
   10193             :         {
   10194             :             /*
   10195             :              * Check it's a btree; currently this can never fail since no
   10196             :              * other index AMs support unique indexes.  If we ever did have
   10197             :              * other types of unique indexes, we'd need a way to determine
   10198             :              * which operator strategy number is equality.  (We could use
   10199             :              * something like GistTranslateStratnum.)
   10200             :              */
   10201        2380 :             if (amid != BTREE_AM_OID)
   10202           0 :                 elog(ERROR, "only b-tree indexes are supported for foreign keys");
   10203        2380 :             eqstrategy = BTEqualStrategyNumber;
   10204             :         }
   10205             : 
   10206             :         /*
   10207             :          * There had better be a primary equality operator for the index.
   10208             :          * We'll use it for PK = PK comparisons.
   10209             :          */
   10210        2812 :         ppeqop = get_opfamily_member(opfamily, opcintype, opcintype,
   10211             :                                      eqstrategy);
   10212             : 
   10213        2812 :         if (!OidIsValid(ppeqop))
   10214           0 :             elog(ERROR, "missing operator %d(%u,%u) in opfamily %u",
   10215             :                  eqstrategy, opcintype, opcintype, opfamily);
   10216             : 
   10217             :         /*
   10218             :          * Are there equality operators that take exactly the FK type? Assume
   10219             :          * we should look through any domain here.
   10220             :          */
   10221        2812 :         fktyped = getBaseType(fktype);
   10222             : 
   10223        2812 :         pfeqop = get_opfamily_member(opfamily, opcintype, fktyped,
   10224             :                                      eqstrategy);
   10225        2812 :         if (OidIsValid(pfeqop))
   10226             :         {
   10227        2130 :             pfeqop_right = fktyped;
   10228        2130 :             ffeqop = get_opfamily_member(opfamily, fktyped, fktyped,
   10229             :                                          eqstrategy);
   10230             :         }
   10231             :         else
   10232             :         {
   10233             :             /* keep compiler quiet */
   10234         682 :             pfeqop_right = InvalidOid;
   10235         682 :             ffeqop = InvalidOid;
   10236             :         }
   10237             : 
   10238        2812 :         if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
   10239             :         {
   10240             :             /*
   10241             :              * Otherwise, look for an implicit cast from the FK type to the
   10242             :              * opcintype, and if found, use the primary equality operator.
   10243             :              * This is a bit tricky because opcintype might be a polymorphic
   10244             :              * type such as ANYARRAY or ANYENUM; so what we have to test is
   10245             :              * whether the two actual column types can be concurrently cast to
   10246             :              * that type.  (Otherwise, we'd fail to reject combinations such
   10247             :              * as int[] and point[].)
   10248             :              */
   10249             :             Oid         input_typeids[2];
   10250             :             Oid         target_typeids[2];
   10251             : 
   10252         682 :             input_typeids[0] = pktype;
   10253         682 :             input_typeids[1] = fktype;
   10254         682 :             target_typeids[0] = opcintype;
   10255         682 :             target_typeids[1] = opcintype;
   10256         682 :             if (can_coerce_type(2, input_typeids, target_typeids,
   10257             :                                 COERCION_IMPLICIT))
   10258             :             {
   10259         454 :                 pfeqop = ffeqop = ppeqop;
   10260         454 :                 pfeqop_right = opcintype;
   10261             :             }
   10262             :         }
   10263             : 
   10264        2812 :         if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
   10265         228 :             ereport(ERROR,
   10266             :                     (errcode(ERRCODE_DATATYPE_MISMATCH),
   10267             :                      errmsg("foreign key constraint \"%s\" cannot be implemented",
   10268             :                             fkconstraint->conname),
   10269             :                      errdetail("Key columns \"%s\" and \"%s\" "
   10270             :                                "are of incompatible types: %s and %s.",
   10271             :                                strVal(list_nth(fkconstraint->fk_attrs, i)),
   10272             :                                strVal(list_nth(fkconstraint->pk_attrs, i)),
   10273             :                                format_type_be(fktype),
   10274             :                                format_type_be(pktype))));
   10275             : 
   10276        2584 :         if (old_check_ok)
   10277             :         {
   10278             :             /*
   10279             :              * When a pfeqop changes, revalidate the constraint.  We could
   10280             :              * permit intra-opfamily changes, but that adds subtle complexity
   10281             :              * without any concrete benefit for core types.  We need not
   10282             :              * assess ppeqop or ffeqop, which RI_Initial_Check() does not use.
   10283             :              */
   10284           6 :             old_check_ok = (pfeqop == lfirst_oid(old_pfeqop_item));
   10285           6 :             old_pfeqop_item = lnext(fkconstraint->old_conpfeqop,
   10286             :                                     old_pfeqop_item);
   10287             :         }
   10288        2584 :         if (old_check_ok)
   10289             :         {
   10290             :             Oid         old_fktype;
   10291             :             Oid         new_fktype;
   10292             :             CoercionPathType old_pathtype;
   10293             :             CoercionPathType new_pathtype;
   10294             :             Oid         old_castfunc;
   10295             :             Oid         new_castfunc;
   10296           6 :             Form_pg_attribute attr = TupleDescAttr(tab->oldDesc,
   10297             :                                                    fkattnum[i] - 1);
   10298             : 
   10299             :             /*
   10300             :              * Identify coercion pathways from each of the old and new FK-side
   10301             :              * column types to the right (foreign) operand type of the pfeqop.
   10302             :              * We may assume that pg_constraint.conkey is not changing.
   10303             :              */
   10304           6 :             old_fktype = attr->atttypid;
   10305           6 :             new_fktype = fktype;
   10306           6 :             old_pathtype = findFkeyCast(pfeqop_right, old_fktype,
   10307             :                                         &old_castfunc);
   10308           6 :             new_pathtype = findFkeyCast(pfeqop_right, new_fktype,
   10309             :                                         &new_castfunc);
   10310             : 
   10311             :             /*
   10312             :              * Upon a change to the cast from the FK column to its pfeqop
   10313             :              * operand, revalidate the constraint.  For this evaluation, a
   10314             :              * binary coercion cast is equivalent to no cast at all.  While
   10315             :              * type implementors should design implicit casts with an eye
   10316             :              * toward consistency of operations like equality, we cannot
   10317             :              * assume here that they have done so.
   10318             :              *
   10319             :              * A function with a polymorphic argument could change behavior
   10320             :              * arbitrarily in response to get_fn_expr_argtype().  Therefore,
   10321             :              * when the cast destination is polymorphic, we only avoid
   10322             :              * revalidation if the input type has not changed at all.  Given
   10323             :              * just the core data types and operator classes, this requirement
   10324             :              * prevents no would-be optimizations.
   10325             :              *
   10326             :              * If the cast converts from a base type to a domain thereon, then
   10327             :              * that domain type must be the opcintype of the unique index.
   10328             :              * Necessarily, the primary key column must then be of the domain
   10329             :              * type.  Since the constraint was previously valid, all values on
   10330             :              * the foreign side necessarily exist on the primary side and in
   10331             :              * turn conform to the domain.  Consequently, we need not treat
   10332             :              * domains specially here.
   10333             :              *
   10334             :              * Since we require that all collations share the same notion of
   10335             :              * equality (which they do, because texteq reduces to bitwise
   10336             :              * equality), we don't compare collation here.
   10337             :              *
   10338             :              * We need not directly consider the PK type.  It's necessarily
   10339             :              * binary coercible to the opcintype of the unique index column,
   10340             :              * and ri_triggers.c will only deal with PK datums in terms of
   10341             :              * that opcintype.  Changing the opcintype also changes pfeqop.
   10342             :              */
   10343           6 :             old_check_ok = (new_pathtype == old_pathtype &&
   10344          12 :                             new_castfunc == old_castfunc &&
   10345           6 :                             (!IsPolymorphicType(pfeqop_right) ||
   10346             :                              new_fktype == old_fktype));
   10347             :         }
   10348             : 
   10349        2584 :         pfeqoperators[i] = pfeqop;
   10350        2584 :         ppeqoperators[i] = ppeqop;
   10351        2584 :         ffeqoperators[i] = ffeqop;
   10352             :     }
   10353             : 
   10354             :     /*
   10355             :      * For FKs with PERIOD we need additional operators to check whether the
   10356             :      * referencing row's range is contained by the aggregated ranges of the
   10357             :      * referenced row(s). For rangetypes and multirangetypes this is
   10358             :      * fk.periodatt <@ range_agg(pk.periodatt). Those are the only types we
   10359             :      * support for now. FKs will look these up at "runtime", but we should
   10360             :      * make sure the lookup works here, even if we don't use the values.
   10361             :      */
   10362        1982 :     if (with_period)
   10363             :     {
   10364             :         Oid         periodoperoid;
   10365             :         Oid         aggedperiodoperoid;
   10366             : 
   10367         184 :         FindFKPeriodOpers(opclasses[numpks - 1], &periodoperoid, &aggedperiodoperoid);
   10368             :     }
   10369             : 
   10370             :     /*
   10371             :      * Create all the constraint and trigger objects, recursing to partitions
   10372             :      * as necessary.  First handle the referenced side.
   10373             :      */
   10374        1976 :     address = addFkRecurseReferenced(wqueue, fkconstraint, rel, pkrel,
   10375             :                                      indexOid,
   10376             :                                      InvalidOid,    /* no parent constraint */
   10377             :                                      numfks,
   10378             :                                      pkattnum,
   10379             :                                      fkattnum,
   10380             :                                      pfeqoperators,
   10381             :                                      ppeqoperators,
   10382             :                                      ffeqoperators,
   10383             :                                      numfkdelsetcols,
   10384             :                                      fkdelsetcols,
   10385             :                                      old_check_ok,
   10386             :                                      InvalidOid, InvalidOid,
   10387             :                                      with_period);
   10388             : 
   10389             :     /* Now handle the referencing side. */
   10390        1976 :     addFkRecurseReferencing(wqueue, fkconstraint, rel, pkrel,
   10391             :                             indexOid,
   10392             :                             address.objectId,
   10393             :                             numfks,
   10394             :                             pkattnum,
   10395             :                             fkattnum,
   10396             :                             pfeqoperators,
   10397             :                             ppeqoperators,
   10398             :                             ffeqoperators,
   10399             :                             numfkdelsetcols,
   10400             :                             fkdelsetcols,
   10401             :                             old_check_ok,
   10402             :                             lockmode,
   10403             :                             InvalidOid, InvalidOid,
   10404             :                             with_period);
   10405             : 
   10406             :     /*
   10407             :      * Done.  Close pk table, but keep lock until we've committed.
   10408             :      */
   10409        1976 :     table_close(pkrel, NoLock);
   10410             : 
   10411        1976 :     return address;
   10412             : }
   10413             : 
   10414             : /*
   10415             :  * validateFkOnDeleteSetColumns
   10416             :  *      Verifies that columns used in ON DELETE SET NULL/DEFAULT (...)
   10417             :  *      column lists are valid.
   10418             :  */
   10419             : void
   10420        2390 : validateFkOnDeleteSetColumns(int numfks, const int16 *fkattnums,
   10421             :                              int numfksetcols, const int16 *fksetcolsattnums,
   10422             :                              List *fksetcols)
   10423             : {
   10424        2414 :     for (int i = 0; i < numfksetcols; i++)
   10425             :     {
   10426          30 :         int16       setcol_attnum = fksetcolsattnums[i];
   10427          30 :         bool        seen = false;
   10428             : 
   10429          54 :         for (int j = 0; j < numfks; j++)
   10430             :         {
   10431          48 :             if (fkattnums[j] == setcol_attnum)
   10432             :             {
   10433          24 :                 seen = true;
   10434          24 :                 break;
   10435             :             }
   10436             :         }
   10437             : 
   10438          30 :         if (!seen)
   10439             :         {
   10440           6 :             char       *col = strVal(list_nth(fksetcols, i));
   10441             : 
   10442           6 :             ereport(ERROR,
   10443             :                     (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
   10444             :                      errmsg("column \"%s\" referenced in ON DELETE SET action must be part of foreign key", col)));
   10445             :         }
   10446             :     }
   10447        2384 : }
   10448             : 
   10449             : /*
   10450             :  * addFkRecurseReferenced
   10451             :  *      subroutine for ATAddForeignKeyConstraint; recurses on the referenced
   10452             :  *      side of the constraint
   10453             :  *
   10454             :  * Create pg_constraint rows for the referenced side of the constraint,
   10455             :  * referencing the parent of the referencing side; also create action triggers
   10456             :  * on leaf partitions.  If the table is partitioned, recurse to handle each
   10457             :  * partition.
   10458             :  *
   10459             :  * wqueue is the ALTER TABLE work queue; can be NULL when not running as part
   10460             :  * of an ALTER TABLE sequence.
   10461             :  * fkconstraint is the constraint being added.
   10462             :  * rel is the root referencing relation.
   10463             :  * pkrel is the referenced relation; might be a partition, if recursing.
   10464             :  * indexOid is the OID of the index (on pkrel) implementing this constraint.
   10465             :  * parentConstr is the OID of a parent constraint; InvalidOid if this is a
   10466             :  * top-level constraint.
   10467             :  * numfks is the number of columns in the foreign key
   10468             :  * pkattnum is the attnum array of referenced attributes.
   10469             :  * fkattnum is the attnum array of referencing attributes.
   10470             :  * numfkdelsetcols is the number of columns in the ON DELETE SET NULL/DEFAULT
   10471             :  *      (...) clause
   10472             :  * fkdelsetcols is the attnum array of the columns in the ON DELETE SET
   10473             :  *      NULL/DEFAULT clause
   10474             :  * pf/pp/ffeqoperators are OID array of operators between columns.
   10475             :  * old_check_ok signals that this constraint replaces an existing one that
   10476             :  * was already validated (thus this one doesn't need validation).
   10477             :  * parentDelTrigger and parentUpdTrigger, when being recursively called on
   10478             :  * a partition, are the OIDs of the parent action triggers for DELETE and
   10479             :  * UPDATE respectively.
   10480             :  */
   10481             : static ObjectAddress
   10482        2696 : addFkRecurseReferenced(List **wqueue, Constraint *fkconstraint, Relation rel,
   10483             :                        Relation pkrel, Oid indexOid, Oid parentConstr,
   10484             :                        int numfks,
   10485             :                        int16 *pkattnum, int16 *fkattnum, Oid *pfeqoperators,
   10486             :                        Oid *ppeqoperators, Oid *ffeqoperators,
   10487             :                        int numfkdelsetcols, int16 *fkdelsetcols,
   10488             :                        bool old_check_ok,
   10489             :                        Oid parentDelTrigger, Oid parentUpdTrigger,
   10490             :                        bool with_period)
   10491             : {
   10492             :     ObjectAddress address;
   10493             :     Oid         constrOid;
   10494             :     char       *conname;
   10495             :     bool        conislocal;
   10496             :     int         coninhcount;
   10497             :     bool        connoinherit;
   10498             :     Oid         deleteTriggerOid,
   10499             :                 updateTriggerOid;
   10500             : 
   10501             :     /*
   10502             :      * Verify relkind for each referenced partition.  At the top level, this
   10503             :      * is redundant with a previous check, but we need it when recursing.
   10504             :      */
   10505        2696 :     if (pkrel->rd_rel->relkind != RELKIND_RELATION &&
   10506         378 :         pkrel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
   10507           0 :         ereport(ERROR,
   10508             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   10509             :                  errmsg("referenced relation \"%s\" is not a table",
   10510             :                         RelationGetRelationName(pkrel))));
   10511             : 
   10512             :     /*
   10513             :      * Caller supplies us with a constraint name; however, it may be used in
   10514             :      * this partition, so come up with a different one in that case.
   10515             :      */
   10516        2696 :     if (ConstraintNameIsUsed(CONSTRAINT_RELATION,
   10517             :                              RelationGetRelid(rel),
   10518        2696 :                              fkconstraint->conname))
   10519         720 :         conname = ChooseConstraintName(RelationGetRelationName(rel),
   10520         720 :                                        ChooseForeignKeyConstraintNameAddition(fkconstraint->fk_attrs),
   10521             :                                        "fkey",
   10522         720 :                                        RelationGetNamespace(rel), NIL);
   10523             :     else
   10524        1976 :         conname = fkconstraint->conname;
   10525             : 
   10526        2696 :     if (OidIsValid(parentConstr))
   10527             :     {
   10528         720 :         conislocal = false;
   10529         720 :         coninhcount = 1;
   10530         720 :         connoinherit = false;
   10531             :     }
   10532             :     else
   10533             :     {
   10534        1976 :         conislocal = true;
   10535        1976 :         coninhcount = 0;
   10536             : 
   10537             :         /*
   10538             :          * always inherit for partitioned tables, never for legacy inheritance
   10539             :          */
   10540        1976 :         connoinherit = rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE;
   10541             :     }
   10542             : 
   10543             :     /*
   10544             :      * Record the FK constraint in pg_constraint.
   10545             :      */
   10546        2696 :     constrOid = CreateConstraintEntry(conname,
   10547        2696 :                                       RelationGetNamespace(rel),
   10548             :                                       CONSTRAINT_FOREIGN,
   10549        2696 :                                       fkconstraint->deferrable,
   10550        2696 :                                       fkconstraint->initdeferred,
   10551        2696 :                                       fkconstraint->initially_valid,
   10552             :                                       parentConstr,
   10553             :                                       RelationGetRelid(rel),
   10554             :                                       fkattnum,
   10555             :                                       numfks,
   10556             :                                       numfks,
   10557             :                                       InvalidOid,   /* not a domain constraint */
   10558             :                                       indexOid,
   10559             :                                       RelationGetRelid(pkrel),
   10560             :                                       pkattnum,
   10561             :                                       pfeqoperators,
   10562             :                                       ppeqoperators,
   10563             :                                       ffeqoperators,
   10564             :                                       numfks,
   10565        2696 :                                       fkconstraint->fk_upd_action,
   10566        2696 :                                       fkconstraint->fk_del_action,
   10567             :                                       fkdelsetcols,
   10568             :                                       numfkdelsetcols,
   10569        2696 :                                       fkconstraint->fk_matchtype,
   10570             :                                       NULL, /* no exclusion constraint */
   10571             :                                       NULL, /* no check constraint */
   10572             :                                       NULL,
   10573             :                                       conislocal,   /* islocal */
   10574             :                                       coninhcount,  /* inhcount */
   10575             :                                       connoinherit, /* conNoInherit */
   10576             :                                       with_period,  /* conPeriod */
   10577             :                                       false);   /* is_internal */
   10578             : 
   10579        2696 :     ObjectAddressSet(address, ConstraintRelationId, constrOid);
   10580             : 
   10581             :     /*
   10582             :      * Mark the child constraint as part of the parent constraint; it must not
   10583             :      * be dropped on its own.  (This constraint is deleted when the partition
   10584             :      * is detached, but a special check needs to occur that the partition
   10585             :      * contains no referenced values.)
   10586             :      */
   10587        2696 :     if (OidIsValid(parentConstr))
   10588             :     {
   10589             :         ObjectAddress referenced;
   10590             : 
   10591         720 :         ObjectAddressSet(referenced, ConstraintRelationId, parentConstr);
   10592         720 :         recordDependencyOn(&address, &referenced, DEPENDENCY_INTERNAL);
   10593             :     }
   10594             : 
   10595             :     /* make new constraint visible, in case we add more */
   10596        2696 :     CommandCounterIncrement();
   10597             : 
   10598             :     /*
   10599             :      * Create the action triggers that enforce the constraint.
   10600             :      */
   10601        2696 :     createForeignKeyActionTriggers(rel, RelationGetRelid(pkrel),
   10602             :                                    fkconstraint,
   10603             :                                    constrOid, indexOid,
   10604             :                                    parentDelTrigger, parentUpdTrigger,
   10605             :                                    &deleteTriggerOid, &updateTriggerOid);
   10606             : 
   10607             :     /*
   10608             :      * If the referenced table is partitioned, recurse on ourselves to handle
   10609             :      * each partition.  We need one pg_constraint row created for each
   10610             :      * partition in addition to the pg_constraint row for the parent table.
   10611             :      */
   10612        2696 :     if (pkrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
   10613             :     {
   10614         378 :         PartitionDesc pd = RelationGetPartitionDesc(pkrel, true);
   10615             : 
   10616         948 :         for (int i = 0; i < pd->nparts; i++)
   10617             :         {
   10618             :             Relation    partRel;
   10619             :             AttrMap    *map;
   10620             :             AttrNumber *mapped_pkattnum;
   10621             :             Oid         partIndexId;
   10622             : 
   10623         570 :             partRel = table_open(pd->oids[i], ShareRowExclusiveLock);
   10624             : 
   10625             :             /*
   10626             :              * Map the attribute numbers in the referenced side of the FK
   10627             :              * definition to match the partition's column layout.
   10628             :              */
   10629         570 :             map = build_attrmap_by_name_if_req(RelationGetDescr(partRel),
   10630             :                                                RelationGetDescr(pkrel),
   10631             :                                                false);
   10632         570 :             if (map)
   10633             :             {
   10634          46 :                 mapped_pkattnum = palloc(sizeof(AttrNumber) * numfks);
   10635          92 :                 for (int j = 0; j < numfks; j++)
   10636          46 :                     mapped_pkattnum[j] = map->attnums[pkattnum[j] - 1];
   10637             :             }
   10638             :             else
   10639         524 :                 mapped_pkattnum = pkattnum;
   10640             : 
   10641             :             /* do the deed */
   10642         570 :             partIndexId = index_get_partition(partRel, indexOid);
   10643         570 :             if (!OidIsValid(partIndexId))
   10644           0 :                 elog(ERROR, "index for %u not found in partition %s",
   10645             :                      indexOid, RelationGetRelationName(partRel));
   10646         570 :             addFkRecurseReferenced(wqueue, fkconstraint, rel, partRel,
   10647             :                                    partIndexId, constrOid, numfks,
   10648             :                                    mapped_pkattnum, fkattnum,
   10649             :                                    pfeqoperators, ppeqoperators, ffeqoperators,
   10650             :                                    numfkdelsetcols, fkdelsetcols,
   10651             :                                    old_check_ok,
   10652             :                                    deleteTriggerOid, updateTriggerOid,
   10653             :                                    with_period);
   10654             : 
   10655             :             /* Done -- clean up (but keep the lock) */
   10656         570 :             table_close(partRel, NoLock);
   10657         570 :             if (map)
   10658             :             {
   10659          46 :                 pfree(mapped_pkattnum);
   10660          46 :                 free_attrmap(map);
   10661             :             }
   10662             :         }
   10663             :     }
   10664             : 
   10665        2696 :     return address;
   10666             : }
   10667             : 
   10668             : /*
   10669             :  * addFkRecurseReferencing
   10670             :  *      subroutine for ATAddForeignKeyConstraint and CloneFkReferencing
   10671             :  *
   10672             :  * If the referencing relation is a plain relation, create the necessary check
   10673             :  * triggers that implement the constraint, and set up for Phase 3 constraint
   10674             :  * verification.  If the referencing relation is a partitioned table, then
   10675             :  * we create a pg_constraint row for it and recurse on this routine for each
   10676             :  * partition.
   10677             :  *
   10678             :  * We assume that the referenced relation is locked against concurrent
   10679             :  * deletions.  If it's a partitioned relation, every partition must be so
   10680             :  * locked.
   10681             :  *
   10682             :  * wqueue is the ALTER TABLE work queue; can be NULL when not running as part
   10683             :  * of an ALTER TABLE sequence.
   10684             :  * fkconstraint is the constraint being added.
   10685             :  * rel is the referencing relation; might be a partition, if recursing.
   10686             :  * pkrel is the root referenced relation.
   10687             :  * indexOid is the OID of the index (on pkrel) implementing this constraint.
   10688             :  * parentConstr is the OID of the parent constraint (there is always one).
   10689             :  * numfks is the number of columns in the foreign key
   10690             :  * pkattnum is the attnum array of referenced attributes.
   10691             :  * fkattnum is the attnum array of referencing attributes.
   10692             :  * pf/pp/ffeqoperators are OID array of operators between columns.
   10693             :  * numfkdelsetcols is the number of columns in the ON DELETE SET NULL/DEFAULT
   10694             :  *      (...) clause
   10695             :  * fkdelsetcols is the attnum array of the columns in the ON DELETE SET
   10696             :  *      NULL/DEFAULT clause
   10697             :  * old_check_ok signals that this constraint replaces an existing one that
   10698             :  *      was already validated (thus this one doesn't need validation).
   10699             :  * lockmode is the lockmode to acquire on partitions when recursing.
   10700             :  * parentInsTrigger and parentUpdTrigger, when being recursively called on
   10701             :  * a partition, are the OIDs of the parent check triggers for INSERT and
   10702             :  * UPDATE respectively.
   10703             :  */
   10704             : static void
   10705        2746 : addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint, Relation rel,
   10706             :                         Relation pkrel, Oid indexOid, Oid parentConstr,
   10707             :                         int numfks, int16 *pkattnum, int16 *fkattnum,
   10708             :                         Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators,
   10709             :                         int numfkdelsetcols, int16 *fkdelsetcols,
   10710             :                         bool old_check_ok, LOCKMODE lockmode,
   10711             :                         Oid parentInsTrigger, Oid parentUpdTrigger,
   10712             :                         bool with_period)
   10713             : {
   10714             :     Oid         insertTriggerOid,
   10715             :                 updateTriggerOid;
   10716             : 
   10717             :     Assert(OidIsValid(parentConstr));
   10718             : 
   10719        2746 :     if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
   10720           0 :         ereport(ERROR,
   10721             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   10722             :                  errmsg("foreign key constraints are not supported on foreign tables")));
   10723             : 
   10724             :     /*
   10725             :      * Add the check triggers to it and, if necessary, schedule it to be
   10726             :      * checked in Phase 3.
   10727             :      *
   10728             :      * If the relation is partitioned, drill down to do it to its partitions.
   10729             :      */
   10730        2746 :     createForeignKeyCheckTriggers(RelationGetRelid(rel),
   10731             :                                   RelationGetRelid(pkrel),
   10732             :                                   fkconstraint,
   10733             :                                   parentConstr,
   10734             :                                   indexOid,
   10735             :                                   parentInsTrigger, parentUpdTrigger,
   10736             :                                   &insertTriggerOid, &updateTriggerOid);
   10737             : 
   10738        2746 :     if (rel->rd_rel->relkind == RELKIND_RELATION)
   10739             :     {
   10740             :         /*
   10741             :          * Tell Phase 3 to check that the constraint is satisfied by existing
   10742             :          * rows. We can skip this during table creation, when requested
   10743             :          * explicitly by specifying NOT VALID in an ADD FOREIGN KEY command,
   10744             :          * and when we're recreating a constraint following a SET DATA TYPE
   10745             :          * operation that did not impugn its validity.
   10746             :          */
   10747        2334 :         if (wqueue && !old_check_ok && !fkconstraint->skip_validation)
   10748             :         {
   10749             :             NewConstraint *newcon;
   10750             :             AlteredTableInfo *tab;
   10751             : 
   10752         792 :             tab = ATGetQueueEntry(wqueue, rel);
   10753             : 
   10754         792 :             newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
   10755         792 :             newcon->name = get_constraint_name(parentConstr);
   10756         792 :             newcon->contype = CONSTR_FOREIGN;
   10757         792 :             newcon->refrelid = RelationGetRelid(pkrel);
   10758         792 :             newcon->refindid = indexOid;
   10759         792 :             newcon->conid = parentConstr;
   10760         792 :             newcon->conwithperiod = fkconstraint->fk_with_period;
   10761         792 :             newcon->qual = (Node *) fkconstraint;
   10762             : 
   10763         792 :             tab->constraints = lappend(tab->constraints, newcon);
   10764             :         }
   10765             :     }
   10766         412 :     else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
   10767             :     {
   10768         412 :         PartitionDesc pd = RelationGetPartitionDesc(rel, true);
   10769             :         Relation    trigrel;
   10770             : 
   10771             :         /*
   10772             :          * Triggers of the foreign keys will be manipulated a bunch of times
   10773             :          * in the loop below.  To avoid repeatedly opening/closing the trigger
   10774             :          * catalog relation, we open it here and pass it to the subroutines
   10775             :          * called below.
   10776             :          */
   10777         412 :         trigrel = table_open(TriggerRelationId, RowExclusiveLock);
   10778             : 
   10779             :         /*
   10780             :          * Recurse to take appropriate action on each partition; either we
   10781             :          * find an existing constraint to reparent to ours, or we create a new
   10782             :          * one.
   10783             :          */
   10784         788 :         for (int i = 0; i < pd->nparts; i++)
   10785             :         {
   10786         376 :             Oid         partitionId = pd->oids[i];
   10787         376 :             Relation    partition = table_open(partitionId, lockmode);
   10788             :             List       *partFKs;
   10789             :             AttrMap    *attmap;
   10790             :             AttrNumber  mapped_fkattnum[INDEX_MAX_KEYS];
   10791             :             bool        attached;
   10792             :             char       *conname;
   10793             :             Oid         constrOid;
   10794             :             ObjectAddress address,
   10795             :                         referenced;
   10796             :             ListCell   *cell;
   10797             : 
   10798         376 :             CheckTableNotInUse(partition, "ALTER TABLE");
   10799             : 
   10800         376 :             attmap = build_attrmap_by_name(RelationGetDescr(partition),
   10801             :                                            RelationGetDescr(rel),
   10802             :                                            false);
   10803         968 :             for (int j = 0; j < numfks; j++)
   10804         592 :                 mapped_fkattnum[j] = attmap->attnums[fkattnum[j] - 1];
   10805             : 
   10806             :             /* Check whether an existing constraint can be repurposed */
   10807         376 :             partFKs = copyObject(RelationGetFKeyList(partition));
   10808         376 :             attached = false;
   10809         394 :             foreach(cell, partFKs)
   10810             :             {
   10811             :                 ForeignKeyCacheInfo *fk;
   10812             : 
   10813          30 :                 fk = lfirst_node(ForeignKeyCacheInfo, cell);
   10814          30 :                 if (tryAttachPartitionForeignKey(fk,
   10815             :                                                  partitionId,
   10816             :                                                  parentConstr,
   10817             :                                                  numfks,
   10818             :                                                  mapped_fkattnum,
   10819             :                                                  pkattnum,
   10820             :                                                  pfeqoperators,
   10821             :                                                  insertTriggerOid,
   10822             :                                                  updateTriggerOid,
   10823             :                                                  trigrel))
   10824             :                 {
   10825          12 :                     attached = true;
   10826          12 :                     break;
   10827             :                 }
   10828             :             }
   10829         376 :             if (attached)
   10830             :             {
   10831          12 :                 table_close(partition, NoLock);
   10832          12 :                 continue;
   10833             :             }
   10834             : 
   10835             :             /*
   10836             :              * No luck finding a good constraint to reuse; create our own.
   10837             :              */
   10838         364 :             if (ConstraintNameIsUsed(CONSTRAINT_RELATION,
   10839             :                                      RelationGetRelid(partition),
   10840         364 :                                      fkconstraint->conname))
   10841           0 :                 conname = ChooseConstraintName(RelationGetRelationName(partition),
   10842           0 :                                                ChooseForeignKeyConstraintNameAddition(fkconstraint->fk_attrs),
   10843             :                                                "fkey",
   10844           0 :                                                RelationGetNamespace(partition), NIL);
   10845             :             else
   10846         364 :                 conname = fkconstraint->conname;
   10847             :             constrOid =
   10848         364 :                 CreateConstraintEntry(conname,
   10849         364 :                                       RelationGetNamespace(partition),
   10850             :                                       CONSTRAINT_FOREIGN,
   10851         364 :                                       fkconstraint->deferrable,
   10852         364 :                                       fkconstraint->initdeferred,
   10853         364 :                                       fkconstraint->initially_valid,
   10854             :                                       parentConstr,
   10855             :                                       partitionId,
   10856             :                                       mapped_fkattnum,
   10857             :                                       numfks,
   10858             :                                       numfks,
   10859             :                                       InvalidOid,
   10860             :                                       indexOid,
   10861             :                                       RelationGetRelid(pkrel),
   10862             :                                       pkattnum,
   10863             :                                       pfeqoperators,
   10864             :                                       ppeqoperators,
   10865             :                                       ffeqoperators,
   10866             :                                       numfks,
   10867         364 :                                       fkconstraint->fk_upd_action,
   10868         364 :                                       fkconstraint->fk_del_action,
   10869             :                                       fkdelsetcols,
   10870             :                                       numfkdelsetcols,
   10871         364 :                                       fkconstraint->fk_matchtype,
   10872             :                                       NULL,
   10873             :                                       NULL,
   10874             :                                       NULL,
   10875             :                                       false,
   10876             :                                       1,
   10877             :                                       false,
   10878             :                                       with_period,  /* conPeriod */
   10879             :                                       false);
   10880             : 
   10881             :             /*
   10882             :              * Give this constraint partition-type dependencies on the parent
   10883             :              * constraint as well as the table.
   10884             :              */
   10885         364 :             ObjectAddressSet(address, ConstraintRelationId, constrOid);
   10886         364 :             ObjectAddressSet(referenced, ConstraintRelationId, parentConstr);
   10887         364 :             recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_PRI);
   10888         364 :             ObjectAddressSet(referenced, RelationRelationId, partitionId);
   10889         364 :             recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_SEC);
   10890             : 
   10891             :             /* Make all this visible before recursing */
   10892         364 :             CommandCounterIncrement();
   10893             : 
   10894             :             /* call ourselves to finalize the creation and we're done */
   10895         364 :             addFkRecurseReferencing(wqueue, fkconstraint, partition, pkrel,
   10896             :                                     indexOid,
   10897             :                                     constrOid,
   10898             :                                     numfks,
   10899             :                                     pkattnum,
   10900             :                                     mapped_fkattnum,
   10901             :                                     pfeqoperators,
   10902             :                                     ppeqoperators,
   10903             :                                     ffeqoperators,
   10904             :                                     numfkdelsetcols,
   10905             :                                     fkdelsetcols,
   10906             :                                     old_check_ok,
   10907             :                                     lockmode,
   10908             :                                     insertTriggerOid,
   10909             :                                     updateTriggerOid,
   10910             :                                     with_period);
   10911             : 
   10912         364 :             table_close(partition, NoLock);
   10913             :         }
   10914             : 
   10915         412 :         table_close(trigrel, RowExclusiveLock);
   10916             :     }
   10917        2746 : }
   10918             : 
   10919             : /*
   10920             :  * CloneForeignKeyConstraints
   10921             :  *      Clone foreign keys from a partitioned table to a newly acquired
   10922             :  *      partition.
   10923             :  *
   10924             :  * partitionRel is a partition of parentRel, so we can be certain that it has
   10925             :  * the same columns with the same datatypes.  The columns may be in different
   10926             :  * order, though.
   10927             :  *
   10928             :  * wqueue must be passed to set up phase 3 constraint checking, unless the
   10929             :  * referencing-side partition is known to be empty (such as in CREATE TABLE /
   10930             :  * PARTITION OF).
   10931             :  */
   10932             : static void
   10933       10180 : CloneForeignKeyConstraints(List **wqueue, Relation parentRel,
   10934             :                            Relation partitionRel)
   10935             : {
   10936             :     /* This only works for declarative partitioning */
   10937             :     Assert(parentRel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
   10938             : 
   10939             :     /*
   10940             :      * Clone constraints for which the parent is on the referenced side.
   10941             :      */
   10942       10180 :     CloneFkReferenced(parentRel, partitionRel);
   10943             : 
   10944             :     /*
   10945             :      * Now clone constraints where the parent is on the referencing side.
   10946             :      */
   10947       10180 :     CloneFkReferencing(wqueue, parentRel, partitionRel);
   10948       10180 : }
   10949             : 
   10950             : /*
   10951             :  * CloneFkReferenced
   10952             :  *      Subroutine for CloneForeignKeyConstraints
   10953             :  *
   10954             :  * Find all the FKs that have the parent relation on the referenced side;
   10955             :  * clone those constraints to the given partition.  This is to be called
   10956             :  * when the partition is being created or attached.
   10957             :  *
   10958             :  * This ignores self-referencing FKs; those are handled by CloneFkReferencing.
   10959             :  *
   10960             :  * This recurses to partitions, if the relation being attached is partitioned.
   10961             :  * Recursion is done by calling addFkRecurseReferenced.
   10962             :  */
   10963             : static void
   10964       10180 : CloneFkReferenced(Relation parentRel, Relation partitionRel)
   10965             : {
   10966             :     Relation    pg_constraint;
   10967             :     AttrMap    *attmap;
   10968             :     ListCell   *cell;
   10969             :     SysScanDesc scan;
   10970             :     ScanKeyData key[2];
   10971             :     HeapTuple   tuple;
   10972       10180 :     List       *clone = NIL;
   10973             :     Relation    trigrel;
   10974             : 
   10975             :     /*
   10976             :      * Search for any constraints where this partition's parent is in the
   10977             :      * referenced side.  However, we must not clone any constraint whose
   10978             :      * parent constraint is also going to be cloned, to avoid duplicates.  So
   10979             :      * do it in two steps: first construct the list of constraints to clone,
   10980             :      * then go over that list cloning those whose parents are not in the list.
   10981             :      * (We must not rely on the parent being seen first, since the catalog
   10982             :      * scan could return children first.)
   10983             :      */
   10984       10180 :     pg_constraint = table_open(ConstraintRelationId, RowShareLock);
   10985       10180 :     ScanKeyInit(&key[0],
   10986             :                 Anum_pg_constraint_confrelid, BTEqualStrategyNumber,
   10987             :                 F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(parentRel)));
   10988       10180 :     ScanKeyInit(&key[1],
   10989             :                 Anum_pg_constraint_contype, BTEqualStrategyNumber,
   10990             :                 F_CHAREQ, CharGetDatum(CONSTRAINT_FOREIGN));
   10991             :     /* This is a seqscan, as we don't have a usable index ... */
   10992       10180 :     scan = systable_beginscan(pg_constraint, InvalidOid, true,
   10993             :                               NULL, 2, key);
   10994       10510 :     while ((tuple = systable_getnext(scan)) != NULL)
   10995             :     {
   10996         330 :         Form_pg_constraint constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
   10997             : 
   10998         330 :         clone = lappend_oid(clone, constrForm->oid);
   10999             :     }
   11000       10180 :     systable_endscan(scan);
   11001       10180 :     table_close(pg_constraint, RowShareLock);
   11002             : 
   11003             :     /*
   11004             :      * Triggers of the foreign keys will be manipulated a bunch of times in
   11005             :      * the loop below.  To avoid repeatedly opening/closing the trigger
   11006             :      * catalog relation, we open it here and pass it to the subroutines called
   11007             :      * below.
   11008             :      */
   11009       10180 :     trigrel = table_open(TriggerRelationId, RowExclusiveLock);
   11010             : 
   11011       10180 :     attmap = build_attrmap_by_name(RelationGetDescr(partitionRel),
   11012             :                                    RelationGetDescr(parentRel),
   11013             :                                    false);
   11014       10510 :     foreach(cell, clone)
   11015             :     {
   11016         330 :         Oid         constrOid = lfirst_oid(cell);
   11017             :         Form_pg_constraint constrForm;
   11018             :         Relation    fkRel;
   11019             :         Oid         indexOid;
   11020             :         Oid         partIndexId;
   11021             :         int         numfks;
   11022             :         AttrNumber  conkey[INDEX_MAX_KEYS];
   11023             :         AttrNumber  mapped_confkey[INDEX_MAX_KEYS];
   11024             :         AttrNumber  confkey[INDEX_MAX_KEYS];
   11025             :         Oid         conpfeqop[INDEX_MAX_KEYS];
   11026             :         Oid         conppeqop[INDEX_MAX_KEYS];
   11027             :         Oid         conffeqop[INDEX_MAX_KEYS];
   11028             :         int         numfkdelsetcols;
   11029             :         AttrNumber  confdelsetcols[INDEX_MAX_KEYS];
   11030             :         Constraint *fkconstraint;
   11031             :         Oid         deleteTriggerOid,
   11032             :                     updateTriggerOid;
   11033             : 
   11034         330 :         tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constrOid));
   11035         330 :         if (!HeapTupleIsValid(tuple))
   11036           0 :             elog(ERROR, "cache lookup failed for constraint %u", constrOid);
   11037         330 :         constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
   11038             : 
   11039             :         /*
   11040             :          * As explained above: don't try to clone a constraint for which we're
   11041             :          * going to clone the parent.
   11042             :          */
   11043         330 :         if (list_member_oid(clone, constrForm->conparentid))
   11044             :         {
   11045         126 :             ReleaseSysCache(tuple);
   11046         180 :             continue;
   11047             :         }
   11048             : 
   11049             :         /*
   11050             :          * Don't clone self-referencing foreign keys, which can be in the
   11051             :          * partitioned table or in the partition-to-be.
   11052             :          */
   11053         204 :         if (constrForm->conrelid == RelationGetRelid(parentRel) ||
   11054         162 :             constrForm->conrelid == RelationGetRelid(partitionRel))
   11055             :         {
   11056          54 :             ReleaseSysCache(tuple);
   11057          54 :             continue;
   11058             :         }
   11059             : 
   11060             :         /*
   11061             :          * Because we're only expanding the key space at the referenced side,
   11062             :          * we don't need to prevent any operation in the referencing table, so
   11063             :          * AccessShareLock suffices (assumes that dropping the constraint
   11064             :          * acquires AEL).
   11065             :          */
   11066         150 :         fkRel = table_open(constrForm->conrelid, AccessShareLock);
   11067             : 
   11068         150 :         indexOid = constrForm->conindid;
   11069         150 :         DeconstructFkConstraintRow(tuple,
   11070             :                                    &numfks,
   11071             :                                    conkey,
   11072             :                                    confkey,
   11073             :                                    conpfeqop,
   11074             :                                    conppeqop,
   11075             :                                    conffeqop,
   11076             :                                    &numfkdelsetcols,
   11077             :                                    confdelsetcols);
   11078             : 
   11079         300 :         for (int i = 0; i < numfks; i++)
   11080         150 :             mapped_confkey[i] = attmap->attnums[confkey[i] - 1];
   11081             : 
   11082         150 :         fkconstraint = makeNode(Constraint);
   11083         150 :         fkconstraint->contype = CONSTRAINT_FOREIGN;
   11084         150 :         fkconstraint->conname = NameStr(constrForm->conname);
   11085         150 :         fkconstraint->deferrable = constrForm->condeferrable;
   11086         150 :         fkconstraint->initdeferred = constrForm->condeferred;
   11087         150 :         fkconstraint->location = -1;
   11088         150 :         fkconstraint->pktable = NULL;
   11089             :         /* ->fk_attrs determined below */
   11090         150 :         fkconstraint->pk_attrs = NIL;
   11091         150 :         fkconstraint->fk_matchtype = constrForm->confmatchtype;
   11092         150 :         fkconstraint->fk_upd_action = constrForm->confupdtype;
   11093         150 :         fkconstraint->fk_del_action = constrForm->confdeltype;
   11094         150 :         fkconstraint->fk_del_set_cols = NIL;
   11095         150 :         fkconstraint->old_conpfeqop = NIL;
   11096         150 :         fkconstraint->old_pktable_oid = InvalidOid;
   11097         150 :         fkconstraint->skip_validation = false;
   11098         150 :         fkconstraint->initially_valid = true;
   11099             : 
   11100             :         /* set up colnames that are used to generate the constraint name */
   11101         300 :         for (int i = 0; i < numfks; i++)
   11102             :         {
   11103             :             Form_pg_attribute att;
   11104             : 
   11105         150 :             att = TupleDescAttr(RelationGetDescr(fkRel),
   11106             :                                 conkey[i] - 1);
   11107         150 :             fkconstraint->fk_attrs = lappend(fkconstraint->fk_attrs,
   11108         150 :                                              makeString(NameStr(att->attname)));
   11109             :         }
   11110             : 
   11111             :         /*
   11112             :          * Add the new foreign key constraint pointing to the new partition.
   11113             :          * Because this new partition appears in the referenced side of the
   11114             :          * constraint, we don't need to set up for Phase 3 check.
   11115             :          */
   11116         150 :         partIndexId = index_get_partition(partitionRel, indexOid);
   11117         150 :         if (!OidIsValid(partIndexId))
   11118           0 :             elog(ERROR, "index for %u not found in partition %s",
   11119             :                  indexOid, RelationGetRelationName(partitionRel));
   11120             : 
   11121             :         /*
   11122             :          * Get the "action" triggers belonging to the constraint to pass as
   11123             :          * parent OIDs for similar triggers that will be created on the
   11124             :          * partition in addFkRecurseReferenced().
   11125             :          */
   11126         150 :         GetForeignKeyActionTriggers(trigrel, constrOid,
   11127             :                                     constrForm->confrelid, constrForm->conrelid,
   11128             :                                     &deleteTriggerOid, &updateTriggerOid);
   11129             : 
   11130         150 :         addFkRecurseReferenced(NULL,
   11131             :                                fkconstraint,
   11132             :                                fkRel,
   11133             :                                partitionRel,
   11134             :                                partIndexId,
   11135             :                                constrOid,
   11136             :                                numfks,
   11137             :                                mapped_confkey,
   11138             :                                conkey,
   11139             :                                conpfeqop,
   11140             :                                conppeqop,
   11141             :                                conffeqop,
   11142             :                                numfkdelsetcols,
   11143             :                                confdelsetcols,
   11144             :                                true,
   11145             :                                deleteTriggerOid,
   11146             :                                updateTriggerOid,
   11147         150 :                                constrForm->conperiod);
   11148             : 
   11149         150 :         table_close(fkRel, NoLock);
   11150         150 :         ReleaseSysCache(tuple);
   11151             :     }
   11152             : 
   11153       10180 :     table_close(trigrel, RowExclusiveLock);
   11154       10180 : }
   11155             : 
   11156             : /*
   11157             :  * CloneFkReferencing
   11158             :  *      Subroutine for CloneForeignKeyConstraints
   11159             :  *
   11160             :  * For each FK constraint of the parent relation in the given list, find an
   11161             :  * equivalent constraint in its partition relation that can be reparented;
   11162             :  * if one cannot be found, create a new constraint in the partition as its
   11163             :  * child.
   11164             :  *
   11165             :  * If wqueue is given, it is used to set up phase-3 verification for each
   11166             :  * cloned constraint; if omitted, we assume that such verification is not
   11167             :  * needed (example: the partition is being created anew).
   11168             :  */
   11169             : static void
   11170       10180 : CloneFkReferencing(List **wqueue, Relation parentRel, Relation partRel)
   11171             : {
   11172             :     AttrMap    *attmap;
   11173             :     List       *partFKs;
   11174       10180 :     List       *clone = NIL;
   11175             :     ListCell   *cell;
   11176             :     Relation    trigrel;
   11177             : 
   11178             :     /* obtain a list of constraints that we need to clone */
   11179       11076 :     foreach(cell, RelationGetFKeyList(parentRel))
   11180             :     {
   11181         896 :         ForeignKeyCacheInfo *fk = lfirst(cell);
   11182             : 
   11183         896 :         clone = lappend_oid(clone, fk->conoid);
   11184             :     }
   11185             : 
   11186             :     /*
   11187             :      * Silently do nothing if there's nothing to do.  In particular, this
   11188             :      * avoids throwing a spurious error for foreign tables.
   11189             :      */
   11190       10180 :     if (clone == NIL)
   11191        9732 :         return;
   11192             : 
   11193         448 :     if (partRel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
   11194           0 :         ereport(ERROR,
   11195             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   11196             :                  errmsg("foreign key constraints are not supported on foreign tables")));
   11197             : 
   11198             :     /*
   11199             :      * Triggers of the foreign keys will be manipulated a bunch of times in
   11200             :      * the loop below.  To avoid repeatedly opening/closing the trigger
   11201             :      * catalog relation, we open it here and pass it to the subroutines called
   11202             :      * below.
   11203             :      */
   11204         448 :     trigrel = table_open(TriggerRelationId, RowExclusiveLock);
   11205             : 
   11206             :     /*
   11207             :      * The constraint key may differ, if the columns in the partition are
   11208             :      * different.  This map is used to convert them.
   11209             :      */
   11210         448 :     attmap = build_attrmap_by_name(RelationGetDescr(partRel),
   11211             :                                    RelationGetDescr(parentRel),
   11212             :                                    false);
   11213             : 
   11214         448 :     partFKs = copyObject(RelationGetFKeyList(partRel));
   11215             : 
   11216        1344 :     foreach(cell, clone)
   11217             :     {
   11218         896 :         Oid         parentConstrOid = lfirst_oid(cell);
   11219             :         Form_pg_constraint constrForm;
   11220             :         Relation    pkrel;
   11221             :         HeapTuple   tuple;
   11222             :         int         numfks;
   11223             :         AttrNumber  conkey[INDEX_MAX_KEYS];
   11224             :         AttrNumber  mapped_conkey[INDEX_MAX_KEYS];
   11225             :         AttrNumber  confkey[INDEX_MAX_KEYS];
   11226             :         Oid         conpfeqop[INDEX_MAX_KEYS];
   11227             :         Oid         conppeqop[INDEX_MAX_KEYS];
   11228             :         Oid         conffeqop[INDEX_MAX_KEYS];
   11229             :         int         numfkdelsetcols;
   11230             :         AttrNumber  confdelsetcols[INDEX_MAX_KEYS];
   11231             :         Constraint *fkconstraint;
   11232             :         bool        attached;
   11233             :         Oid         indexOid;
   11234             :         Oid         constrOid;
   11235             :         ObjectAddress address,
   11236             :                     referenced;
   11237             :         ListCell   *lc;
   11238             :         Oid         insertTriggerOid,
   11239             :                     updateTriggerOid;
   11240             :         bool        with_period;
   11241             : 
   11242         896 :         tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(parentConstrOid));
   11243         896 :         if (!HeapTupleIsValid(tuple))
   11244           0 :             elog(ERROR, "cache lookup failed for constraint %u",
   11245             :                  parentConstrOid);
   11246         896 :         constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
   11247             : 
   11248             :         /* Don't clone constraints whose parents are being cloned */
   11249         896 :         if (list_member_oid(clone, constrForm->conparentid))
   11250             :         {
   11251         424 :             ReleaseSysCache(tuple);
   11252         490 :             continue;
   11253             :         }
   11254             : 
   11255             :         /*
   11256             :          * Need to prevent concurrent deletions.  If pkrel is a partitioned
   11257             :          * relation, that means to lock all partitions.
   11258             :          */
   11259         472 :         pkrel = table_open(constrForm->confrelid, ShareRowExclusiveLock);
   11260         472 :         if (pkrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
   11261         184 :             (void) find_all_inheritors(RelationGetRelid(pkrel),
   11262             :                                        ShareRowExclusiveLock, NULL);
   11263             : 
   11264         472 :         DeconstructFkConstraintRow(tuple, &numfks, conkey, confkey,
   11265             :                                    conpfeqop, conppeqop, conffeqop,
   11266             :                                    &numfkdelsetcols, confdelsetcols);
   11267        1082 :         for (int i = 0; i < numfks; i++)
   11268         610 :             mapped_conkey[i] = attmap->attnums[conkey[i] - 1];
   11269             : 
   11270             :         /*
   11271             :          * Get the "check" triggers belonging to the constraint to pass as
   11272             :          * parent OIDs for similar triggers that will be created on the
   11273             :          * partition in addFkRecurseReferencing().  They are also passed to
   11274             :          * tryAttachPartitionForeignKey() below to simply assign as parents to
   11275             :          * the partition's existing "check" triggers, that is, if the
   11276             :          * corresponding constraints is deemed attachable to the parent
   11277             :          * constraint.
   11278             :          */
   11279         472 :         GetForeignKeyCheckTriggers(trigrel, constrForm->oid,
   11280             :                                    constrForm->confrelid, constrForm->conrelid,
   11281             :                                    &insertTriggerOid, &updateTriggerOid);
   11282             : 
   11283             :         /*
   11284             :          * Before creating a new constraint, see whether any existing FKs are
   11285             :          * fit for the purpose.  If one is, attach the parent constraint to
   11286             :          * it, and don't clone anything.  This way we avoid the expensive
   11287             :          * verification step and don't end up with a duplicate FK, and we
   11288             :          * don't need to recurse to partitions for this constraint.
   11289             :          */
   11290         472 :         attached = false;
   11291         556 :         foreach(lc, partFKs)
   11292             :         {
   11293         150 :             ForeignKeyCacheInfo *fk = lfirst_node(ForeignKeyCacheInfo, lc);
   11294             : 
   11295         150 :             if (tryAttachPartitionForeignKey(fk,
   11296             :                                              RelationGetRelid(partRel),
   11297             :                                              parentConstrOid,
   11298             :                                              numfks,
   11299             :                                              mapped_conkey,
   11300             :                                              confkey,
   11301             :                                              conpfeqop,
   11302             :                                              insertTriggerOid,
   11303             :                                              updateTriggerOid,
   11304             :                                              trigrel))
   11305             :             {
   11306          66 :                 attached = true;
   11307          66 :                 table_close(pkrel, NoLock);
   11308          66 :                 break;
   11309             :             }
   11310             :         }
   11311         472 :         if (attached)
   11312             :         {
   11313          66 :             ReleaseSysCache(tuple);
   11314          66 :             continue;
   11315             :         }
   11316             : 
   11317             :         /* No dice.  Set up to create our own constraint */
   11318         406 :         fkconstraint = makeNode(Constraint);
   11319         406 :         fkconstraint->contype = CONSTRAINT_FOREIGN;
   11320             :         /* ->conname determined below */
   11321         406 :         fkconstraint->deferrable = constrForm->condeferrable;
   11322         406 :         fkconstraint->initdeferred = constrForm->condeferred;
   11323         406 :         fkconstraint->location = -1;
   11324         406 :         fkconstraint->pktable = NULL;
   11325             :         /* ->fk_attrs determined below */
   11326         406 :         fkconstraint->pk_attrs = NIL;
   11327         406 :         fkconstraint->fk_matchtype = constrForm->confmatchtype;
   11328         406 :         fkconstraint->fk_upd_action = constrForm->confupdtype;
   11329         406 :         fkconstraint->fk_del_action = constrForm->confdeltype;
   11330         406 :         fkconstraint->fk_del_set_cols = NIL;
   11331         406 :         fkconstraint->old_conpfeqop = NIL;
   11332         406 :         fkconstraint->old_pktable_oid = InvalidOid;
   11333         406 :         fkconstraint->skip_validation = false;
   11334         406 :         fkconstraint->initially_valid = true;
   11335         902 :         for (int i = 0; i < numfks; i++)
   11336             :         {
   11337             :             Form_pg_attribute att;
   11338             : 
   11339         496 :             att = TupleDescAttr(RelationGetDescr(partRel),
   11340             :                                 mapped_conkey[i] - 1);
   11341         496 :             fkconstraint->fk_attrs = lappend(fkconstraint->fk_attrs,
   11342         496 :                                              makeString(NameStr(att->attname)));
   11343             :         }
   11344         406 :         if (ConstraintNameIsUsed(CONSTRAINT_RELATION,
   11345             :                                  RelationGetRelid(partRel),
   11346         406 :                                  NameStr(constrForm->conname)))
   11347           6 :             fkconstraint->conname =
   11348           6 :                 ChooseConstraintName(RelationGetRelationName(partRel),
   11349           6 :                                      ChooseForeignKeyConstraintNameAddition(fkconstraint->fk_attrs),
   11350             :                                      "fkey",
   11351           6 :                                      RelationGetNamespace(partRel), NIL);
   11352             :         else
   11353         400 :             fkconstraint->conname = pstrdup(NameStr(constrForm->conname));
   11354             : 
   11355         406 :         indexOid = constrForm->conindid;
   11356         406 :         with_period = constrForm->conperiod;
   11357             :         constrOid =
   11358         406 :             CreateConstraintEntry(fkconstraint->conname,
   11359             :                                   constrForm->connamespace,
   11360             :                                   CONSTRAINT_FOREIGN,
   11361         406 :                                   fkconstraint->deferrable,
   11362         406 :                                   fkconstraint->initdeferred,
   11363         406 :                                   constrForm->convalidated,
   11364             :                                   parentConstrOid,
   11365             :                                   RelationGetRelid(partRel),
   11366             :                                   mapped_conkey,
   11367             :                                   numfks,
   11368             :                                   numfks,
   11369             :                                   InvalidOid,   /* not a domain constraint */
   11370             :                                   indexOid,
   11371             :                                   constrForm->confrelid, /* same foreign rel */
   11372             :                                   confkey,
   11373             :                                   conpfeqop,
   11374             :                                   conppeqop,
   11375             :                                   conffeqop,
   11376             :                                   numfks,
   11377         406 :                                   fkconstraint->fk_upd_action,
   11378         406 :                                   fkconstraint->fk_del_action,
   11379             :                                   confdelsetcols,
   11380             :                                   numfkdelsetcols,
   11381         406 :                                   fkconstraint->fk_matchtype,
   11382             :                                   NULL,
   11383             :                                   NULL,
   11384             :                                   NULL,
   11385             :                                   false,    /* islocal */
   11386             :                                   1,    /* inhcount */
   11387             :                                   false,    /* conNoInherit */
   11388             :                                   with_period,  /* conPeriod */
   11389             :                                   true);
   11390             : 
   11391             :         /* Set up partition dependencies for the new constraint */
   11392         406 :         ObjectAddressSet(address, ConstraintRelationId, constrOid);
   11393         406 :         ObjectAddressSet(referenced, ConstraintRelationId, parentConstrOid);
   11394         406 :         recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_PRI);
   11395         406 :         ObjectAddressSet(referenced, RelationRelationId,
   11396             :                          RelationGetRelid(partRel));
   11397         406 :         recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_SEC);
   11398             : 
   11399             :         /* Done with the cloned constraint's tuple */
   11400         406 :         ReleaseSysCache(tuple);
   11401             : 
   11402             :         /* Make all this visible before recursing */
   11403         406 :         CommandCounterIncrement();
   11404             : 
   11405         406 :         addFkRecurseReferencing(wqueue,
   11406             :                                 fkconstraint,
   11407             :                                 partRel,
   11408             :                                 pkrel,
   11409             :                                 indexOid,
   11410             :                                 constrOid,
   11411             :                                 numfks,
   11412             :                                 confkey,
   11413             :                                 mapped_conkey,
   11414             :                                 conpfeqop,
   11415             :                                 conppeqop,
   11416             :                                 conffeqop,
   11417             :                                 numfkdelsetcols,
   11418             :                                 confdelsetcols,
   11419             :                                 false,  /* no old check exists */
   11420             :                                 AccessExclusiveLock,
   11421             :                                 insertTriggerOid,
   11422             :                                 updateTriggerOid,
   11423             :                                 with_period);
   11424         406 :         table_close(pkrel, NoLock);
   11425             :     }
   11426             : 
   11427         448 :     table_close(trigrel, RowExclusiveLock);
   11428             : }
   11429             : 
   11430             : /*
   11431             :  * When the parent of a partition receives [the referencing side of] a foreign
   11432             :  * key, we must propagate that foreign key to the partition.  However, the
   11433             :  * partition might already have an equivalent foreign key; this routine
   11434             :  * compares the given ForeignKeyCacheInfo (in the partition) to the FK defined
   11435             :  * by the other parameters.  If they are equivalent, create the link between
   11436             :  * the two constraints and return true.
   11437             :  *
   11438             :  * If the given FK does not match the one defined by rest of the params,
   11439             :  * return false.
   11440             :  */
   11441             : static bool
   11442         180 : tryAttachPartitionForeignKey(ForeignKeyCacheInfo *fk,
   11443             :                              Oid partRelid,
   11444             :                              Oid parentConstrOid,
   11445             :                              int numfks,
   11446             :                              AttrNumber *mapped_conkey,
   11447             :                              AttrNumber *confkey,
   11448             :                              Oid *conpfeqop,
   11449             :                              Oid parentInsTrigger,
   11450             :                              Oid parentUpdTrigger,
   11451             :                              Relation trigrel)
   11452             : {
   11453             :     HeapTuple   parentConstrTup;
   11454             :     Form_pg_constraint parentConstr;
   11455             :     HeapTuple   partcontup;
   11456             :     Form_pg_constraint partConstr;
   11457             :     ScanKeyData key;
   11458             :     SysScanDesc scan;
   11459             :     HeapTuple   trigtup;
   11460             :     Oid         insertTriggerOid,
   11461             :                 updateTriggerOid;
   11462             : 
   11463         180 :     parentConstrTup = SearchSysCache1(CONSTROID,
   11464             :                                       ObjectIdGetDatum(parentConstrOid));
   11465         180 :     if (!HeapTupleIsValid(parentConstrTup))
   11466           0 :         elog(ERROR, "cache lookup failed for constraint %u", parentConstrOid);
   11467         180 :     parentConstr = (Form_pg_constraint) GETSTRUCT(parentConstrTup);
   11468             : 
   11469             :     /*
   11470             :      * Do some quick & easy initial checks.  If any of these fail, we cannot
   11471             :      * use this constraint.
   11472             :      */
   11473         180 :     if (fk->confrelid != parentConstr->confrelid || fk->nkeys != numfks)
   11474             :     {
   11475           0 :         ReleaseSysCache(parentConstrTup);
   11476           0 :         return false;
   11477             :     }
   11478         510 :     for (int i = 0; i < numfks; i++)
   11479             :     {
   11480         330 :         if (fk->conkey[i] != mapped_conkey[i] ||
   11481         330 :             fk->confkey[i] != confkey[i] ||
   11482         330 :             fk->conpfeqop[i] != conpfeqop[i])
   11483             :         {
   11484           0 :             ReleaseSysCache(parentConstrTup);
   11485           0 :             return false;
   11486             :         }
   11487             :     }
   11488             : 
   11489             :     /*
   11490             :      * Looks good so far; do some more extensive checks.  Presumably the check
   11491             :      * for 'convalidated' could be dropped, since we don't really care about
   11492             :      * that, but let's be careful for now.
   11493             :      */
   11494         180 :     partcontup = SearchSysCache1(CONSTROID,
   11495             :                                  ObjectIdGetDatum(fk->conoid));
   11496         180 :     if (!HeapTupleIsValid(partcontup))
   11497           0 :         elog(ERROR, "cache lookup failed for constraint %u", fk->conoid);
   11498         180 :     partConstr = (Form_pg_constraint) GETSTRUCT(partcontup);
   11499         180 :     if (OidIsValid(partConstr->conparentid) ||
   11500         156 :         !partConstr->convalidated ||
   11501         156 :         partConstr->condeferrable != parentConstr->condeferrable ||
   11502         128 :         partConstr->condeferred != parentConstr->condeferred ||
   11503         128 :         partConstr->confupdtype != parentConstr->confupdtype ||
   11504          92 :         partConstr->confdeltype != parentConstr->confdeltype ||
   11505          92 :         partConstr->confmatchtype != parentConstr->confmatchtype)
   11506             :     {
   11507         102 :         ReleaseSysCache(parentConstrTup);
   11508         102 :         ReleaseSysCache(partcontup);
   11509         102 :         return false;
   11510             :     }
   11511             : 
   11512          78 :     ReleaseSysCache(partcontup);
   11513          78 :     ReleaseSysCache(parentConstrTup);
   11514             : 
   11515             :     /*
   11516             :      * Looks good!  Attach this constraint.  The action triggers in the new
   11517             :      * partition become redundant -- the parent table already has equivalent
   11518             :      * ones, and those will be able to reach the partition.  Remove the ones
   11519             :      * in the partition.  We identify them because they have our constraint
   11520             :      * OID, as well as being on the referenced rel.
   11521             :      */
   11522          78 :     ScanKeyInit(&key,
   11523             :                 Anum_pg_trigger_tgconstraint,
   11524             :                 BTEqualStrategyNumber, F_OIDEQ,
   11525             :                 ObjectIdGetDatum(fk->conoid));
   11526          78 :     scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
   11527             :                               NULL, 1, &key);
   11528         390 :     while ((trigtup = systable_getnext(scan)) != NULL)
   11529             :     {
   11530         312 :         Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
   11531             :         ObjectAddress trigger;
   11532             : 
   11533         312 :         if (trgform->tgconstrrelid != fk->conrelid)
   11534         156 :             continue;
   11535         156 :         if (trgform->tgrelid != fk->confrelid)
   11536           0 :             continue;
   11537             : 
   11538             :         /*
   11539             :          * The constraint is originally set up to contain this trigger as an
   11540             :          * implementation object, so there's a dependency record that links
   11541             :          * the two; however, since the trigger is no longer needed, we remove
   11542             :          * the dependency link in order to be able to drop the trigger while
   11543             :          * keeping the constraint intact.
   11544             :          */
   11545         156 :         deleteDependencyRecordsFor(TriggerRelationId,
   11546             :                                    trgform->oid,
   11547             :                                    false);
   11548             :         /* make dependency deletion visible to performDeletion */
   11549         156 :         CommandCounterIncrement();
   11550         156 :         ObjectAddressSet(trigger, TriggerRelationId,
   11551             :                          trgform->oid);
   11552         156 :         performDeletion(&trigger, DROP_RESTRICT, 0);
   11553             :         /* make trigger drop visible, in case the loop iterates */
   11554         156 :         CommandCounterIncrement();
   11555             :     }
   11556             : 
   11557          78 :     systable_endscan(scan);
   11558             : 
   11559          78 :     ConstraintSetParentConstraint(fk->conoid, parentConstrOid, partRelid);
   11560             : 
   11561             :     /*
   11562             :      * Like the constraint, attach partition's "check" triggers to the
   11563             :      * corresponding parent triggers.
   11564             :      */
   11565          78 :     GetForeignKeyCheckTriggers(trigrel,
   11566             :                                fk->conoid, fk->confrelid, fk->conrelid,
   11567             :                                &insertTriggerOid, &updateTriggerOid);
   11568             :     Assert(OidIsValid(insertTriggerOid) && OidIsValid(parentInsTrigger));
   11569          78 :     TriggerSetParentTrigger(trigrel, insertTriggerOid, parentInsTrigger,
   11570             :                             partRelid);
   11571             :     Assert(OidIsValid(updateTriggerOid) && OidIsValid(parentUpdTrigger));
   11572          78 :     TriggerSetParentTrigger(trigrel, updateTriggerOid, parentUpdTrigger,
   11573             :                             partRelid);
   11574             : 
   11575          78 :     CommandCounterIncrement();
   11576          78 :     return true;
   11577             : }
   11578             : 
   11579             : /*
   11580             :  * GetForeignKeyActionTriggers
   11581             :  *      Returns delete and update "action" triggers of the given relation
   11582             :  *      belonging to the given constraint
   11583             :  */
   11584             : static void
   11585         150 : GetForeignKeyActionTriggers(Relation trigrel,
   11586             :                             Oid conoid, Oid confrelid, Oid conrelid,
   11587             :                             Oid *deleteTriggerOid,
   11588             :                             Oid *updateTriggerOid)
   11589             : {
   11590             :     ScanKeyData key;
   11591             :     SysScanDesc scan;
   11592             :     HeapTuple   trigtup;
   11593             : 
   11594         150 :     *deleteTriggerOid = *updateTriggerOid = InvalidOid;
   11595         150 :     ScanKeyInit(&key,
   11596             :                 Anum_pg_trigger_tgconstraint,
   11597             :                 BTEqualStrategyNumber, F_OIDEQ,
   11598             :                 ObjectIdGetDatum(conoid));
   11599             : 
   11600         150 :     scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
   11601             :                               NULL, 1, &key);
   11602         312 :     while ((trigtup = systable_getnext(scan)) != NULL)
   11603             :     {
   11604         312 :         Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
   11605             : 
   11606         312 :         if (trgform->tgconstrrelid != conrelid)
   11607          12 :             continue;
   11608         300 :         if (trgform->tgrelid != confrelid)
   11609           0 :             continue;
   11610             :         /* Only ever look at "action" triggers on the PK side. */
   11611         300 :         if (RI_FKey_trigger_type(trgform->tgfoid) != RI_TRIGGER_PK)
   11612           0 :             continue;
   11613         300 :         if (TRIGGER_FOR_DELETE(trgform->tgtype))
   11614             :         {
   11615             :             Assert(*deleteTriggerOid == InvalidOid);
   11616         150 :             *deleteTriggerOid = trgform->oid;
   11617             :         }
   11618         150 :         else if (TRIGGER_FOR_UPDATE(trgform->tgtype))
   11619             :         {
   11620             :             Assert(*updateTriggerOid == InvalidOid);
   11621         150 :             *updateTriggerOid = trgform->oid;
   11622             :         }
   11623             : #ifndef USE_ASSERT_CHECKING
   11624             :         /* In an assert-enabled build, continue looking to find duplicates */
   11625         300 :         if (OidIsValid(*deleteTriggerOid) && OidIsValid(*updateTriggerOid))
   11626         150 :             break;
   11627             : #endif
   11628             :     }
   11629             : 
   11630         150 :     if (!OidIsValid(*deleteTriggerOid))
   11631           0 :         elog(ERROR, "could not find ON DELETE action trigger of foreign key constraint %u",
   11632             :              conoid);
   11633         150 :     if (!OidIsValid(*updateTriggerOid))
   11634           0 :         elog(ERROR, "could not find ON UPDATE action trigger of foreign key constraint %u",
   11635             :              conoid);
   11636             : 
   11637         150 :     systable_endscan(scan);
   11638         150 : }
   11639             : 
   11640             : /*
   11641             :  * GetForeignKeyCheckTriggers
   11642             :  *      Returns insert and update "check" triggers of the given relation
   11643             :  *      belonging to the given constraint
   11644             :  */
   11645             : static void
   11646         616 : GetForeignKeyCheckTriggers(Relation trigrel,
   11647             :                            Oid conoid, Oid confrelid, Oid conrelid,
   11648             :                            Oid *insertTriggerOid,
   11649             :                            Oid *updateTriggerOid)
   11650             : {
   11651             :     ScanKeyData key;
   11652             :     SysScanDesc scan;
   11653             :     HeapTuple   trigtup;
   11654             : 
   11655         616 :     *insertTriggerOid = *updateTriggerOid = InvalidOid;
   11656         616 :     ScanKeyInit(&key,
   11657             :                 Anum_pg_trigger_tgconstraint,
   11658             :                 BTEqualStrategyNumber, F_OIDEQ,
   11659             :                 ObjectIdGetDatum(conoid));
   11660             : 
   11661         616 :     scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
   11662             :                               NULL, 1, &key);
   11663        2036 :     while ((trigtup = systable_getnext(scan)) != NULL)
   11664             :     {
   11665        2036 :         Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
   11666             : 
   11667        2036 :         if (trgform->tgconstrrelid != confrelid)
   11668         720 :             continue;
   11669        1316 :         if (trgform->tgrelid != conrelid)
   11670           0 :             continue;
   11671             :         /* Only ever look at "check" triggers on the FK side. */
   11672        1316 :         if (RI_FKey_trigger_type(trgform->tgfoid) != RI_TRIGGER_FK)
   11673          84 :             continue;
   11674        1232 :         if (TRIGGER_FOR_INSERT(trgform->tgtype))
   11675             :         {
   11676             :             Assert(*insertTriggerOid == InvalidOid);
   11677         616 :             *insertTriggerOid = trgform->oid;
   11678             :         }
   11679         616 :         else if (TRIGGER_FOR_UPDATE(trgform->tgtype))
   11680             :         {
   11681             :             Assert(*updateTriggerOid == InvalidOid);
   11682         616 :             *updateTriggerOid = trgform->oid;
   11683             :         }
   11684             : #ifndef USE_ASSERT_CHECKING
   11685             :         /* In an assert-enabled build, continue looking to find duplicates. */
   11686        1232 :         if (OidIsValid(*insertTriggerOid) && OidIsValid(*updateTriggerOid))
   11687         616 :             break;
   11688             : #endif
   11689             :     }
   11690             : 
   11691         616 :     if (!OidIsValid(*insertTriggerOid))
   11692           0 :         elog(ERROR, "could not find ON INSERT check triggers of foreign key constraint %u",
   11693             :              conoid);
   11694         616 :     if (!OidIsValid(*updateTriggerOid))
   11695           0 :         elog(ERROR, "could not find ON UPDATE check triggers of foreign key constraint %u",
   11696             :              conoid);
   11697             : 
   11698         616 :     systable_endscan(scan);
   11699         616 : }
   11700             : 
   11701             : /*
   11702             :  * ALTER TABLE ALTER CONSTRAINT
   11703             :  *
   11704             :  * Update the attributes of a constraint.
   11705             :  *
   11706             :  * Currently only works for Foreign Key constraints.
   11707             :  *
   11708             :  * If the constraint is modified, returns its address; otherwise, return
   11709             :  * InvalidObjectAddress.
   11710             :  */
   11711             : static ObjectAddress
   11712         126 : ATExecAlterConstraint(Relation rel, AlterTableCmd *cmd, bool recurse,
   11713             :                       bool recursing, LOCKMODE lockmode)
   11714             : {
   11715             :     Constraint *cmdcon;
   11716             :     Relation    conrel;
   11717             :     Relation    tgrel;
   11718             :     SysScanDesc scan;
   11719             :     ScanKeyData skey[3];
   11720             :     HeapTuple   contuple;
   11721             :     Form_pg_constraint currcon;
   11722             :     ObjectAddress address;
   11723         126 :     List       *otherrelids = NIL;
   11724             :     ListCell   *lc;
   11725             : 
   11726         126 :     cmdcon = castNode(Constraint, cmd->def);
   11727             : 
   11728         126 :     conrel = table_open(ConstraintRelationId, RowExclusiveLock);
   11729         126 :     tgrel = table_open(TriggerRelationId, RowExclusiveLock);
   11730             : 
   11731             :     /*
   11732             :      * Find and check the target constraint
   11733             :      */
   11734         126 :     ScanKeyInit(&skey[0],
   11735             :                 Anum_pg_constraint_conrelid,
   11736             :                 BTEqualStrategyNumber, F_OIDEQ,
   11737             :                 ObjectIdGetDatum(RelationGetRelid(rel)));
   11738         126 :     ScanKeyInit(&skey[1],
   11739             :                 Anum_pg_constraint_contypid,
   11740             :                 BTEqualStrategyNumber, F_OIDEQ,
   11741             :                 ObjectIdGetDatum(InvalidOid));
   11742         126 :     ScanKeyInit(&skey[2],
   11743             :                 Anum_pg_constraint_conname,
   11744             :                 BTEqualStrategyNumber, F_NAMEEQ,
   11745         126 :                 CStringGetDatum(cmdcon->conname));
   11746         126 :     scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
   11747             :                               true, NULL, 3, skey);
   11748             : 
   11749             :     /* There can be at most one matching row */
   11750         126 :     if (!HeapTupleIsValid(contuple = systable_getnext(scan)))
   11751           0 :         ereport(ERROR,
   11752             :                 (errcode(ERRCODE_UNDEFINED_OBJECT),
   11753             :                  errmsg("constraint \"%s\" of relation \"%s\" does not exist",
   11754             :                         cmdcon->conname, RelationGetRelationName(rel))));
   11755             : 
   11756         126 :     currcon = (Form_pg_constraint) GETSTRUCT(contuple);
   11757         126 :     if (currcon->contype != CONSTRAINT_FOREIGN)
   11758           0 :         ereport(ERROR,
   11759             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   11760             :                  errmsg("constraint \"%s\" of relation \"%s\" is not a foreign key constraint",
   11761             :                         cmdcon->conname, RelationGetRelationName(rel))));
   11762             : 
   11763             :     /*
   11764             :      * If it's not the topmost constraint, raise an error.
   11765             :      *
   11766             :      * Altering a non-topmost constraint leaves some triggers untouched, since
   11767             :      * they are not directly connected to this constraint; also, pg_dump would
   11768             :      * ignore the deferrability status of the individual constraint, since it
   11769             :      * only dumps topmost constraints.  Avoid these problems by refusing this
   11770             :      * operation and telling the user to alter the parent constraint instead.
   11771             :      */
   11772         126 :     if (OidIsValid(currcon->conparentid))
   11773             :     {
   11774             :         HeapTuple   tp;
   11775          12 :         Oid         parent = currcon->conparentid;
   11776          12 :         char       *ancestorname = NULL;
   11777          12 :         char       *ancestortable = NULL;
   11778             : 
   11779             :         /* Loop to find the topmost constraint */
   11780          24 :         while (HeapTupleIsValid(tp = SearchSysCache1(CONSTROID, ObjectIdGetDatum(parent))))
   11781             :         {
   11782          24 :             Form_pg_constraint contup = (Form_pg_constraint) GETSTRUCT(tp);
   11783             : 
   11784             :             /* If no parent, this is the constraint we want */
   11785          24 :             if (!OidIsValid(contup->conparentid))
   11786             :             {
   11787          12 :                 ancestorname = pstrdup(NameStr(contup->conname));
   11788          12 :                 ancestortable = get_rel_name(contup->conrelid);
   11789          12 :                 ReleaseSysCache(tp);
   11790          12 :                 break;
   11791             :             }
   11792             : 
   11793          12 :             parent = contup->conparentid;
   11794          12 :             ReleaseSysCache(tp);
   11795             :         }
   11796             : 
   11797          12 :         ereport(ERROR,
   11798             :                 (errmsg("cannot alter constraint \"%s\" on relation \"%s\"",
   11799             :                         cmdcon->conname, RelationGetRelationName(rel)),
   11800             :                  ancestorname && ancestortable ?
   11801             :                  errdetail("Constraint \"%s\" is derived from constraint \"%s\" of relation \"%s\".",
   11802             :                            cmdcon->conname, ancestorname, ancestortable) : 0,
   11803             :                  errhint("You may alter the constraint it derives from instead.")));
   11804             :     }
   11805             : 
   11806             :     /*
   11807             :      * Do the actual catalog work.  We can skip changing if already in the
   11808             :      * desired state, but not if a partitioned table: partitions need to be
   11809             :      * processed regardless, in case they had the constraint locally changed.
   11810             :      */
   11811         114 :     address = InvalidObjectAddress;
   11812         114 :     if (currcon->condeferrable != cmdcon->deferrable ||
   11813           6 :         currcon->condeferred != cmdcon->initdeferred ||
   11814           0 :         rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
   11815             :     {
   11816         114 :         if (ATExecAlterConstrRecurse(cmdcon, conrel, tgrel, rel, contuple,
   11817             :                                      &otherrelids, lockmode))
   11818         114 :             ObjectAddressSet(address, ConstraintRelationId, currcon->oid);
   11819             :     }
   11820             : 
   11821             :     /*
   11822             :      * ATExecAlterConstrRecurse already invalidated relcache for the relations
   11823             :      * having the constraint itself; here we also invalidate for relations
   11824             :      * that have any triggers that are part of the constraint.
   11825             :      */
   11826         258 :     foreach(lc, otherrelids)
   11827         144 :         CacheInvalidateRelcacheByRelid(lfirst_oid(lc));
   11828             : 
   11829         114 :     systable_endscan(scan);
   11830             : 
   11831         114 :     table_close(tgrel, RowExclusiveLock);
   11832         114 :     table_close(conrel, RowExclusiveLock);
   11833             : 
   11834         114 :     return address;
   11835             : }
   11836             : 
   11837             : /*
   11838             :  * Recursive subroutine of ATExecAlterConstraint.  Returns true if the
   11839             :  * constraint is altered.
   11840             :  *
   11841             :  * *otherrelids is appended OIDs of relations containing affected triggers.
   11842             :  *
   11843             :  * Note that we must recurse even when the values are correct, in case
   11844             :  * indirect descendants have had their constraints altered locally.
   11845             :  * (This could be avoided if we forbade altering constraints in partitions
   11846             :  * but existing releases don't do that.)
   11847             :  */
   11848             : static bool
   11849         180 : ATExecAlterConstrRecurse(Constraint *cmdcon, Relation conrel, Relation tgrel,
   11850             :                          Relation rel, HeapTuple contuple, List **otherrelids,
   11851             :                          LOCKMODE lockmode)
   11852             : {
   11853             :     Form_pg_constraint currcon;
   11854             :     Oid         conoid;
   11855             :     Oid         refrelid;
   11856         180 :     bool        changed = false;
   11857             : 
   11858             :     /* since this function recurses, it could be driven to stack overflow */
   11859         180 :     check_stack_depth();
   11860             : 
   11861         180 :     currcon = (Form_pg_constraint) GETSTRUCT(contuple);
   11862         180 :     conoid = currcon->oid;
   11863         180 :     refrelid = currcon->confrelid;
   11864             : 
   11865             :     /*
   11866             :      * Update pg_constraint with the flags from cmdcon.
   11867             :      *
   11868             :      * If called to modify a constraint that's already in the desired state,
   11869             :      * silently do nothing.
   11870             :      */
   11871         180 :     if (currcon->condeferrable != cmdcon->deferrable ||
   11872           6 :         currcon->condeferred != cmdcon->initdeferred)
   11873             :     {
   11874             :         HeapTuple   copyTuple;
   11875             :         Form_pg_constraint copy_con;
   11876             :         HeapTuple   tgtuple;
   11877             :         ScanKeyData tgkey;
   11878             :         SysScanDesc tgscan;
   11879             : 
   11880         180 :         copyTuple = heap_copytuple(contuple);
   11881         180 :         copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
   11882         180 :         copy_con->condeferrable = cmdcon->deferrable;
   11883         180 :         copy_con->condeferred = cmdcon->initdeferred;
   11884         180 :         CatalogTupleUpdate(conrel, &copyTuple->t_self, copyTuple);
   11885             : 
   11886         180 :         InvokeObjectPostAlterHook(ConstraintRelationId,
   11887             :                                   conoid, 0);
   11888             : 
   11889         180 :         heap_freetuple(copyTuple);
   11890         180 :         changed = true;
   11891             : 
   11892             :         /* Make new constraint flags visible to others */
   11893         180 :         CacheInvalidateRelcache(rel);
   11894             : 
   11895             :         /*
   11896             :          * Now we need to update the multiple entries in pg_trigger that
   11897             :          * implement the constraint.
   11898             :          */
   11899         180 :         ScanKeyInit(&tgkey,
   11900             :                     Anum_pg_trigger_tgconstraint,
   11901             :                     BTEqualStrategyNumber, F_OIDEQ,
   11902             :                     ObjectIdGetDatum(conoid));
   11903         180 :         tgscan = systable_beginscan(tgrel, TriggerConstraintIndexId, true,
   11904             :                                     NULL, 1, &tgkey);
   11905         768 :         while (HeapTupleIsValid(tgtuple = systable_getnext(tgscan)))
   11906             :         {
   11907         588 :             Form_pg_trigger tgform = (Form_pg_trigger) GETSTRUCT(tgtuple);
   11908             :             Form_pg_trigger copy_tg;
   11909             :             HeapTuple   tgCopyTuple;
   11910             : 
   11911             :             /*
   11912             :              * Remember OIDs of other relation(s) involved in FK constraint.
   11913             :              * (Note: it's likely that we could skip forcing a relcache inval
   11914             :              * for other rels that don't have a trigger whose properties
   11915             :              * change, but let's be conservative.)
   11916             :              */
   11917         588 :             if (tgform->tgrelid != RelationGetRelid(rel))
   11918         288 :                 *otherrelids = list_append_unique_oid(*otherrelids,
   11919             :                                                       tgform->tgrelid);
   11920             : 
   11921             :             /*
   11922             :              * Update deferrability of RI_FKey_noaction_del,
   11923             :              * RI_FKey_noaction_upd, RI_FKey_check_ins and RI_FKey_check_upd
   11924             :              * triggers, but not others; see createForeignKeyActionTriggers
   11925             :              * and CreateFKCheckTrigger.
   11926             :              */
   11927         588 :             if (tgform->tgfoid != F_RI_FKEY_NOACTION_DEL &&
   11928         474 :                 tgform->tgfoid != F_RI_FKEY_NOACTION_UPD &&
   11929         342 :                 tgform->tgfoid != F_RI_FKEY_CHECK_INS &&
   11930         192 :                 tgform->tgfoid != F_RI_FKEY_CHECK_UPD)
   11931          42 :                 continue;
   11932             : 
   11933         546 :             tgCopyTuple = heap_copytuple(tgtuple);
   11934         546 :             copy_tg = (Form_pg_trigger) GETSTRUCT(tgCopyTuple);
   11935             : 
   11936         546 :             copy_tg->tgdeferrable = cmdcon->deferrable;
   11937         546 :             copy_tg->tginitdeferred = cmdcon->initdeferred;
   11938         546 :             CatalogTupleUpdate(tgrel, &tgCopyTuple->t_self, tgCopyTuple);
   11939             : 
   11940         546 :             InvokeObjectPostAlterHook(TriggerRelationId, tgform->oid, 0);
   11941             : 
   11942         546 :             heap_freetuple(tgCopyTuple);
   11943             :         }
   11944             : 
   11945         180 :         systable_endscan(tgscan);
   11946             :     }
   11947             : 
   11948             :     /*
   11949             :      * If the table at either end of the constraint is partitioned, we need to
   11950             :      * recurse and handle every constraint that is a child of this one.
   11951             :      *
   11952             :      * (This assumes that the recurse flag is forcibly set for partitioned
   11953             :      * tables, and not set for legacy inheritance, though we don't check for
   11954             :      * that here.)
   11955             :      */
   11956         336 :     if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
   11957         156 :         get_rel_relkind(refrelid) == RELKIND_PARTITIONED_TABLE)
   11958             :     {
   11959             :         ScanKeyData pkey;
   11960             :         SysScanDesc pscan;
   11961             :         HeapTuple   childtup;
   11962             : 
   11963          42 :         ScanKeyInit(&pkey,
   11964             :                     Anum_pg_constraint_conparentid,
   11965             :                     BTEqualStrategyNumber, F_OIDEQ,
   11966             :                     ObjectIdGetDatum(conoid));
   11967             : 
   11968          42 :         pscan = systable_beginscan(conrel, ConstraintParentIndexId,
   11969             :                                    true, NULL, 1, &pkey);
   11970             : 
   11971         108 :         while (HeapTupleIsValid(childtup = systable_getnext(pscan)))
   11972             :         {
   11973          66 :             Form_pg_constraint childcon = (Form_pg_constraint) GETSTRUCT(childtup);
   11974             :             Relation    childrel;
   11975             : 
   11976          66 :             childrel = table_open(childcon->conrelid, lockmode);
   11977          66 :             ATExecAlterConstrRecurse(cmdcon, conrel, tgrel, childrel, childtup,
   11978             :                                      otherrelids, lockmode);
   11979          66 :             table_close(childrel, NoLock);
   11980             :         }
   11981             : 
   11982          42 :         systable_endscan(pscan);
   11983             :     }
   11984             : 
   11985         180 :     return changed;
   11986             : }
   11987             : 
   11988             : /*
   11989             :  * ALTER TABLE VALIDATE CONSTRAINT
   11990             :  *
   11991             :  * XXX The reason we handle recursion here rather than at Phase 1 is because
   11992             :  * there's no good way to skip recursing when handling foreign keys: there is
   11993             :  * no need to lock children in that case, yet we wouldn't be able to avoid
   11994             :  * doing so at that level.
   11995             :  *
   11996             :  * Return value is the address of the validated constraint.  If the constraint
   11997             :  * was already validated, InvalidObjectAddress is returned.
   11998             :  */
   11999             : static ObjectAddress
   12000         436 : ATExecValidateConstraint(List **wqueue, Relation rel, char *constrName,
   12001             :                          bool recurse, bool recursing, LOCKMODE lockmode)
   12002             : {
   12003             :     Relation    conrel;
   12004             :     SysScanDesc scan;
   12005             :     ScanKeyData skey[3];
   12006             :     HeapTuple   tuple;
   12007             :     Form_pg_constraint con;
   12008             :     ObjectAddress address;
   12009             : 
   12010         436 :     conrel = table_open(ConstraintRelationId, RowExclusiveLock);
   12011             : 
   12012             :     /*
   12013             :      * Find and check the target constraint
   12014             :      */
   12015         436 :     ScanKeyInit(&skey[0],
   12016             :                 Anum_pg_constraint_conrelid,
   12017             :                 BTEqualStrategyNumber, F_OIDEQ,
   12018             :                 ObjectIdGetDatum(RelationGetRelid(rel)));
   12019         436 :     ScanKeyInit(&skey[1],
   12020             :                 Anum_pg_constraint_contypid,
   12021             :                 BTEqualStrategyNumber, F_OIDEQ,
   12022             :                 ObjectIdGetDatum(InvalidOid));
   12023         436 :     ScanKeyInit(&skey[2],
   12024             :                 Anum_pg_constraint_conname,
   12025             :                 BTEqualStrategyNumber, F_NAMEEQ,
   12026             :                 CStringGetDatum(constrName));
   12027         436 :     scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
   12028             :                               true, NULL, 3, skey);
   12029             : 
   12030             :     /* There can be at most one matching row */
   12031         436 :     if (!HeapTupleIsValid(tuple = systable_getnext(scan)))
   12032           0 :         ereport(ERROR,
   12033             :                 (errcode(ERRCODE_UNDEFINED_OBJECT),
   12034             :                  errmsg("constraint \"%s\" of relation \"%s\" does not exist",
   12035             :                         constrName, RelationGetRelationName(rel))));
   12036             : 
   12037         436 :     con = (Form_pg_constraint) GETSTRUCT(tuple);
   12038         436 :     if (con->contype != CONSTRAINT_FOREIGN &&
   12039         132 :         con->contype != CONSTRAINT_CHECK)
   12040           0 :         ereport(ERROR,
   12041             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   12042             :                  errmsg("constraint \"%s\" of relation \"%s\" is not a foreign key or check constraint",
   12043             :                         constrName, RelationGetRelationName(rel))));
   12044             : 
   12045         436 :     if (!con->convalidated)
   12046             :     {
   12047             :         AlteredTableInfo *tab;
   12048             :         HeapTuple   copyTuple;
   12049             :         Form_pg_constraint copy_con;
   12050             : 
   12051         418 :         if (con->contype == CONSTRAINT_FOREIGN)
   12052             :         {
   12053             :             NewConstraint *newcon;
   12054             :             Constraint *fkconstraint;
   12055             : 
   12056             :             /* Queue validation for phase 3 */
   12057         298 :             fkconstraint = makeNode(Constraint);
   12058             :             /* for now this is all we need */
   12059         298 :             fkconstraint->conname = constrName;
   12060             : 
   12061         298 :             newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
   12062         298 :             newcon->name = constrName;
   12063         298 :             newcon->contype = CONSTR_FOREIGN;
   12064         298 :             newcon->refrelid = con->confrelid;
   12065         298 :             newcon->refindid = con->conindid;
   12066         298 :             newcon->conid = con->oid;
   12067         298 :             newcon->qual = (Node *) fkconstraint;
   12068             : 
   12069             :             /* Find or create work queue entry for this table */
   12070         298 :             tab = ATGetQueueEntry(wqueue, rel);
   12071         298 :             tab->constraints = lappend(tab->constraints, newcon);
   12072             : 
   12073             :             /*
   12074             :              * We disallow creating invalid foreign keys to or from
   12075             :              * partitioned tables, so ignoring the recursion bit is okay.
   12076             :              */
   12077             :         }
   12078         120 :         else if (con->contype == CONSTRAINT_CHECK)
   12079             :         {
   12080         120 :             List       *children = NIL;
   12081             :             ListCell   *child;
   12082             :             NewConstraint *newcon;
   12083             :             Datum       val;
   12084             :             char       *conbin;
   12085             : 
   12086             :             /*
   12087             :              * If we're recursing, the parent has already done this, so skip
   12088             :              * it.  Also, if the constraint is a NO INHERIT constraint, we
   12089             :              * shouldn't try to look for it in the children.
   12090             :              */
   12091         120 :             if (!recursing && !con->connoinherit)
   12092          66 :                 children = find_all_inheritors(RelationGetRelid(rel),
   12093             :                                                lockmode, NULL);
   12094             : 
   12095             :             /*
   12096             :              * For CHECK constraints, we must ensure that we only mark the
   12097             :              * constraint as validated on the parent if it's already validated
   12098             :              * on the children.
   12099             :              *
   12100             :              * We recurse before validating on the parent, to reduce risk of
   12101             :              * deadlocks.
   12102             :              */
   12103         234 :             foreach(child, children)
   12104             :             {
   12105         114 :                 Oid         childoid = lfirst_oid(child);
   12106             :                 Relation    childrel;
   12107             : 
   12108         114 :                 if (childoid == RelationGetRelid(rel))
   12109          66 :                     continue;
   12110             : 
   12111             :                 /*
   12112             :                  * If we are told not to recurse, there had better not be any
   12113             :                  * child tables, because we can't mark the constraint on the
   12114             :                  * parent valid unless it is valid for all child tables.
   12115             :                  */
   12116          48 :                 if (!recurse)
   12117           0 :                     ereport(ERROR,
   12118             :                             (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
   12119             :                              errmsg("constraint must be validated on child tables too")));
   12120             : 
   12121             :                 /* find_all_inheritors already got lock */
   12122          48 :                 childrel = table_open(childoid, NoLock);
   12123             : 
   12124          48 :                 ATExecValidateConstraint(wqueue, childrel, constrName, false,
   12125             :                                          true, lockmode);
   12126          48 :                 table_close(childrel, NoLock);
   12127             :             }
   12128             : 
   12129             :             /* Queue validation for phase 3 */
   12130         120 :             newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
   12131         120 :             newcon->name = constrName;
   12132         120 :             newcon->contype = CONSTR_CHECK;
   12133         120 :             newcon->refrelid = InvalidOid;
   12134         120 :             newcon->refindid = InvalidOid;
   12135         120 :             newcon->conid = con->oid;
   12136             : 
   12137         120 :             val = SysCacheGetAttrNotNull(CONSTROID, tuple,
   12138             :                                          Anum_pg_constraint_conbin);
   12139         120 :             conbin = TextDatumGetCString(val);
   12140         120 :             newcon->qual = (Node *) stringToNode(conbin);
   12141             : 
   12142             :             /* Find or create work queue entry for this table */
   12143         120 :             tab = ATGetQueueEntry(wqueue, rel);
   12144         120 :             tab->constraints = lappend(tab->constraints, newcon);
   12145             : 
   12146             :             /*
   12147             :              * Invalidate relcache so that others see the new validated
   12148             :              * constraint.
   12149             :              */
   12150         120 :             CacheInvalidateRelcache(rel);
   12151             :         }
   12152             : 
   12153             :         /*
   12154             :          * Now update the catalog, while we have the door open.
   12155             :          */
   12156         418 :         copyTuple = heap_copytuple(tuple);
   12157         418 :         copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
   12158         418 :         copy_con->convalidated = true;
   12159         418 :         CatalogTupleUpdate(conrel, &copyTuple->t_self, copyTuple);
   12160             : 
   12161         418 :         InvokeObjectPostAlterHook(ConstraintRelationId, con->oid, 0);
   12162             : 
   12163         418 :         heap_freetuple(copyTuple);
   12164             : 
   12165         418 :         ObjectAddressSet(address, ConstraintRelationId, con->oid);
   12166             :     }
   12167             :     else
   12168          18 :         address = InvalidObjectAddress; /* already validated */
   12169             : 
   12170         436 :     systable_endscan(scan);
   12171             : 
   12172         436 :     table_close(conrel, RowExclusiveLock);
   12173             : 
   12174         436 :     return address;
   12175             : }
   12176             : 
   12177             : 
   12178             : /*
   12179             :  * transformColumnNameList - transform list of column names
   12180             :  *
   12181             :  * Lookup each name and return its attnum and, optionally, type OID
   12182             :  *
   12183             :  * Note: the name of this function suggests that it's general-purpose,
   12184             :  * but actually it's only used to look up names appearing in foreign-key
   12185             :  * clauses.  The error messages would need work to use it in other cases,
   12186             :  * and perhaps the validity checks as well.
   12187             :  */
   12188             : static int
   12189        6160 : transformColumnNameList(Oid relId, List *colList,
   12190             :                         int16 *attnums, Oid *atttypids)
   12191             : {
   12192             :     ListCell   *l;
   12193             :     int         attnum;
   12194             : 
   12195        6160 :     attnum = 0;
   12196       11262 :     foreach(l, colList)
   12197             :     {
   12198        5168 :         char       *attname = strVal(lfirst(l));
   12199             :         HeapTuple   atttuple;
   12200             :         Form_pg_attribute attform;
   12201             : 
   12202        5168 :         atttuple = SearchSysCacheAttName(relId, attname);
   12203        5168 :         if (!HeapTupleIsValid(atttuple))
   12204          54 :             ereport(ERROR,
   12205             :                     (errcode(ERRCODE_UNDEFINED_COLUMN),
   12206             :                      errmsg("column \"%s\" referenced in foreign key constraint does not exist",
   12207             :                             attname)));
   12208        5114 :         attform = (Form_pg_attribute) GETSTRUCT(atttuple);
   12209        5114 :         if (attform->attnum < 0)
   12210          12 :             ereport(ERROR,
   12211             :                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   12212             :                      errmsg("system columns cannot be used in foreign keys")));
   12213        5102 :         if (attnum >= INDEX_MAX_KEYS)
   12214           0 :             ereport(ERROR,
   12215             :                     (errcode(ERRCODE_TOO_MANY_COLUMNS),
   12216             :                      errmsg("cannot have more than %d keys in a foreign key",
   12217             :                             INDEX_MAX_KEYS)));
   12218        5102 :         attnums[attnum] = attform->attnum;
   12219        5102 :         if (atttypids != NULL)
   12220        5072 :             atttypids[attnum] = attform->atttypid;
   12221        5102 :         ReleaseSysCache(atttuple);
   12222        5102 :         attnum++;
   12223             :     }
   12224             : 
   12225        6094 :     return attnum;
   12226             : }
   12227             : 
   12228             : /*
   12229             :  * transformFkeyGetPrimaryKey -
   12230             :  *
   12231             :  *  Look up the names, attnums, and types of the primary key attributes
   12232             :  *  for the pkrel.  Also return the index OID and index opclasses of the
   12233             :  *  index supporting the primary key.  Also return whether the index has
   12234             :  *  WITHOUT OVERLAPS.
   12235             :  *
   12236             :  *  All parameters except pkrel are output parameters.  Also, the function
   12237             :  *  return value is the number of attributes in the primary key.
   12238             :  *
   12239             :  *  Used when the column list in the REFERENCES specification is omitted.
   12240             :  */
   12241             : static int
   12242        1070 : transformFkeyGetPrimaryKey(Relation pkrel, Oid *indexOid,
   12243             :                            List **attnamelist,
   12244             :                            int16 *attnums, Oid *atttypids,
   12245             :                            Oid *opclasses, bool *pk_has_without_overlaps)
   12246             : {
   12247             :     List       *indexoidlist;
   12248             :     ListCell   *indexoidscan;
   12249        1070 :     HeapTuple   indexTuple = NULL;
   12250        1070 :     Form_pg_index indexStruct = NULL;
   12251             :     Datum       indclassDatum;
   12252             :     oidvector  *indclass;
   12253             :     int         i;
   12254             : 
   12255             :     /*
   12256             :      * Get the list of index OIDs for the table from the relcache, and look up
   12257             :      * each one in the pg_index syscache until we find one marked primary key
   12258             :      * (hopefully there isn't more than one such).  Insist it's valid, too.
   12259             :      */
   12260        1070 :     *indexOid = InvalidOid;
   12261             : 
   12262        1070 :     indexoidlist = RelationGetIndexList(pkrel);
   12263             : 
   12264        1076 :     foreach(indexoidscan, indexoidlist)
   12265             :     {
   12266        1076 :         Oid         indexoid = lfirst_oid(indexoidscan);
   12267             : 
   12268        1076 :         indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexoid));
   12269        1076 :         if (!HeapTupleIsValid(indexTuple))
   12270           0 :             elog(ERROR, "cache lookup failed for index %u", indexoid);
   12271        1076 :         indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
   12272        1076 :         if (indexStruct->indisprimary && indexStruct->indisvalid)
   12273             :         {
   12274             :             /*
   12275             :              * Refuse to use a deferrable primary key.  This is per SQL spec,
   12276             :              * and there would be a lot of interesting semantic problems if we
   12277             :              * tried to allow it.
   12278             :              */
   12279        1070 :             if (!indexStruct->indimmediate)
   12280           0 :                 ereport(ERROR,
   12281             :                         (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
   12282             :                          errmsg("cannot use a deferrable primary key for referenced table \"%s\"",
   12283             :                                 RelationGetRelationName(pkrel))));
   12284             : 
   12285        1070 :             *indexOid = indexoid;
   12286        1070 :             break;
   12287             :         }
   12288           6 :         ReleaseSysCache(indexTuple);
   12289             :     }
   12290             : 
   12291        1070 :     list_free(indexoidlist);
   12292             : 
   12293             :     /*
   12294             :      * Check that we found it
   12295             :      */
   12296        1070 :     if (!OidIsValid(*indexOid))
   12297           0 :         ereport(ERROR,
   12298             :                 (errcode(ERRCODE_UNDEFINED_OBJECT),
   12299             :                  errmsg("there is no primary key for referenced table \"%s\"",
   12300             :                         RelationGetRelationName(pkrel))));
   12301             : 
   12302             :     /* Must get indclass the hard way */
   12303        1070 :     indclassDatum = SysCacheGetAttrNotNull(INDEXRELID, indexTuple,
   12304             :                                            Anum_pg_index_indclass);
   12305        1070 :     indclass = (oidvector *) DatumGetPointer(indclassDatum);
   12306             : 
   12307             :     /*
   12308             :      * Now build the list of PK attributes from the indkey definition (we
   12309             :      * assume a primary key cannot have expressional elements)
   12310             :      */
   12311        1070 :     *attnamelist = NIL;
   12312        2504 :     for (i = 0; i < indexStruct->indnkeyatts; i++)
   12313             :     {
   12314        1434 :         int         pkattno = indexStruct->indkey.values[i];
   12315             : 
   12316        1434 :         attnums[i] = pkattno;
   12317        1434 :         atttypids[i] = attnumTypeId(pkrel, pkattno);
   12318        1434 :         opclasses[i] = indclass->values[i];
   12319        1434 :         *attnamelist = lappend(*attnamelist,
   12320        1434 :                                makeString(pstrdup(NameStr(*attnumAttName(pkrel, pkattno)))));
   12321             :     }
   12322             : 
   12323        1070 :     *pk_has_without_overlaps = indexStruct->indisexclusion;
   12324             : 
   12325        1070 :     ReleaseSysCache(indexTuple);
   12326             : 
   12327        1070 :     return i;
   12328             : }
   12329             : 
   12330             : /*
   12331             :  * transformFkeyCheckAttrs -
   12332             :  *
   12333             :  *  Validate that the 'attnums' columns in the 'pkrel' relation are valid to
   12334             :  *  reference as part of a foreign key constraint.
   12335             :  *
   12336             :  *  Returns the OID of the unique index supporting the constraint and
   12337             :  *  populates the caller-provided 'opclasses' array with the opclasses
   12338             :  *  associated with the index columns.  Also sets whether the index
   12339             :  *  uses WITHOUT OVERLAPS.
   12340             :  *
   12341             :  *  Raises an ERROR on validation failure.
   12342             :  */
   12343             : static Oid
   12344        1260 : transformFkeyCheckAttrs(Relation pkrel,
   12345             :                         int numattrs, int16 *attnums,
   12346             :                         bool with_period, Oid *opclasses,
   12347             :                         bool *pk_has_without_overlaps)
   12348             : {
   12349        1260 :     Oid         indexoid = InvalidOid;
   12350        1260 :     bool        found = false;
   12351        1260 :     bool        found_deferrable = false;
   12352             :     List       *indexoidlist;
   12353             :     ListCell   *indexoidscan;
   12354             :     int         i,
   12355             :                 j;
   12356             : 
   12357             :     /*
   12358             :      * Reject duplicate appearances of columns in the referenced-columns list.
   12359             :      * Such a case is forbidden by the SQL standard, and even if we thought it
   12360             :      * useful to allow it, there would be ambiguity about how to match the
   12361             :      * list to unique indexes (in particular, it'd be unclear which index
   12362             :      * opclass goes with which FK column).
   12363             :      */
   12364        2950 :     for (i = 0; i < numattrs; i++)
   12365             :     {
   12366        2238 :         for (j = i + 1; j < numattrs; j++)
   12367             :         {
   12368         548 :             if (attnums[i] == attnums[j])
   12369          24 :                 ereport(ERROR,
   12370             :                         (errcode(ERRCODE_INVALID_FOREIGN_KEY),
   12371             :                          errmsg("foreign key referenced-columns list must not contain duplicates")));
   12372             :         }
   12373             :     }
   12374             : 
   12375             :     /*
   12376             :      * Get the list of index OIDs for the table from the relcache, and look up
   12377             :      * each one in the pg_index syscache, and match unique indexes to the list
   12378             :      * of attnums we are given.
   12379             :      */
   12380        1236 :     indexoidlist = RelationGetIndexList(pkrel);
   12381             : 
   12382        1416 :     foreach(indexoidscan, indexoidlist)
   12383             :     {
   12384             :         HeapTuple   indexTuple;
   12385             :         Form_pg_index indexStruct;
   12386             : 
   12387        1404 :         indexoid = lfirst_oid(indexoidscan);
   12388        1404 :         indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexoid));
   12389        1404 :         if (!HeapTupleIsValid(indexTuple))
   12390           0 :             elog(ERROR, "cache lookup failed for index %u", indexoid);
   12391        1404 :         indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
   12392             : 
   12393             :         /*
   12394             :          * Must have the right number of columns; must be unique (or if
   12395             :          * temporal then exclusion instead) and not a partial index; forget it
   12396             :          * if there are any expressions, too. Invalid indexes are out as well.
   12397             :          */
   12398        2700 :         if (indexStruct->indnkeyatts == numattrs &&
   12399        1296 :             (with_period ? indexStruct->indisexclusion : indexStruct->indisunique) &&
   12400        2564 :             indexStruct->indisvalid &&
   12401        2564 :             heap_attisnull(indexTuple, Anum_pg_index_indpred, NULL) &&
   12402        1282 :             heap_attisnull(indexTuple, Anum_pg_index_indexprs, NULL))
   12403             :         {
   12404             :             Datum       indclassDatum;
   12405             :             oidvector  *indclass;
   12406             : 
   12407             :             /* Must get indclass the hard way */
   12408        1282 :             indclassDatum = SysCacheGetAttrNotNull(INDEXRELID, indexTuple,
   12409             :                                                    Anum_pg_index_indclass);
   12410        1282 :             indclass = (oidvector *) DatumGetPointer(indclassDatum);
   12411             : 
   12412             :             /*
   12413             :              * The given attnum list may match the index columns in any order.
   12414             :              * Check for a match, and extract the appropriate opclasses while
   12415             :              * we're at it.
   12416             :              *
   12417             :              * We know that attnums[] is duplicate-free per the test at the
   12418             :              * start of this function, and we checked above that the number of
   12419             :              * index columns agrees, so if we find a match for each attnums[]
   12420             :              * entry then we must have a one-to-one match in some order.
   12421             :              */
   12422        2960 :             for (i = 0; i < numattrs; i++)
   12423             :             {
   12424        1736 :                 found = false;
   12425        2318 :                 for (j = 0; j < numattrs; j++)
   12426             :                 {
   12427        2260 :                     if (attnums[i] == indexStruct->indkey.values[j])
   12428             :                     {
   12429        1678 :                         opclasses[i] = indclass->values[j];
   12430        1678 :                         found = true;
   12431        1678 :                         break;
   12432             :                     }
   12433             :                 }
   12434        1736 :                 if (!found)
   12435          58 :                     break;
   12436             :             }
   12437             :             /* The last attribute in the index must be the PERIOD FK part */
   12438        1282 :             if (found && with_period)
   12439             :             {
   12440         130 :                 int16       periodattnum = attnums[numattrs - 1];
   12441             : 
   12442         130 :                 found = (periodattnum == indexStruct->indkey.values[numattrs - 1]);
   12443             :             }
   12444             : 
   12445             :             /*
   12446             :              * Refuse to use a deferrable unique/primary key.  This is per SQL
   12447             :              * spec, and there would be a lot of interesting semantic problems
   12448             :              * if we tried to allow it.
   12449             :              */
   12450        1282 :             if (found && !indexStruct->indimmediate)
   12451             :             {
   12452             :                 /*
   12453             :                  * Remember that we found an otherwise matching index, so that
   12454             :                  * we can generate a more appropriate error message.
   12455             :                  */
   12456           0 :                 found_deferrable = true;
   12457           0 :                 found = false;
   12458             :             }
   12459             : 
   12460             :             /* We need to know whether the index has WITHOUT OVERLAPS */
   12461        1282 :             if (found)
   12462        1224 :                 *pk_has_without_overlaps = indexStruct->indisexclusion;
   12463             :         }
   12464        1404 :         ReleaseSysCache(indexTuple);
   12465        1404 :         if (found)
   12466        1224 :             break;
   12467             :     }
   12468             : 
   12469        1236 :     if (!found)
   12470             :     {
   12471          12 :         if (found_deferrable)
   12472           0 :             ereport(ERROR,
   12473             :                     (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
   12474             :                      errmsg("cannot use a deferrable unique constraint for referenced table \"%s\"",
   12475             :                             RelationGetRelationName(pkrel))));
   12476             :         else
   12477          12 :             ereport(ERROR,
   12478             :                     (errcode(ERRCODE_INVALID_FOREIGN_KEY),
   12479             :                      errmsg("there is no unique constraint matching given keys for referenced table \"%s\"",
   12480             :                             RelationGetRelationName(pkrel))));
   12481             :     }
   12482             : 
   12483        1224 :     list_free(indexoidlist);
   12484             : 
   12485        1224 :     return indexoid;
   12486             : }
   12487             : 
   12488             : /*
   12489             :  * findFkeyCast -
   12490             :  *
   12491             :  *  Wrapper around find_coercion_pathway() for ATAddForeignKeyConstraint().
   12492             :  *  Caller has equal regard for binary coercibility and for an exact match.
   12493             : */
   12494             : static CoercionPathType
   12495          12 : findFkeyCast(Oid targetTypeId, Oid sourceTypeId, Oid *funcid)
   12496             : {
   12497             :     CoercionPathType ret;
   12498             : 
   12499          12 :     if (targetTypeId == sourceTypeId)
   12500             :     {
   12501          12 :         ret = COERCION_PATH_RELABELTYPE;
   12502          12 :         *funcid = InvalidOid;
   12503             :     }
   12504             :     else
   12505             :     {
   12506           0 :         ret = find_coercion_pathway(targetTypeId, sourceTypeId,
   12507             :                                     COERCION_IMPLICIT, funcid);
   12508           0 :         if (ret == COERCION_PATH_NONE)
   12509             :             /* A previously-relied-upon cast is now gone. */
   12510           0 :             elog(ERROR, "could not find cast from %u to %u",
   12511             :                  sourceTypeId, targetTypeId);
   12512             :     }
   12513             : 
   12514          12 :     return ret;
   12515             : }
   12516             : 
   12517             : /*
   12518             :  * Permissions checks on the referenced table for ADD FOREIGN KEY
   12519             :  *
   12520             :  * Note: we have already checked that the user owns the referencing table,
   12521             :  * else we'd have failed much earlier; no additional checks are needed for it.
   12522             :  */
   12523             : static void
   12524        2258 : checkFkeyPermissions(Relation rel, int16 *attnums, int natts)
   12525             : {
   12526        2258 :     Oid         roleid = GetUserId();
   12527             :     AclResult   aclresult;
   12528             :     int         i;
   12529             : 
   12530             :     /* Okay if we have relation-level REFERENCES permission */
   12531        2258 :     aclresult = pg_class_aclcheck(RelationGetRelid(rel), roleid,
   12532             :                                   ACL_REFERENCES);
   12533        2258 :     if (aclresult == ACLCHECK_OK)
   12534        2258 :         return;
   12535             :     /* Else we must have REFERENCES on each column */
   12536           0 :     for (i = 0; i < natts; i++)
   12537             :     {
   12538           0 :         aclresult = pg_attribute_aclcheck(RelationGetRelid(rel), attnums[i],
   12539             :                                           roleid, ACL_REFERENCES);
   12540           0 :         if (aclresult != ACLCHECK_OK)
   12541           0 :             aclcheck_error(aclresult, get_relkind_objtype(rel->rd_rel->relkind),
   12542           0 :                            RelationGetRelationName(rel));
   12543             :     }
   12544             : }
   12545             : 
   12546             : /*
   12547             :  * Scan the existing rows in a table to verify they meet a proposed FK
   12548             :  * constraint.
   12549             :  *
   12550             :  * Caller must have opened and locked both relations appropriately.
   12551             :  */
   12552             : static void
   12553        1084 : validateForeignKeyConstraint(char *conname,
   12554             :                              Relation rel,
   12555             :                              Relation pkrel,
   12556             :                              Oid pkindOid,
   12557             :                              Oid constraintOid,
   12558             :                              bool hasperiod)
   12559             : {
   12560             :     TupleTableSlot *slot;
   12561             :     TableScanDesc scan;
   12562        1084 :     Trigger     trig = {0};
   12563             :     Snapshot    snapshot;
   12564             :     MemoryContext oldcxt;
   12565             :     MemoryContext perTupCxt;
   12566             : 
   12567        1084 :     ereport(DEBUG1,
   12568             :             (errmsg_internal("validating foreign key constraint \"%s\"", conname)));
   12569             : 
   12570             :     /*
   12571             :      * Build a trigger call structure; we'll need it either way.
   12572             :      */
   12573        1084 :     trig.tgoid = InvalidOid;
   12574        1084 :     trig.tgname = conname;
   12575        1084 :     trig.tgenabled = TRIGGER_FIRES_ON_ORIGIN;
   12576        1084 :     trig.tgisinternal = true;
   12577        1084 :     trig.tgconstrrelid = RelationGetRelid(pkrel);
   12578        1084 :     trig.tgconstrindid = pkindOid;
   12579        1084 :     trig.tgconstraint = constraintOid;
   12580        1084 :     trig.tgdeferrable = false;
   12581        1084 :     trig.tginitdeferred = false;
   12582             :     /* we needn't fill in remaining fields */
   12583             : 
   12584             :     /*
   12585             :      * See if we can do it with a single LEFT JOIN query.  A false result
   12586             :      * indicates we must proceed with the fire-the-trigger method. We can't do
   12587             :      * a LEFT JOIN for temporal FKs yet, but we can once we support temporal
   12588             :      * left joins.
   12589             :      */
   12590        1084 :     if (!hasperiod && RI_Initial_Check(&trig, rel, pkrel))
   12591         876 :         return;
   12592             : 
   12593             :     /*
   12594             :      * Scan through each tuple, calling RI_FKey_check_ins (insert trigger) as
   12595             :      * if that tuple had just been inserted.  If any of those fail, it should
   12596             :      * ereport(ERROR) and that's that.
   12597             :      */
   12598         146 :     snapshot = RegisterSnapshot(GetLatestSnapshot());
   12599         146 :     slot = table_slot_create(rel, NULL);
   12600         146 :     scan = table_beginscan(rel, snapshot, 0, NULL);
   12601             : 
   12602         146 :     perTupCxt = AllocSetContextCreate(CurrentMemoryContext,
   12603             :                                       "validateForeignKeyConstraint",
   12604             :                                       ALLOCSET_SMALL_SIZES);
   12605         146 :     oldcxt = MemoryContextSwitchTo(perTupCxt);
   12606             : 
   12607         230 :     while (table_scan_getnextslot(scan, ForwardScanDirection, slot))
   12608             :     {
   12609         102 :         LOCAL_FCINFO(fcinfo, 0);
   12610         102 :         TriggerData trigdata = {0};
   12611             : 
   12612         102 :         CHECK_FOR_INTERRUPTS();
   12613             : 
   12614             :         /*
   12615             :          * Make a call to the trigger function
   12616             :          *
   12617             :          * No parameters are passed, but we do set a context
   12618             :          */
   12619         510 :         MemSet(fcinfo, 0, SizeForFunctionCallInfo(0));
   12620             : 
   12621             :         /*
   12622             :          * We assume RI_FKey_check_ins won't look at flinfo...
   12623             :          */
   12624         102 :         trigdata.type = T_TriggerData;
   12625         102 :         trigdata.tg_event = TRIGGER_EVENT_INSERT | TRIGGER_EVENT_ROW;
   12626         102 :         trigdata.tg_relation = rel;
   12627         102 :         trigdata.tg_trigtuple = ExecFetchSlotHeapTuple(slot, false, NULL);
   12628         102 :         trigdata.tg_trigslot = slot;
   12629         102 :         trigdata.tg_trigger = &trig;
   12630             : 
   12631         102 :         fcinfo->context = (Node *) &trigdata;
   12632             : 
   12633         102 :         RI_FKey_check_ins(fcinfo);
   12634             : 
   12635          84 :         MemoryContextReset(perTupCxt);
   12636             :     }
   12637             : 
   12638         128 :     MemoryContextSwitchTo(oldcxt);
   12639         128 :     MemoryContextDelete(perTupCxt);
   12640         128 :     table_endscan(scan);
   12641         128 :     UnregisterSnapshot(snapshot);
   12642         128 :     ExecDropSingleTupleTableSlot(slot);
   12643             : }
   12644             : 
   12645             : /*
   12646             :  * CreateFKCheckTrigger
   12647             :  *      Creates the insert (on_insert=true) or update "check" trigger that
   12648             :  *      implements a given foreign key
   12649             :  *
   12650             :  * Returns the OID of the so created trigger.
   12651             :  */
   12652             : static Oid
   12653        5492 : CreateFKCheckTrigger(Oid myRelOid, Oid refRelOid, Constraint *fkconstraint,
   12654             :                      Oid constraintOid, Oid indexOid, Oid parentTrigOid,
   12655             :                      bool on_insert)
   12656             : {
   12657             :     ObjectAddress trigAddress;
   12658             :     CreateTrigStmt *fk_trigger;
   12659             : 
   12660             :     /*
   12661             :      * Note: for a self-referential FK (referencing and referenced tables are
   12662             :      * the same), it is important that the ON UPDATE action fires before the
   12663             :      * CHECK action, since both triggers will fire on the same row during an
   12664             :      * UPDATE event; otherwise the CHECK trigger will be checking a non-final
   12665             :      * state of the row.  Triggers fire in name order, so we ensure this by
   12666             :      * using names like "RI_ConstraintTrigger_a_NNNN" for the action triggers
   12667             :      * and "RI_ConstraintTrigger_c_NNNN" for the check triggers.
   12668             :      */
   12669        5492 :     fk_trigger = makeNode(CreateTrigStmt);
   12670        5492 :     fk_trigger->replace = false;
   12671        5492 :     fk_trigger->isconstraint = true;
   12672        5492 :     fk_trigger->trigname = "RI_ConstraintTrigger_c";
   12673        5492 :     fk_trigger->relation = NULL;
   12674             : 
   12675             :     /* Either ON INSERT or ON UPDATE */
   12676        5492 :     if (on_insert)
   12677             :     {
   12678        2746 :         fk_trigger->funcname = SystemFuncName("RI_FKey_check_ins");
   12679        2746 :         fk_trigger->events = TRIGGER_TYPE_INSERT;
   12680             :     }
   12681             :     else
   12682             :     {
   12683        2746 :         fk_trigger->funcname = SystemFuncName("RI_FKey_check_upd");
   12684        2746 :         fk_trigger->events = TRIGGER_TYPE_UPDATE;
   12685             :     }
   12686             : 
   12687        5492 :     fk_trigger->args = NIL;
   12688        5492 :     fk_trigger->row = true;
   12689        5492 :     fk_trigger->timing = TRIGGER_TYPE_AFTER;
   12690        5492 :     fk_trigger->columns = NIL;
   12691        5492 :     fk_trigger->whenClause = NULL;
   12692        5492 :     fk_trigger->transitionRels = NIL;
   12693        5492 :     fk_trigger->deferrable = fkconstraint->deferrable;
   12694        5492 :     fk_trigger->initdeferred = fkconstraint->initdeferred;
   12695        5492 :     fk_trigger->constrrel = NULL;
   12696             : 
   12697        5492 :     trigAddress = CreateTrigger(fk_trigger, NULL, myRelOid, refRelOid,
   12698             :                                 constraintOid, indexOid, InvalidOid,
   12699             :                                 parentTrigOid, NULL, true, false);
   12700             : 
   12701             :     /* Make changes-so-far visible */
   12702        5492 :     CommandCounterIncrement();
   12703             : 
   12704        5492 :     return trigAddress.objectId;
   12705             : }
   12706             : 
   12707             : /*
   12708             :  * createForeignKeyActionTriggers
   12709             :  *      Create the referenced-side "action" triggers that implement a foreign
   12710             :  *      key.
   12711             :  *
   12712             :  * Returns the OIDs of the so created triggers in *deleteTrigOid and
   12713             :  * *updateTrigOid.
   12714             :  */
   12715             : static void
   12716        2762 : createForeignKeyActionTriggers(Relation rel, Oid refRelOid, Constraint *fkconstraint,
   12717             :                                Oid constraintOid, Oid indexOid,
   12718             :                                Oid parentDelTrigger, Oid parentUpdTrigger,
   12719             :                                Oid *deleteTrigOid, Oid *updateTrigOid)
   12720             : {
   12721             :     CreateTrigStmt *fk_trigger;
   12722             :     ObjectAddress trigAddress;
   12723             : 
   12724             :     /*
   12725             :      * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
   12726             :      * DELETE action on the referenced table.
   12727             :      */
   12728        2762 :     fk_trigger = makeNode(CreateTrigStmt);
   12729        2762 :     fk_trigger->replace = false;
   12730        2762 :     fk_trigger->isconstraint = true;
   12731        2762 :     fk_trigger->trigname = "RI_ConstraintTrigger_a";
   12732        2762 :     fk_trigger->relation = NULL;
   12733        2762 :     fk_trigger->args = NIL;
   12734        2762 :     fk_trigger->row = true;
   12735        2762 :     fk_trigger->timing = TRIGGER_TYPE_AFTER;
   12736        2762 :     fk_trigger->events = TRIGGER_TYPE_DELETE;
   12737        2762 :     fk_trigger->columns = NIL;
   12738        2762 :     fk_trigger->whenClause = NULL;
   12739        2762 :     fk_trigger->transitionRels = NIL;
   12740        2762 :     fk_trigger->constrrel = NULL;
   12741             : 
   12742        2762 :     switch (fkconstraint->fk_del_action)
   12743             :     {
   12744        2100 :         case FKCONSTR_ACTION_NOACTION:
   12745        2100 :             fk_trigger->deferrable = fkconstraint->deferrable;
   12746        2100 :             fk_trigger->initdeferred = fkconstraint->initdeferred;
   12747        2100 :             fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_del");
   12748        2100 :             break;
   12749          64 :         case FKCONSTR_ACTION_RESTRICT:
   12750          64 :             fk_trigger->deferrable = false;
   12751          64 :             fk_trigger->initdeferred = false;
   12752          64 :             fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_del");
   12753          64 :             break;
   12754         440 :         case FKCONSTR_ACTION_CASCADE:
   12755         440 :             fk_trigger->deferrable = false;
   12756         440 :             fk_trigger->initdeferred = false;
   12757         440 :             fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_del");
   12758         440 :             break;
   12759          98 :         case FKCONSTR_ACTION_SETNULL:
   12760          98 :             fk_trigger->deferrable = false;
   12761          98 :             fk_trigger->initdeferred = false;
   12762          98 :             fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_del");
   12763          98 :             break;
   12764          60 :         case FKCONSTR_ACTION_SETDEFAULT:
   12765          60 :             fk_trigger->deferrable = false;
   12766          60 :             fk_trigger->initdeferred = false;
   12767          60 :             fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_del");
   12768          60 :             break;
   12769           0 :         default:
   12770           0 :             elog(ERROR, "unrecognized FK action type: %d",
   12771             :                  (int) fkconstraint->fk_del_action);
   12772             :             break;
   12773             :     }
   12774             : 
   12775        2762 :     trigAddress = CreateTrigger(fk_trigger, NULL, refRelOid,
   12776             :                                 RelationGetRelid(rel),
   12777             :                                 constraintOid, indexOid, InvalidOid,
   12778             :                                 parentDelTrigger, NULL, true, false);
   12779        2762 :     if (deleteTrigOid)
   12780        2696 :         *deleteTrigOid = trigAddress.objectId;
   12781             : 
   12782             :     /* Make changes-so-far visible */
   12783        2762 :     CommandCounterIncrement();
   12784             : 
   12785             :     /*
   12786             :      * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
   12787             :      * UPDATE action on the referenced table.
   12788             :      */
   12789        2762 :     fk_trigger = makeNode(CreateTrigStmt);
   12790        2762 :     fk_trigger->replace = false;
   12791        2762 :     fk_trigger->isconstraint = true;
   12792        2762 :     fk_trigger->trigname = "RI_ConstraintTrigger_a";
   12793        2762 :     fk_trigger->relation = NULL;
   12794        2762 :     fk_trigger->args = NIL;
   12795        2762 :     fk_trigger->row = true;
   12796        2762 :     fk_trigger->timing = TRIGGER_TYPE_AFTER;
   12797        2762 :     fk_trigger->events = TRIGGER_TYPE_UPDATE;
   12798        2762 :     fk_trigger->columns = NIL;
   12799        2762 :     fk_trigger->whenClause = NULL;
   12800        2762 :     fk_trigger->transitionRels = NIL;
   12801        2762 :     fk_trigger->constrrel = NULL;
   12802             : 
   12803        2762 :     switch (fkconstraint->fk_upd_action)
   12804             :     {
   12805        2328 :         case FKCONSTR_ACTION_NOACTION:
   12806        2328 :             fk_trigger->deferrable = fkconstraint->deferrable;
   12807        2328 :             fk_trigger->initdeferred = fkconstraint->initdeferred;
   12808        2328 :             fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_upd");
   12809        2328 :             break;
   12810          42 :         case FKCONSTR_ACTION_RESTRICT:
   12811          42 :             fk_trigger->deferrable = false;
   12812          42 :             fk_trigger->initdeferred = false;
   12813          42 :             fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_upd");
   12814          42 :             break;
   12815         288 :         case FKCONSTR_ACTION_CASCADE:
   12816         288 :             fk_trigger->deferrable = false;
   12817         288 :             fk_trigger->initdeferred = false;
   12818         288 :             fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_upd");
   12819         288 :             break;
   12820          62 :         case FKCONSTR_ACTION_SETNULL:
   12821          62 :             fk_trigger->deferrable = false;
   12822          62 :             fk_trigger->initdeferred = false;
   12823          62 :             fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_upd");
   12824          62 :             break;
   12825          42 :         case FKCONSTR_ACTION_SETDEFAULT:
   12826          42 :             fk_trigger->deferrable = false;
   12827          42 :             fk_trigger->initdeferred = false;
   12828          42 :             fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_upd");
   12829          42 :             break;
   12830           0 :         default:
   12831           0 :             elog(ERROR, "unrecognized FK action type: %d",
   12832             :                  (int) fkconstraint->fk_upd_action);
   12833             :             break;
   12834             :     }
   12835             : 
   12836        2762 :     trigAddress = CreateTrigger(fk_trigger, NULL, refRelOid,
   12837             :                                 RelationGetRelid(rel),
   12838             :                                 constraintOid, indexOid, InvalidOid,
   12839             :                                 parentUpdTrigger, NULL, true, false);
   12840        2762 :     if (updateTrigOid)
   12841        2696 :         *updateTrigOid = trigAddress.objectId;
   12842        2762 : }
   12843             : 
   12844             : /*
   12845             :  * createForeignKeyCheckTriggers
   12846             :  *      Create the referencing-side "check" triggers that implement a foreign
   12847             :  *      key.
   12848             :  *
   12849             :  * Returns the OIDs of the so created triggers in *insertTrigOid and
   12850             :  * *updateTrigOid.
   12851             :  */
   12852             : static void
   12853        2746 : createForeignKeyCheckTriggers(Oid myRelOid, Oid refRelOid,
   12854             :                               Constraint *fkconstraint, Oid constraintOid,
   12855             :                               Oid indexOid,
   12856             :                               Oid parentInsTrigger, Oid parentUpdTrigger,
   12857             :                               Oid *insertTrigOid, Oid *updateTrigOid)
   12858             : {
   12859        2746 :     *insertTrigOid = CreateFKCheckTrigger(myRelOid, refRelOid, fkconstraint,
   12860             :                                           constraintOid, indexOid,
   12861             :                                           parentInsTrigger, true);
   12862        2746 :     *updateTrigOid = CreateFKCheckTrigger(myRelOid, refRelOid, fkconstraint,
   12863             :                                           constraintOid, indexOid,
   12864             :                                           parentUpdTrigger, false);
   12865        2746 : }
   12866             : 
   12867             : /*
   12868             :  * ALTER TABLE DROP CONSTRAINT
   12869             :  *
   12870             :  * Like DROP COLUMN, we can't use the normal ALTER TABLE recursion mechanism.
   12871             :  */
   12872             : static void
   12873         962 : ATExecDropConstraint(Relation rel, const char *constrName,
   12874             :                      DropBehavior behavior, bool recurse,
   12875             :                      bool missing_ok, LOCKMODE lockmode)
   12876             : {
   12877             :     Relation    conrel;
   12878             :     SysScanDesc scan;
   12879             :     ScanKeyData skey[3];
   12880             :     HeapTuple   tuple;
   12881         962 :     bool        found = false;
   12882             : 
   12883         962 :     conrel = table_open(ConstraintRelationId, RowExclusiveLock);
   12884             : 
   12885             :     /*
   12886             :      * Find and drop the target constraint
   12887             :      */
   12888         962 :     ScanKeyInit(&skey[0],
   12889             :                 Anum_pg_constraint_conrelid,
   12890             :                 BTEqualStrategyNumber, F_OIDEQ,
   12891             :                 ObjectIdGetDatum(RelationGetRelid(rel)));
   12892         962 :     ScanKeyInit(&skey[1],
   12893             :                 Anum_pg_constraint_contypid,
   12894             :                 BTEqualStrategyNumber, F_OIDEQ,
   12895             :                 ObjectIdGetDatum(InvalidOid));
   12896         962 :     ScanKeyInit(&skey[2],
   12897             :                 Anum_pg_constraint_conname,
   12898             :                 BTEqualStrategyNumber, F_NAMEEQ,
   12899             :                 CStringGetDatum(constrName));
   12900         962 :     scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
   12901             :                               true, NULL, 3, skey);
   12902             : 
   12903             :     /* There can be at most one matching row */
   12904         962 :     if (HeapTupleIsValid(tuple = systable_getnext(scan)))
   12905             :     {
   12906         926 :         List       *readyRels = NIL;
   12907             : 
   12908         926 :         dropconstraint_internal(rel, tuple, behavior, recurse, false,
   12909             :                                 missing_ok, &readyRels, lockmode);
   12910         752 :         found = true;
   12911             :     }
   12912             : 
   12913         788 :     systable_endscan(scan);
   12914             : 
   12915         788 :     if (!found)
   12916             :     {
   12917          36 :         if (!missing_ok)
   12918          24 :             ereport(ERROR,
   12919             :                     errcode(ERRCODE_UNDEFINED_OBJECT),
   12920             :                     errmsg("constraint \"%s\" of relation \"%s\" does not exist",
   12921             :                            constrName, RelationGetRelationName(rel)));
   12922             :         else
   12923          12 :             ereport(NOTICE,
   12924             :                     errmsg("constraint \"%s\" of relation \"%s\" does not exist, skipping",
   12925             :                            constrName, RelationGetRelationName(rel)));
   12926             :     }
   12927             : 
   12928         764 :     table_close(conrel, RowExclusiveLock);
   12929         764 : }
   12930             : 
   12931             : /*
   12932             :  * Remove a constraint, using its pg_constraint tuple
   12933             :  *
   12934             :  * Implementation for ALTER TABLE DROP CONSTRAINT and ALTER TABLE ALTER COLUMN
   12935             :  * DROP NOT NULL.
   12936             :  *
   12937             :  * Returns the address of the constraint being removed.
   12938             :  */
   12939             : static ObjectAddress
   12940        1320 : dropconstraint_internal(Relation rel, HeapTuple constraintTup, DropBehavior behavior,
   12941             :                         bool recurse, bool recursing, bool missing_ok, List **readyRels,
   12942             :                         LOCKMODE lockmode)
   12943             : {
   12944             :     Relation    conrel;
   12945             :     Form_pg_constraint con;
   12946             :     ObjectAddress conobj;
   12947             :     List       *children;
   12948        1320 :     bool        is_no_inherit_constraint = false;
   12949             :     char       *constrName;
   12950        1320 :     List       *unconstrained_cols = NIL;
   12951        1320 :     char       *colname = NULL;
   12952        1320 :     bool        dropping_pk = false;
   12953             : 
   12954        1320 :     if (list_member_oid(*readyRels, RelationGetRelid(rel)))
   12955           0 :         return InvalidObjectAddress;
   12956        1320 :     *readyRels = lappend_oid(*readyRels, RelationGetRelid(rel));
   12957             : 
   12958             :     /* Guard against stack overflow due to overly deep inheritance tree. */
   12959        1320 :     check_stack_depth();
   12960             : 
   12961             :     /* At top level, permission check was done in ATPrepCmd, else do it */
   12962        1320 :     if (recursing)
   12963         234 :         ATSimplePermissions(AT_DropConstraint, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
   12964             : 
   12965        1314 :     conrel = table_open(ConstraintRelationId, RowExclusiveLock);
   12966             : 
   12967        1314 :     con = (Form_pg_constraint) GETSTRUCT(constraintTup);
   12968        1314 :     constrName = NameStr(con->conname);
   12969             : 
   12970             :     /*
   12971             :      * If we're asked to drop a constraint which is both defined locally and
   12972             :      * inherited, we can simply mark it as no longer having a local
   12973             :      * definition, and no further changes are required.
   12974             :      *
   12975             :      * XXX We do this for not-null constraints only, not CHECK, because the
   12976             :      * latter have historically not behaved this way and it might be confusing
   12977             :      * to change the behavior now.
   12978             :      */
   12979        1314 :     if (con->contype == CONSTRAINT_NOTNULL &&
   12980         484 :         con->conislocal && con->coninhcount > 0)
   12981             :     {
   12982             :         HeapTuple   copytup;
   12983             : 
   12984           6 :         copytup = heap_copytuple(constraintTup);
   12985           6 :         con = (Form_pg_constraint) GETSTRUCT(copytup);
   12986           6 :         con->conislocal = false;
   12987           6 :         CatalogTupleUpdate(conrel, &copytup->t_self, copytup);
   12988           6 :         ObjectAddressSet(conobj, ConstraintRelationId, con->oid);
   12989             : 
   12990           6 :         CommandCounterIncrement();
   12991           6 :         table_close(conrel, RowExclusiveLock);
   12992           6 :         return conobj;
   12993             :     }
   12994             : 
   12995             :     /* Don't allow drop of inherited constraints */
   12996        1308 :     if (con->coninhcount > 0 && !recursing)
   12997         138 :         ereport(ERROR,
   12998             :                 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
   12999             :                  errmsg("cannot drop inherited constraint \"%s\" of relation \"%s\"",
   13000             :                         constrName, RelationGetRelationName(rel))));
   13001             : 
   13002             :     /*
   13003             :      * See if we have a not-null constraint or a PRIMARY KEY.  If so, we have
   13004             :      * more checks and actions below, so obtain the list of columns that are
   13005             :      * constrained by the constraint being dropped.
   13006             :      */
   13007        1170 :     if (con->contype == CONSTRAINT_NOTNULL)
   13008             :     {
   13009             :         AttrNumber  colnum;
   13010             : 
   13011         460 :         colnum = extractNotNullColumn(constraintTup);
   13012         460 :         unconstrained_cols = list_make1_int(colnum);
   13013         460 :         colname = NameStr(TupleDescAttr(RelationGetDescr(rel),
   13014             :                                         colnum - 1)->attname);
   13015             :     }
   13016         710 :     else if (con->contype == CONSTRAINT_PRIMARY)
   13017             :     {
   13018             :         Datum       adatum;
   13019             :         ArrayType  *arr;
   13020             :         int         numkeys;
   13021             :         bool        isNull;
   13022             :         int16      *attnums;
   13023             : 
   13024         140 :         dropping_pk = true;
   13025             : 
   13026         140 :         adatum = heap_getattr(constraintTup, Anum_pg_constraint_conkey,
   13027             :                               RelationGetDescr(conrel), &isNull);
   13028         140 :         if (isNull)
   13029           0 :             elog(ERROR, "null conkey for constraint %u", con->oid);
   13030         140 :         arr = DatumGetArrayTypeP(adatum);   /* ensure not toasted */
   13031         140 :         numkeys = ARR_DIMS(arr)[0];
   13032         140 :         if (ARR_NDIM(arr) != 1 ||
   13033         140 :             numkeys < 0 ||
   13034         140 :             ARR_HASNULL(arr) ||
   13035         140 :             ARR_ELEMTYPE(arr) != INT2OID)
   13036           0 :             elog(ERROR, "conkey is not a 1-D smallint array");
   13037         140 :         attnums = (int16 *) ARR_DATA_PTR(arr);
   13038             : 
   13039         318 :         for (int i = 0; i < numkeys; i++)
   13040         178 :             unconstrained_cols = lappend_int(unconstrained_cols, attnums[i]);
   13041             :     }
   13042             : 
   13043        1170 :     is_no_inherit_constraint = con->connoinherit;
   13044             : 
   13045             :     /*
   13046             :      * If it's a foreign-key constraint, we'd better lock the referenced table
   13047             :      * and check that that's not in use, just as we've already done for the
   13048             :      * constrained table (else we might, eg, be dropping a trigger that has
   13049             :      * unfired events).  But we can/must skip that in the self-referential
   13050             :      * case.
   13051             :      */
   13052        1170 :     if (con->contype == CONSTRAINT_FOREIGN &&
   13053         192 :         con->confrelid != RelationGetRelid(rel))
   13054             :     {
   13055             :         Relation    frel;
   13056             : 
   13057             :         /* Must match lock taken by RemoveTriggerById: */
   13058         192 :         frel = table_open(con->confrelid, AccessExclusiveLock);
   13059         192 :         CheckTableNotInUse(frel, "ALTER TABLE");
   13060         186 :         table_close(frel, NoLock);
   13061             :     }
   13062             : 
   13063             :     /*
   13064             :      * Perform the actual constraint deletion
   13065             :      */
   13066        1164 :     ObjectAddressSet(conobj, ConstraintRelationId, con->oid);
   13067        1164 :     performDeletion(&conobj, behavior, 0);
   13068             : 
   13069             :     /*
   13070             :      * If this was a NOT NULL or the primary key, verify that we still have
   13071             :      * constraints to support GENERATED AS IDENTITY or the replica identity.
   13072             :      */
   13073        1128 :     if (unconstrained_cols != NIL)
   13074             :     {
   13075             :         Relation    attrel;
   13076             :         Bitmapset  *pkcols;
   13077             :         Bitmapset  *ircols;
   13078             : 
   13079             :         /* Make implicit attnotnull changes visible */
   13080         564 :         CommandCounterIncrement();
   13081             : 
   13082         564 :         attrel = table_open(AttributeRelationId, RowExclusiveLock);
   13083             : 
   13084             :         /*
   13085             :          * We want to test columns for their presence in the primary key, but
   13086             :          * only if we're not dropping it.
   13087             :          */
   13088         564 :         pkcols = dropping_pk ? NULL :
   13089         460 :             RelationGetIndexAttrBitmap(rel,
   13090             :                                        INDEX_ATTR_BITMAP_PRIMARY_KEY);
   13091         564 :         ircols = RelationGetIndexAttrBitmap(rel, INDEX_ATTR_BITMAP_IDENTITY_KEY);
   13092             : 
   13093        1682 :         foreach_int(attnum, unconstrained_cols)
   13094             :         {
   13095             :             HeapTuple   atttup;
   13096             :             HeapTuple   contup;
   13097             :             Form_pg_attribute attForm;
   13098             :             char        attidentity;
   13099             : 
   13100             :             /*
   13101             :              * Obtain pg_attribute tuple and verify conditions on it.
   13102             :              */
   13103         590 :             atttup = SearchSysCacheAttNum(RelationGetRelid(rel), attnum);
   13104         590 :             if (!HeapTupleIsValid(atttup))
   13105           0 :                 elog(ERROR, "cache lookup failed for attribute %d of relation %u",
   13106             :                      attnum, RelationGetRelid(rel));
   13107         590 :             attForm = (Form_pg_attribute) GETSTRUCT(atttup);
   13108         590 :             attidentity = attForm->attidentity;
   13109         590 :             ReleaseSysCache(atttup);
   13110             : 
   13111             :             /*
   13112             :              * Since the above deletion has been made visible, we can now
   13113             :              * search for any remaining constraints on this column (or these
   13114             :              * columns, in the case we're dropping a multicol primary key.)
   13115             :              * Then, verify whether any further NOT NULL or primary key
   13116             :              * exists: if none and this is a generated identity column or the
   13117             :              * replica identity, abort the whole thing.
   13118             :              */
   13119         590 :             contup = findNotNullConstraintAttnum(RelationGetRelid(rel), attnum);
   13120        1162 :             if (contup ||
   13121         572 :                 bms_is_member(attnum - FirstLowInvalidHeapAttributeNumber,
   13122             :                               pkcols))
   13123         248 :                 continue;
   13124             : 
   13125             :             /*
   13126             :              * It's not valid to drop the not-null constraint for a GENERATED
   13127             :              * AS IDENTITY column.
   13128             :              */
   13129         342 :             if (attidentity != '\0')
   13130           0 :                 ereport(ERROR,
   13131             :                         errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
   13132             :                         errmsg("column \"%s\" of relation \"%s\" is an identity column",
   13133             :                                get_attname(RelationGetRelid(rel), attnum,
   13134             :                                            false),
   13135             :                                RelationGetRelationName(rel)));
   13136             : 
   13137             :             /*
   13138             :              * It's not valid to drop the not-null constraint for a column in
   13139             :              * the replica identity index, either. (FULL is not affected.)
   13140             :              */
   13141         342 :             if (bms_is_member(attnum - FirstLowInvalidHeapAttributeNumber, ircols))
   13142          18 :                 ereport(ERROR,
   13143             :                         errcode(ERRCODE_INVALID_TABLE_DEFINITION),
   13144             :                         errmsg("column \"%s\" is in index used as replica identity",
   13145             :                                get_attname(RelationGetRelid(rel), attnum, false)));
   13146             :         }
   13147         546 :         table_close(attrel, RowExclusiveLock);
   13148             :     }
   13149             : 
   13150             :     /*
   13151             :      * For partitioned tables, non-CHECK, non-NOT-NULL inherited constraints
   13152             :      * are dropped via the dependency mechanism, so we're done here.
   13153             :      */
   13154        1110 :     if (con->contype != CONSTRAINT_CHECK &&
   13155         786 :         con->contype != CONSTRAINT_NOTNULL &&
   13156         338 :         rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
   13157             :     {
   13158          66 :         table_close(conrel, RowExclusiveLock);
   13159          66 :         return conobj;
   13160             :     }
   13161             : 
   13162             :     /*
   13163             :      * Propagate to children as appropriate.  Unlike most other ALTER
   13164             :      * routines, we have to do this one level of recursion at a time; we can't
   13165             :      * use find_all_inheritors to do it in one pass.
   13166             :      */
   13167        1044 :     if (!is_no_inherit_constraint)
   13168         566 :         children = find_inheritance_children(RelationGetRelid(rel), lockmode);
   13169             :     else
   13170         478 :         children = NIL;
   13171             : 
   13172             :     /*
   13173             :      * For a partitioned table, if partitions exist and we are told not to
   13174             :      * recurse, it's a user error.  It doesn't make sense to have a constraint
   13175             :      * be defined only on the parent, especially if it's a partitioned table.
   13176             :      */
   13177        1044 :     if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&
   13178          62 :         children != NIL && !recurse)
   13179           6 :         ereport(ERROR,
   13180             :                 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
   13181             :                  errmsg("cannot remove constraint from only the partitioned table when partitions exist"),
   13182             :                  errhint("Do not specify the ONLY keyword.")));
   13183             : 
   13184        2364 :     foreach_oid(childrelid, children)
   13185             :     {
   13186             :         Relation    childrel;
   13187             :         HeapTuple   tuple;
   13188             :         Form_pg_constraint childcon;
   13189             : 
   13190         300 :         if (list_member_oid(*readyRels, childrelid))
   13191           6 :             continue;           /* child already processed */
   13192             : 
   13193             :         /* find_inheritance_children already got lock */
   13194         294 :         childrel = table_open(childrelid, NoLock);
   13195         294 :         CheckTableNotInUse(childrel, "ALTER TABLE");
   13196             : 
   13197             :         /*
   13198             :          * We search for not-null constraints by column name, and others by
   13199             :          * constraint name.
   13200             :          */
   13201         294 :         if (con->contype == CONSTRAINT_NOTNULL)
   13202             :         {
   13203          94 :             tuple = findNotNullConstraint(childrelid, colname);
   13204          94 :             if (!HeapTupleIsValid(tuple))
   13205           0 :                 elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation %u",
   13206             :                      colname, RelationGetRelid(childrel));
   13207             :         }
   13208             :         else
   13209             :         {
   13210             :             SysScanDesc scan;
   13211             :             ScanKeyData skey[3];
   13212             : 
   13213         200 :             ScanKeyInit(&skey[0],
   13214             :                         Anum_pg_constraint_conrelid,
   13215             :                         BTEqualStrategyNumber, F_OIDEQ,
   13216             :                         ObjectIdGetDatum(childrelid));
   13217         200 :             ScanKeyInit(&skey[1],
   13218             :                         Anum_pg_constraint_contypid,
   13219             :                         BTEqualStrategyNumber, F_OIDEQ,
   13220             :                         ObjectIdGetDatum(InvalidOid));
   13221         200 :             ScanKeyInit(&skey[2],
   13222             :                         Anum_pg_constraint_conname,
   13223             :                         BTEqualStrategyNumber, F_NAMEEQ,
   13224             :                         CStringGetDatum(constrName));
   13225         200 :             scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
   13226             :                                       true, NULL, 3, skey);
   13227             :             /* There can only be one, so no need to loop */
   13228         200 :             tuple = systable_getnext(scan);
   13229         200 :             if (!HeapTupleIsValid(tuple))
   13230           0 :                 ereport(ERROR,
   13231             :                         (errcode(ERRCODE_UNDEFINED_OBJECT),
   13232             :                          errmsg("constraint \"%s\" of relation \"%s\" does not exist",
   13233             :                                 constrName,
   13234             :                                 RelationGetRelationName(childrel))));
   13235         200 :             tuple = heap_copytuple(tuple);
   13236         200 :             systable_endscan(scan);
   13237             :         }
   13238             : 
   13239         294 :         childcon = (Form_pg_constraint) GETSTRUCT(tuple);
   13240             : 
   13241             :         /* Right now only CHECK and not-null constraints can be inherited */
   13242         294 :         if (childcon->contype != CONSTRAINT_CHECK &&
   13243          94 :             childcon->contype != CONSTRAINT_NOTNULL)
   13244           0 :             elog(ERROR, "inherited constraint is not a CHECK or not-null constraint");
   13245             : 
   13246         294 :         if (childcon->coninhcount <= 0) /* shouldn't happen */
   13247           0 :             elog(ERROR, "relation %u has non-inherited constraint \"%s\"",
   13248             :                  childrelid, NameStr(childcon->conname));
   13249             : 
   13250         294 :         if (recurse)
   13251             :         {
   13252             :             /*
   13253             :              * If the child constraint has other definition sources, just
   13254             :              * decrement its inheritance count; if not, recurse to delete it.
   13255             :              */
   13256         288 :             if (childcon->coninhcount == 1 && !childcon->conislocal)
   13257             :             {
   13258             :                 /* Time to delete this child constraint, too */
   13259         210 :                 dropconstraint_internal(childrel, tuple, behavior,
   13260             :                                         recurse, true, missing_ok, readyRels,
   13261             :                                         lockmode);
   13262             :             }
   13263             :             else
   13264             :             {
   13265             :                 /* Child constraint must survive my deletion */
   13266          78 :                 childcon->coninhcount--;
   13267          78 :                 CatalogTupleUpdate(conrel, &tuple->t_self, tuple);
   13268             : 
   13269             :                 /* Make update visible */
   13270          78 :                 CommandCounterIncrement();
   13271             :             }
   13272             :         }
   13273             :         else
   13274             :         {
   13275             :             /*
   13276             :              * If we were told to drop ONLY in this table (no recursion) and
   13277             :              * there are no further parents for this constraint, we need to
   13278             :              * mark the inheritors' constraints as locally defined rather than
   13279             :              * inherited.
   13280             :              */
   13281           6 :             childcon->coninhcount--;
   13282           6 :             if (childcon->coninhcount == 0)
   13283           6 :                 childcon->conislocal = true;
   13284             : 
   13285           6 :             CatalogTupleUpdate(conrel, &tuple->t_self, tuple);
   13286             : 
   13287             :             /* Make update visible */
   13288           6 :             CommandCounterIncrement();
   13289             :         }
   13290             : 
   13291         288 :         heap_freetuple(tuple);
   13292             : 
   13293         288 :         table_close(childrel, NoLock);
   13294             :     }
   13295             : 
   13296             :     /*
   13297             :      * In addition, when dropping a primary key from a legacy-inheritance
   13298             :      * parent table, we must recurse to children to mark the corresponding NOT
   13299             :      * NULL constraint as no longer inherited, or drop it if this its last
   13300             :      * reference.
   13301             :      */
   13302        1032 :     if (con->contype == CONSTRAINT_PRIMARY &&
   13303          92 :         rel->rd_rel->relkind == RELKIND_RELATION &&
   13304          92 :         rel->rd_rel->relhassubclass)
   13305             :     {
   13306          12 :         List       *colnames = NIL;
   13307          12 :         List       *pkready = NIL;
   13308             : 
   13309             :         /*
   13310             :          * Because primary keys are always marked as NO INHERIT, we don't have
   13311             :          * a list of children yet, so obtain one now.
   13312             :          */
   13313          12 :         children = find_inheritance_children(RelationGetRelid(rel), lockmode);
   13314             : 
   13315             :         /*
   13316             :          * Find out the list of column names to process.  Fortunately, we
   13317             :          * already have the list of column numbers.
   13318             :          */
   13319          36 :         foreach_int(attnum, unconstrained_cols)
   13320             :         {
   13321          12 :             colnames = lappend(colnames, get_attname(RelationGetRelid(rel),
   13322             :                                                      attnum, false));
   13323             :         }
   13324             : 
   13325          48 :         foreach_oid(childrelid, children)
   13326             :         {
   13327             :             Relation    childrel;
   13328             : 
   13329          24 :             if (list_member_oid(pkready, childrelid))
   13330           0 :                 continue;       /* child already processed */
   13331             : 
   13332             :             /* find_inheritance_children already got lock */
   13333          24 :             childrel = table_open(childrelid, NoLock);
   13334          24 :             CheckTableNotInUse(childrel, "ALTER TABLE");
   13335             : 
   13336          72 :             foreach_ptr(char, colName, colnames)
   13337             :             {
   13338             :                 HeapTuple   contup;
   13339             : 
   13340          24 :                 contup = findNotNullConstraint(childrelid, colName);
   13341          24 :                 if (contup == NULL)
   13342           0 :                     elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\", relation \"%s\"",
   13343             :                          colName, RelationGetRelationName(childrel));
   13344             : 
   13345          24 :                 dropconstraint_internal(childrel, contup,
   13346             :                                         DROP_RESTRICT, true, true,
   13347             :                                         false, &pkready,
   13348             :                                         lockmode);
   13349          24 :                 pkready = NIL;
   13350             :             }
   13351             : 
   13352          24 :             table_close(childrel, NoLock);
   13353             : 
   13354          24 :             pkready = lappend_oid(pkready, childrelid);
   13355             :         }
   13356             :     }
   13357             : 
   13358        1032 :     table_close(conrel, RowExclusiveLock);
   13359             : 
   13360        1032 :     return conobj;
   13361             : }
   13362             : 
   13363             : /*
   13364             :  * ALTER COLUMN TYPE
   13365             :  *
   13366             :  * Unlike other subcommand types, we do parse transformation for ALTER COLUMN
   13367             :  * TYPE during phase 1 --- the AlterTableCmd passed in here is already
   13368             :  * transformed (and must be, because we rely on some transformed fields).
   13369             :  *
   13370             :  * The point of this is that the execution of all ALTER COLUMN TYPEs for a
   13371             :  * table will be done "in parallel" during phase 3, so all the USING
   13372             :  * expressions should be parsed assuming the original column types.  Also,
   13373             :  * this allows a USING expression to refer to a field that will be dropped.
   13374             :  *
   13375             :  * To make this work safely, AT_PASS_DROP then AT_PASS_ALTER_TYPE must be
   13376             :  * the first two execution steps in phase 2; they must not see the effects
   13377             :  * of any other subcommand types, since the USING expressions are parsed
   13378             :  * against the unmodified table's state.
   13379             :  */
   13380             : static void
   13381        1122 : ATPrepAlterColumnType(List **wqueue,
   13382             :                       AlteredTableInfo *tab, Relation rel,
   13383             :                       bool recurse, bool recursing,
   13384             :                       AlterTableCmd *cmd, LOCKMODE lockmode,
   13385             :                       AlterTableUtilityContext *context)
   13386             : {
   13387        1122 :     char       *colName = cmd->name;
   13388        1122 :     ColumnDef  *def = (ColumnDef *) cmd->def;
   13389        1122 :     TypeName   *typeName = def->typeName;
   13390        1122 :     Node       *transform = def->cooked_default;
   13391             :     HeapTuple   tuple;
   13392             :     Form_pg_attribute attTup;
   13393             :     AttrNumber  attnum;
   13394             :     Oid         targettype;
   13395             :     int32       targettypmod;
   13396             :     Oid         targetcollid;
   13397             :     NewColumnValue *newval;
   13398        1122 :     ParseState *pstate = make_parsestate(NULL);
   13399             :     AclResult   aclresult;
   13400             :     bool        is_expr;
   13401             : 
   13402        1122 :     if (rel->rd_rel->reloftype && !recursing)
   13403           6 :         ereport(ERROR,
   13404             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   13405             :                  errmsg("cannot alter column type of typed table")));
   13406             : 
   13407             :     /* lookup the attribute so we can check inheritance status */
   13408        1116 :     tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
   13409        1116 :     if (!HeapTupleIsValid(tuple))
   13410           0 :         ereport(ERROR,
   13411             :                 (errcode(ERRCODE_UNDEFINED_COLUMN),
   13412             :                  errmsg("column \"%s\" of relation \"%s\" does not exist",
   13413             :                         colName, RelationGetRelationName(rel))));
   13414        1116 :     attTup = (Form_pg_attribute) GETSTRUCT(tuple);
   13415        1116 :     attnum = attTup->attnum;
   13416             : 
   13417             :     /* Can't alter a system attribute */
   13418        1116 :     if (attnum <= 0)
   13419           0 :         ereport(ERROR,
   13420             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   13421             :                  errmsg("cannot alter system column \"%s\"",
   13422             :                         colName)));
   13423             : 
   13424             :     /*
   13425             :      * Don't alter inherited columns.  At outer level, there had better not be
   13426             :      * any inherited definition; when recursing, we assume this was checked at
   13427             :      * the parent level (see below).
   13428             :      */
   13429        1116 :     if (attTup->attinhcount > 0 && !recursing)
   13430           6 :         ereport(ERROR,
   13431             :                 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
   13432             :                  errmsg("cannot alter inherited column \"%s\"",
   13433             :                         colName)));
   13434             : 
   13435             :     /* Don't alter columns used in the partition key */
   13436        1110 :     if (has_partition_attrs(rel,
   13437             :                             bms_make_singleton(attnum - FirstLowInvalidHeapAttributeNumber),
   13438             :                             &is_expr))
   13439          18 :         ereport(ERROR,
   13440             :                 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
   13441             :                  errmsg("cannot alter column \"%s\" because it is part of the partition key of relation \"%s\"",
   13442             :                         colName, RelationGetRelationName(rel))));
   13443             : 
   13444             :     /* Look up the target type */
   13445        1092 :     typenameTypeIdAndMod(NULL, typeName, &targettype, &targettypmod);
   13446             : 
   13447        1092 :     aclresult = object_aclcheck(TypeRelationId, targettype, GetUserId(), ACL_USAGE);
   13448        1092 :     if (aclresult != ACLCHECK_OK)
   13449          12 :         aclcheck_error_type(aclresult, targettype);
   13450             : 
   13451             :     /* And the collation */
   13452        1080 :     targetcollid = GetColumnDefCollation(NULL, def, targettype);
   13453             : 
   13454             :     /* make sure datatype is legal for a column */
   13455        1080 :     CheckAttributeType(colName, targettype, targetcollid,
   13456        1080 :                        list_make1_oid(rel->rd_rel->reltype),
   13457             :                        0);
   13458             : 
   13459        1074 :     if (tab->relkind == RELKIND_RELATION ||
   13460         190 :         tab->relkind == RELKIND_PARTITIONED_TABLE)
   13461             :     {
   13462             :         /*
   13463             :          * Set up an expression to transform the old data value to the new
   13464             :          * type. If a USING option was given, use the expression as
   13465             :          * transformed by transformAlterTableStmt, else just take the old
   13466             :          * value and try to coerce it.  We do this first so that type
   13467             :          * incompatibility can be detected before we waste effort, and because
   13468             :          * we need the expression to be parsed against the original table row
   13469             :          * type.
   13470             :          */
   13471         938 :         if (!transform)
   13472             :         {
   13473         716 :             transform = (Node *) makeVar(1, attnum,
   13474             :                                          attTup->atttypid, attTup->atttypmod,
   13475             :                                          attTup->attcollation,
   13476             :                                          0);
   13477             :         }
   13478             : 
   13479         938 :         transform = coerce_to_target_type(pstate,
   13480             :                                           transform, exprType(transform),
   13481             :                                           targettype, targettypmod,
   13482             :                                           COERCION_ASSIGNMENT,
   13483             :                                           COERCE_IMPLICIT_CAST,
   13484             :                                           -1);
   13485         938 :         if (transform == NULL)
   13486             :         {
   13487             :             /* error text depends on whether USING was specified or not */
   13488          24 :             if (def->cooked_default != NULL)
   13489           6 :                 ereport(ERROR,
   13490             :                         (errcode(ERRCODE_DATATYPE_MISMATCH),
   13491             :                          errmsg("result of USING clause for column \"%s\""
   13492             :                                 " cannot be cast automatically to type %s",
   13493             :                                 colName, format_type_be(targettype)),
   13494             :                          errhint("You might need to add an explicit cast.")));
   13495             :             else
   13496          18 :                 ereport(ERROR,
   13497             :                         (errcode(ERRCODE_DATATYPE_MISMATCH),
   13498             :                          errmsg("column \"%s\" cannot be cast automatically to type %s",
   13499             :                                 colName, format_type_be(targettype)),
   13500             :                 /* translator: USING is SQL, don't translate it */
   13501             :                          errhint("You might need to specify \"USING %s::%s\".",
   13502             :                                  quote_identifier(colName),
   13503             :                                  format_type_with_typemod(targettype,
   13504             :                                                           targettypmod))));
   13505             :         }
   13506             : 
   13507             :         /* Fix collations after all else */
   13508         914 :         assign_expr_collations(pstate, transform);
   13509             : 
   13510             :         /* Plan the expr now so we can accurately assess the need to rewrite. */
   13511         914 :         transform = (Node *) expression_planner((Expr *) transform);
   13512             : 
   13513             :         /*
   13514             :          * Add a work queue item to make ATRewriteTable update the column
   13515             :          * contents.
   13516             :          */
   13517         914 :         newval = (NewColumnValue *) palloc0(sizeof(NewColumnValue));
   13518         914 :         newval->attnum = attnum;
   13519         914 :         newval->expr = (Expr *) transform;
   13520         914 :         newval->is_generated = false;
   13521             : 
   13522         914 :         tab->newvals = lappend(tab->newvals, newval);
   13523         914 :         if (ATColumnChangeRequiresRewrite(transform, attnum))
   13524         750 :             tab->rewrite |= AT_REWRITE_COLUMN_REWRITE;
   13525             :     }
   13526         136 :     else if (transform)
   13527          12 :         ereport(ERROR,
   13528             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   13529             :                  errmsg("\"%s\" is not a table",
   13530             :                         RelationGetRelationName(rel))));
   13531             : 
   13532        1038 :     if (!RELKIND_HAS_STORAGE(tab->relkind))
   13533             :     {
   13534             :         /*
   13535             :          * For relations without storage, do this check now.  Regular tables
   13536             :          * will check it later when the table is being rewritten.
   13537             :          */
   13538         178 :         find_composite_type_dependencies(rel->rd_rel->reltype, rel, NULL);
   13539             :     }
   13540             : 
   13541        1008 :     ReleaseSysCache(tuple);
   13542             : 
   13543             :     /*
   13544             :      * Recurse manually by queueing a new command for each child, if
   13545             :      * necessary. We cannot apply ATSimpleRecursion here because we need to
   13546             :      * remap attribute numbers in the USING expression, if any.
   13547             :      *
   13548             :      * If we are told not to recurse, there had better not be any child
   13549             :      * tables; else the alter would put them out of step.
   13550             :      */
   13551        1008 :     if (recurse)
   13552             :     {
   13553         768 :         Oid         relid = RelationGetRelid(rel);
   13554             :         List       *child_oids,
   13555             :                    *child_numparents;
   13556             :         ListCell   *lo,
   13557             :                    *li;
   13558             : 
   13559         768 :         child_oids = find_all_inheritors(relid, lockmode,
   13560             :                                          &child_numparents);
   13561             : 
   13562             :         /*
   13563             :          * find_all_inheritors does the recursive search of the inheritance
   13564             :          * hierarchy, so all we have to do is process all of the relids in the
   13565             :          * list that it returns.
   13566             :          */
   13567        1726 :         forboth(lo, child_oids, li, child_numparents)
   13568             :         {
   13569         982 :             Oid         childrelid = lfirst_oid(lo);
   13570         982 :             int         numparents = lfirst_int(li);
   13571             :             Relation    childrel;
   13572             :             HeapTuple   childtuple;
   13573             :             Form_pg_attribute childattTup;
   13574             : 
   13575         982 :             if (childrelid == relid)
   13576         768 :                 continue;
   13577             : 
   13578             :             /* find_all_inheritors already got lock */
   13579         214 :             childrel = relation_open(childrelid, NoLock);
   13580         214 :             CheckTableNotInUse(childrel, "ALTER TABLE");
   13581             : 
   13582             :             /*
   13583             :              * Verify that the child doesn't have any inherited definitions of
   13584             :              * this column that came from outside this inheritance hierarchy.
   13585             :              * (renameatt makes a similar test, though in a different way
   13586             :              * because of its different recursion mechanism.)
   13587             :              */
   13588         214 :             childtuple = SearchSysCacheAttName(RelationGetRelid(childrel),
   13589             :                                                colName);
   13590         214 :             if (!HeapTupleIsValid(childtuple))
   13591           0 :                 ereport(ERROR,
   13592             :                         (errcode(ERRCODE_UNDEFINED_COLUMN),
   13593             :                          errmsg("column \"%s\" of relation \"%s\" does not exist",
   13594             :                                 colName, RelationGetRelationName(childrel))));
   13595         214 :             childattTup = (Form_pg_attribute) GETSTRUCT(childtuple);
   13596             : 
   13597         214 :             if (childattTup->attinhcount > numparents)
   13598           6 :                 ereport(ERROR,
   13599             :                         (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
   13600             :                          errmsg("cannot alter inherited column \"%s\" of relation \"%s\"",
   13601             :                                 colName, RelationGetRelationName(childrel))));
   13602             : 
   13603         208 :             ReleaseSysCache(childtuple);
   13604             : 
   13605             :             /*
   13606             :              * Remap the attribute numbers.  If no USING expression was
   13607             :              * specified, there is no need for this step.
   13608             :              */
   13609         208 :             if (def->cooked_default)
   13610             :             {
   13611             :                 AttrMap    *attmap;
   13612             :                 bool        found_whole_row;
   13613             : 
   13614             :                 /* create a copy to scribble on */
   13615          72 :                 cmd = copyObject(cmd);
   13616             : 
   13617          72 :                 attmap = build_attrmap_by_name(RelationGetDescr(childrel),
   13618             :                                                RelationGetDescr(rel),
   13619             :                                                false);
   13620         144 :                 ((ColumnDef *) cmd->def)->cooked_default =
   13621          72 :                     map_variable_attnos(def->cooked_default,
   13622             :                                         1, 0,
   13623             :                                         attmap,
   13624             :                                         InvalidOid, &found_whole_row);
   13625          72 :                 if (found_whole_row)
   13626           6 :                     ereport(ERROR,
   13627             :                             (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   13628             :                              errmsg("cannot convert whole-row table reference"),
   13629             :                              errdetail("USING expression contains a whole-row table reference.")));
   13630          66 :                 pfree(attmap);
   13631             :             }
   13632         202 :             ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode, context);
   13633         190 :             relation_close(childrel, NoLock);
   13634             :         }
   13635             :     }
   13636         290 :     else if (!recursing &&
   13637          50 :              find_inheritance_children(RelationGetRelid(rel), NoLock) != NIL)
   13638           0 :         ereport(ERROR,
   13639             :                 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
   13640             :                  errmsg("type of inherited column \"%s\" must be changed in child tables too",
   13641             :                         colName)));
   13642             : 
   13643         984 :     if (tab->relkind == RELKIND_COMPOSITE_TYPE)
   13644          50 :         ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
   13645         978 : }
   13646             : 
   13647             : /*
   13648             :  * When the data type of a column is changed, a rewrite might not be required
   13649             :  * if the new type is sufficiently identical to the old one, and the USING
   13650             :  * clause isn't trying to insert some other value.  It's safe to skip the
   13651             :  * rewrite in these cases:
   13652             :  *
   13653             :  * - the old type is binary coercible to the new type
   13654             :  * - the new type is an unconstrained domain over the old type
   13655             :  * - {NEW,OLD} or {OLD,NEW} is {timestamptz,timestamp} and the timezone is UTC
   13656             :  *
   13657             :  * In the case of a constrained domain, we could get by with scanning the
   13658             :  * table and checking the constraint rather than actually rewriting it, but we
   13659             :  * don't currently try to do that.
   13660             :  */
   13661             : static bool
   13662        1018 : ATColumnChangeRequiresRewrite(Node *expr, AttrNumber varattno)
   13663             : {
   13664             :     Assert(expr != NULL);
   13665             : 
   13666             :     for (;;)
   13667             :     {
   13668             :         /* only one varno, so no need to check that */
   13669        1018 :         if (IsA(expr, Var) && ((Var *) expr)->varattno == varattno)
   13670         164 :             return false;
   13671         854 :         else if (IsA(expr, RelabelType))
   13672          92 :             expr = (Node *) ((RelabelType *) expr)->arg;
   13673         762 :         else if (IsA(expr, CoerceToDomain))
   13674             :         {
   13675           0 :             CoerceToDomain *d = (CoerceToDomain *) expr;
   13676             : 
   13677           0 :             if (DomainHasConstraints(d->resulttype))
   13678           0 :                 return true;
   13679           0 :             expr = (Node *) d->arg;
   13680             :         }
   13681         762 :         else if (IsA(expr, FuncExpr))
   13682             :         {
   13683         562 :             FuncExpr   *f = (FuncExpr *) expr;
   13684             : 
   13685         562 :             switch (f->funcid)
   13686             :             {
   13687          18 :                 case F_TIMESTAMPTZ_TIMESTAMP:
   13688             :                 case F_TIMESTAMP_TIMESTAMPTZ:
   13689          18 :                     if (TimestampTimestampTzRequiresRewrite())
   13690           6 :                         return true;
   13691             :                     else
   13692          12 :                         expr = linitial(f->args);
   13693          12 :                     break;
   13694         544 :                 default:
   13695         544 :                     return true;
   13696             :             }
   13697             :         }
   13698             :         else
   13699         200 :             return true;
   13700             :     }
   13701             : }
   13702             : 
   13703             : /*
   13704             :  * ALTER COLUMN .. SET DATA TYPE
   13705             :  *
   13706             :  * Return the address of the modified column.
   13707             :  */
   13708             : static ObjectAddress
   13709         948 : ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
   13710             :                       AlterTableCmd *cmd, LOCKMODE lockmode)
   13711             : {
   13712         948 :     char       *colName = cmd->name;
   13713         948 :     ColumnDef  *def = (ColumnDef *) cmd->def;
   13714         948 :     TypeName   *typeName = def->typeName;
   13715             :     HeapTuple   heapTup;
   13716             :     Form_pg_attribute attTup,
   13717             :                 attOldTup;
   13718             :     AttrNumber  attnum;
   13719             :     HeapTuple   typeTuple;
   13720             :     Form_pg_type tform;
   13721             :     Oid         targettype;
   13722             :     int32       targettypmod;
   13723             :     Oid         targetcollid;
   13724             :     Node       *defaultexpr;
   13725             :     Relation    attrelation;
   13726             :     Relation    depRel;
   13727             :     ScanKeyData key[3];
   13728             :     SysScanDesc scan;
   13729             :     HeapTuple   depTup;
   13730             :     ObjectAddress address;
   13731             : 
   13732             :     /*
   13733             :      * Clear all the missing values if we're rewriting the table, since this
   13734             :      * renders them pointless.
   13735             :      */
   13736         948 :     if (tab->rewrite)
   13737             :     {
   13738             :         Relation    newrel;
   13739             : 
   13740         696 :         newrel = table_open(RelationGetRelid(rel), NoLock);
   13741         696 :         RelationClearMissing(newrel);
   13742         696 :         relation_close(newrel, NoLock);
   13743             :         /* make sure we don't conflict with later attribute modifications */
   13744         696 :         CommandCounterIncrement();
   13745             :     }
   13746             : 
   13747         948 :     attrelation = table_open(AttributeRelationId, RowExclusiveLock);
   13748             : 
   13749             :     /* Look up the target column */
   13750         948 :     heapTup = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
   13751         948 :     if (!HeapTupleIsValid(heapTup)) /* shouldn't happen */
   13752           0 :         ereport(ERROR,
   13753             :                 (errcode(ERRCODE_UNDEFINED_COLUMN),
   13754             :                  errmsg("column \"%s\" of relation \"%s\" does not exist",
   13755             :                         colName, RelationGetRelationName(rel))));
   13756         948 :     attTup = (Form_pg_attribute) GETSTRUCT(heapTup);
   13757         948 :     attnum = attTup->attnum;
   13758         948 :     attOldTup = TupleDescAttr(tab->oldDesc, attnum - 1);
   13759             : 
   13760             :     /* Check for multiple ALTER TYPE on same column --- can't cope */
   13761         948 :     if (attTup->atttypid != attOldTup->atttypid ||
   13762         948 :         attTup->atttypmod != attOldTup->atttypmod)
   13763           0 :         ereport(ERROR,
   13764             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   13765             :                  errmsg("cannot alter type of column \"%s\" twice",
   13766             :                         colName)));
   13767             : 
   13768             :     /* Look up the target type (should not fail, since prep found it) */
   13769         948 :     typeTuple = typenameType(NULL, typeName, &targettypmod);
   13770         948 :     tform = (Form_pg_type) GETSTRUCT(typeTuple);
   13771         948 :     targettype = tform->oid;
   13772             :     /* And the collation */
   13773         948 :     targetcollid = GetColumnDefCollation(NULL, def, targettype);
   13774             : 
   13775             :     /*
   13776             :      * If there is a default expression for the column, get it and ensure we
   13777             :      * can coerce it to the new datatype.  (We must do this before changing
   13778             :      * the column type, because build_column_default itself will try to
   13779             :      * coerce, and will not issue the error message we want if it fails.)
   13780             :      *
   13781             :      * We remove any implicit coercion steps at the top level of the old
   13782             :      * default expression; this has been agreed to satisfy the principle of
   13783             :      * least surprise.  (The conversion to the new column type should act like
   13784             :      * it started from what the user sees as the stored expression, and the
   13785             :      * implicit coercions aren't going to be shown.)
   13786             :      */
   13787         948 :     if (attTup->atthasdef)
   13788             :     {
   13789          62 :         defaultexpr = build_column_default(rel, attnum);
   13790             :         Assert(defaultexpr);
   13791          62 :         defaultexpr = strip_implicit_coercions(defaultexpr);
   13792          62 :         defaultexpr = coerce_to_target_type(NULL,   /* no UNKNOWN params */
   13793             :                                             defaultexpr, exprType(defaultexpr),
   13794             :                                             targettype, targettypmod,
   13795             :                                             COERCION_ASSIGNMENT,
   13796             :                                             COERCE_IMPLICIT_CAST,
   13797             :                                             -1);
   13798          62 :         if (defaultexpr == NULL)
   13799             :         {
   13800          12 :             if (attTup->attgenerated)
   13801           6 :                 ereport(ERROR,
   13802             :                         (errcode(ERRCODE_DATATYPE_MISMATCH),
   13803             :                          errmsg("generation expression for column \"%s\" cannot be cast automatically to type %s",
   13804             :                                 colName, format_type_be(targettype))));
   13805             :             else
   13806           6 :                 ereport(ERROR,
   13807             :                         (errcode(ERRCODE_DATATYPE_MISMATCH),
   13808             :                          errmsg("default for column \"%s\" cannot be cast automatically to type %s",
   13809             :                                 colName, format_type_be(targettype))));
   13810             :         }
   13811             :     }
   13812             :     else
   13813         886 :         defaultexpr = NULL;
   13814             : 
   13815             :     /*
   13816             :      * Find everything that depends on the column (constraints, indexes, etc),
   13817             :      * and record enough information to let us recreate the objects.
   13818             :      *
   13819             :      * The actual recreation does not happen here, but only after we have
   13820             :      * performed all the individual ALTER TYPE operations.  We have to save
   13821             :      * the info before executing ALTER TYPE, though, else the deparser will
   13822             :      * get confused.
   13823             :      */
   13824         936 :     RememberAllDependentForRebuilding(tab, AT_AlterColumnType, rel, attnum, colName);
   13825             : 
   13826             :     /*
   13827             :      * Now scan for dependencies of this column on other things.  The only
   13828             :      * things we should find are the dependency on the column datatype and
   13829             :      * possibly a collation dependency.  Those can be removed.
   13830             :      */
   13831         912 :     depRel = table_open(DependRelationId, RowExclusiveLock);
   13832             : 
   13833         912 :     ScanKeyInit(&key[0],
   13834             :                 Anum_pg_depend_classid,
   13835             :                 BTEqualStrategyNumber, F_OIDEQ,
   13836             :                 ObjectIdGetDatum(RelationRelationId));
   13837         912 :     ScanKeyInit(&key[1],
   13838             :                 Anum_pg_depend_objid,
   13839             :                 BTEqualStrategyNumber, F_OIDEQ,
   13840             :                 ObjectIdGetDatum(RelationGetRelid(rel)));
   13841         912 :     ScanKeyInit(&key[2],
   13842             :                 Anum_pg_depend_objsubid,
   13843             :                 BTEqualStrategyNumber, F_INT4EQ,
   13844             :                 Int32GetDatum((int32) attnum));
   13845             : 
   13846         912 :     scan = systable_beginscan(depRel, DependDependerIndexId, true,
   13847             :                               NULL, 3, key);
   13848             : 
   13849         916 :     while (HeapTupleIsValid(depTup = systable_getnext(scan)))
   13850             :     {
   13851           4 :         Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(depTup);
   13852             :         ObjectAddress foundObject;
   13853             : 
   13854           4 :         foundObject.classId = foundDep->refclassid;
   13855           4 :         foundObject.objectId = foundDep->refobjid;
   13856           4 :         foundObject.objectSubId = foundDep->refobjsubid;
   13857             : 
   13858           4 :         if (foundDep->deptype != DEPENDENCY_NORMAL)
   13859           0 :             elog(ERROR, "found unexpected dependency type '%c'",
   13860             :                  foundDep->deptype);
   13861           4 :         if (!(foundDep->refclassid == TypeRelationId &&
   13862           4 :               foundDep->refobjid == attTup->atttypid) &&
   13863           0 :             !(foundDep->refclassid == CollationRelationId &&
   13864           0 :               foundDep->refobjid == attTup->attcollation))
   13865           0 :             elog(ERROR, "found unexpected dependency for column: %s",
   13866             :                  getObjectDescription(&foundObject, false));
   13867             : 
   13868           4 :         CatalogTupleDelete(depRel, &depTup->t_self);
   13869             :     }
   13870             : 
   13871         912 :     systable_endscan(scan);
   13872             : 
   13873         912 :     table_close(depRel, RowExclusiveLock);
   13874             : 
   13875             :     /*
   13876             :      * Here we go --- change the recorded column type and collation.  (Note
   13877             :      * heapTup is a copy of the syscache entry, so okay to scribble on.) First
   13878             :      * fix up the missing value if any.
   13879             :      */
   13880         912 :     if (attTup->atthasmissing)
   13881             :     {
   13882             :         Datum       missingval;
   13883             :         bool        missingNull;
   13884             : 
   13885             :         /* if rewrite is true the missing value should already be cleared */
   13886             :         Assert(tab->rewrite == 0);
   13887             : 
   13888             :         /* Get the missing value datum */
   13889           6 :         missingval = heap_getattr(heapTup,
   13890             :                                   Anum_pg_attribute_attmissingval,
   13891             :                                   attrelation->rd_att,
   13892             :                                   &missingNull);
   13893             : 
   13894             :         /* if it's a null array there is nothing to do */
   13895             : 
   13896           6 :         if (!missingNull)
   13897             :         {
   13898             :             /*
   13899             :              * Get the datum out of the array and repack it in a new array
   13900             :              * built with the new type data. We assume that since the table
   13901             :              * doesn't need rewriting, the actual Datum doesn't need to be
   13902             :              * changed, only the array metadata.
   13903             :              */
   13904             : 
   13905           6 :             int         one = 1;
   13906             :             bool        isNull;
   13907           6 :             Datum       valuesAtt[Natts_pg_attribute] = {0};
   13908           6 :             bool        nullsAtt[Natts_pg_attribute] = {0};
   13909           6 :             bool        replacesAtt[Natts_pg_attribute] = {0};
   13910             :             HeapTuple   newTup;
   13911             : 
   13912          12 :             missingval = array_get_element(missingval,
   13913             :                                            1,
   13914             :                                            &one,
   13915             :                                            0,
   13916           6 :                                            attTup->attlen,
   13917           6 :                                            attTup->attbyval,
   13918           6 :                                            attTup->attalign,
   13919             :                                            &isNull);
   13920           6 :             missingval = PointerGetDatum(construct_array(&missingval,
   13921             :                                                          1,
   13922             :                                                          targettype,
   13923           6 :                                                          tform->typlen,
   13924           6 :                                                          tform->typbyval,
   13925           6 :                                                          tform->typalign));
   13926             : 
   13927           6 :             valuesAtt[Anum_pg_attribute_attmissingval - 1] = missingval;
   13928           6 :             replacesAtt[Anum_pg_attribute_attmissingval - 1] = true;
   13929           6 :             nullsAtt[Anum_pg_attribute_attmissingval - 1] = false;
   13930             : 
   13931           6 :             newTup = heap_modify_tuple(heapTup, RelationGetDescr(attrelation),
   13932             :                                        valuesAtt, nullsAtt, replacesAtt);
   13933           6 :             heap_freetuple(heapTup);
   13934           6 :             heapTup = newTup;
   13935           6 :             attTup = (Form_pg_attribute) GETSTRUCT(heapTup);
   13936             :         }
   13937             :     }
   13938             : 
   13939         912 :     attTup->atttypid = targettype;
   13940         912 :     attTup->atttypmod = targettypmod;
   13941         912 :     attTup->attcollation = targetcollid;
   13942         912 :     if (list_length(typeName->arrayBounds) > PG_INT16_MAX)
   13943           0 :         ereport(ERROR,
   13944             :                 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
   13945             :                 errmsg("too many array dimensions"));
   13946         912 :     attTup->attndims = list_length(typeName->arrayBounds);
   13947         912 :     attTup->attlen = tform->typlen;
   13948         912 :     attTup->attbyval = tform->typbyval;
   13949         912 :     attTup->attalign = tform->typalign;
   13950         912 :     attTup->attstorage = tform->typstorage;
   13951         912 :     attTup->attcompression = InvalidCompressionMethod;
   13952             : 
   13953         912 :     ReleaseSysCache(typeTuple);
   13954             : 
   13955         912 :     CatalogTupleUpdate(attrelation, &heapTup->t_self, heapTup);
   13956             : 
   13957         912 :     table_close(attrelation, RowExclusiveLock);
   13958             : 
   13959             :     /* Install dependencies on new datatype and collation */
   13960         912 :     add_column_datatype_dependency(RelationGetRelid(rel), attnum, targettype);
   13961         912 :     add_column_collation_dependency(RelationGetRelid(rel), attnum, targetcollid);
   13962             : 
   13963             :     /*
   13964             :      * Drop any pg_statistic entry for the column, since it's now wrong type
   13965             :      */
   13966         912 :     RemoveStatistics(RelationGetRelid(rel), attnum);
   13967             : 
   13968         912 :     InvokeObjectPostAlterHook(RelationRelationId,
   13969             :                               RelationGetRelid(rel), attnum);
   13970             : 
   13971             :     /*
   13972             :      * Update the default, if present, by brute force --- remove and re-add
   13973             :      * the default.  Probably unsafe to take shortcuts, since the new version
   13974             :      * may well have additional dependencies.  (It's okay to do this now,
   13975             :      * rather than after other ALTER TYPE commands, since the default won't
   13976             :      * depend on other column types.)
   13977             :      */
   13978         912 :     if (defaultexpr)
   13979             :     {
   13980             :         /*
   13981             :          * If it's a GENERATED default, drop its dependency records, in
   13982             :          * particular its INTERNAL dependency on the column, which would
   13983             :          * otherwise cause dependency.c to refuse to perform the deletion.
   13984             :          */
   13985          50 :         if (attTup->attgenerated)
   13986             :         {
   13987           6 :             Oid         attrdefoid = GetAttrDefaultOid(RelationGetRelid(rel), attnum);
   13988             : 
   13989           6 :             if (!OidIsValid(attrdefoid))
   13990           0 :                 elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
   13991             :                      RelationGetRelid(rel), attnum);
   13992           6 :             (void) deleteDependencyRecordsFor(AttrDefaultRelationId, attrdefoid, false);
   13993             :         }
   13994             : 
   13995             :         /*
   13996             :          * Make updates-so-far visible, particularly the new pg_attribute row
   13997             :          * which will be updated again.
   13998             :          */
   13999          50 :         CommandCounterIncrement();
   14000             : 
   14001             :         /*
   14002             :          * We use RESTRICT here for safety, but at present we do not expect
   14003             :          * anything to depend on the default.
   14004             :          */
   14005          50 :         RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT, true,
   14006             :                           true);
   14007             : 
   14008          50 :         StoreAttrDefault(rel, attnum, defaultexpr, true, false);
   14009             :     }
   14010             : 
   14011         912 :     ObjectAddressSubSet(address, RelationRelationId,
   14012             :                         RelationGetRelid(rel), attnum);
   14013             : 
   14014             :     /* Cleanup */
   14015         912 :     heap_freetuple(heapTup);
   14016             : 
   14017         912 :     return address;
   14018             : }
   14019             : 
   14020             : /*
   14021             :  * Subroutine for ATExecAlterColumnType and ATExecSetExpression: Find everything
   14022             :  * that depends on the column (constraints, indexes, etc), and record enough
   14023             :  * information to let us recreate the objects.
   14024             :  */
   14025             : static void
   14026        1014 : RememberAllDependentForRebuilding(AlteredTableInfo *tab, AlterTableType subtype,
   14027             :                                   Relation rel, AttrNumber attnum, const char *colName)
   14028             : {
   14029             :     Relation    depRel;
   14030             :     ScanKeyData key[3];
   14031             :     SysScanDesc scan;
   14032             :     HeapTuple   depTup;
   14033             : 
   14034             :     Assert(subtype == AT_AlterColumnType || subtype == AT_SetExpression);
   14035             : 
   14036        1014 :     depRel = table_open(DependRelationId, RowExclusiveLock);
   14037             : 
   14038        1014 :     ScanKeyInit(&key[0],
   14039             :                 Anum_pg_depend_refclassid,
   14040             :                 BTEqualStrategyNumber, F_OIDEQ,
   14041             :                 ObjectIdGetDatum(RelationRelationId));
   14042        1014 :     ScanKeyInit(&key[1],
   14043             :                 Anum_pg_depend_refobjid,
   14044             :                 BTEqualStrategyNumber, F_OIDEQ,
   14045             :                 ObjectIdGetDatum(RelationGetRelid(rel)));
   14046        1014 :     ScanKeyInit(&key[2],
   14047             :                 Anum_pg_depend_refobjsubid,
   14048             :                 BTEqualStrategyNumber, F_INT4EQ,
   14049             :                 Int32GetDatum((int32) attnum));
   14050             : 
   14051        1014 :     scan = systable_beginscan(depRel, DependReferenceIndexId, true,
   14052             :                               NULL, 3, key);
   14053             : 
   14054        1944 :     while (HeapTupleIsValid(depTup = systable_getnext(scan)))
   14055             :     {
   14056         954 :         Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(depTup);
   14057             :         ObjectAddress foundObject;
   14058             : 
   14059         954 :         foundObject.classId = foundDep->classid;
   14060         954 :         foundObject.objectId = foundDep->objid;
   14061         954 :         foundObject.objectSubId = foundDep->objsubid;
   14062             : 
   14063         954 :         switch (foundObject.classId)
   14064             :         {
   14065         264 :             case RelationRelationId:
   14066             :                 {
   14067         264 :                     char        relKind = get_rel_relkind(foundObject.objectId);
   14068             : 
   14069         264 :                     if (relKind == RELKIND_INDEX ||
   14070             :                         relKind == RELKIND_PARTITIONED_INDEX)
   14071             :                     {
   14072             :                         Assert(foundObject.objectSubId == 0);
   14073         232 :                         RememberIndexForRebuilding(foundObject.objectId, tab);
   14074             :                     }
   14075          32 :                     else if (relKind == RELKIND_SEQUENCE)
   14076             :                     {
   14077             :                         /*
   14078             :                          * This must be a SERIAL column's sequence.  We need
   14079             :                          * not do anything to it.
   14080             :                          */
   14081             :                         Assert(foundObject.objectSubId == 0);
   14082             :                     }
   14083             :                     else
   14084             :                     {
   14085             :                         /* Not expecting any other direct dependencies... */
   14086           0 :                         elog(ERROR, "unexpected object depending on column: %s",
   14087             :                              getObjectDescription(&foundObject, false));
   14088             :                     }
   14089         264 :                     break;
   14090             :                 }
   14091             : 
   14092         524 :             case ConstraintRelationId:
   14093             :                 Assert(foundObject.objectSubId == 0);
   14094         524 :                 RememberConstraintForRebuilding(foundObject.objectId, tab);
   14095         524 :                 break;
   14096             : 
   14097          12 :             case RewriteRelationId:
   14098             :                 /* XXX someday see if we can cope with revising views */
   14099          12 :                 if (subtype == AT_AlterColumnType)
   14100          12 :                     ereport(ERROR,
   14101             :                             (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   14102             :                              errmsg("cannot alter type of a column used by a view or rule"),
   14103             :                              errdetail("%s depends on column \"%s\"",
   14104             :                                        getObjectDescription(&foundObject, false),
   14105             :                                        colName)));
   14106           0 :                 break;
   14107             : 
   14108           0 :             case TriggerRelationId:
   14109             : 
   14110             :                 /*
   14111             :                  * A trigger can depend on a column because the column is
   14112             :                  * specified as an update target, or because the column is
   14113             :                  * used in the trigger's WHEN condition.  The first case would
   14114             :                  * not require any extra work, but the second case would
   14115             :                  * require updating the WHEN expression, which will take a
   14116             :                  * significant amount of new code.  Since we can't easily tell
   14117             :                  * which case applies, we punt for both.  FIXME someday.
   14118             :                  */
   14119           0 :                 if (subtype == AT_AlterColumnType)
   14120           0 :                     ereport(ERROR,
   14121             :                             (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   14122             :                              errmsg("cannot alter type of a column used in a trigger definition"),
   14123             :                              errdetail("%s depends on column \"%s\"",
   14124             :                                        getObjectDescription(&foundObject, false),
   14125             :                                        colName)));
   14126           0 :                 break;
   14127             : 
   14128           0 :             case PolicyRelationId:
   14129             : 
   14130             :                 /*
   14131             :                  * A policy can depend on a column because the column is
   14132             :                  * specified in the policy's USING or WITH CHECK qual
   14133             :                  * expressions.  It might be possible to rewrite and recheck
   14134             :                  * the policy expression, but punt for now.  It's certainly
   14135             :                  * easy enough to remove and recreate the policy; still, FIXME
   14136             :                  * someday.
   14137             :                  */
   14138           0 :                 if (subtype == AT_AlterColumnType)
   14139           0 :                     ereport(ERROR,
   14140             :                             (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   14141             :                              errmsg("cannot alter type of a column used in a policy definition"),
   14142             :                              errdetail("%s depends on column \"%s\"",
   14143             :                                        getObjectDescription(&foundObject, false),
   14144             :                                        colName)));
   14145           0 :                 break;
   14146             : 
   14147         140 :             case AttrDefaultRelationId:
   14148             :                 {
   14149         140 :                     ObjectAddress col = GetAttrDefaultColumnAddress(foundObject.objectId);
   14150             : 
   14151         140 :                     if (col.objectId == RelationGetRelid(rel) &&
   14152         140 :                         col.objectSubId == attnum)
   14153             :                     {
   14154             :                         /*
   14155             :                          * Ignore the column's own default expression.  The
   14156             :                          * caller deals with it.
   14157             :                          */
   14158             :                     }
   14159             :                     else
   14160             :                     {
   14161             :                         /*
   14162             :                          * This must be a reference from the expression of a
   14163             :                          * generated column elsewhere in the same table.
   14164             :                          * Changing the type/generated expression of a column
   14165             :                          * that is used by a generated column is not allowed
   14166             :                          * by SQL standard, so just punt for now.  It might be
   14167             :                          * doable with some thinking and effort.
   14168             :                          */
   14169          12 :                         if (subtype == AT_AlterColumnType)
   14170          12 :                             ereport(ERROR,
   14171             :                                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   14172             :                                      errmsg("cannot alter type of a column used by a generated column"),
   14173             :                                      errdetail("Column \"%s\" is used by generated column \"%s\".",
   14174             :                                                colName,
   14175             :                                                get_attname(col.objectId,
   14176             :                                                            col.objectSubId,
   14177             :                                                            false))));
   14178             :                     }
   14179         128 :                     break;
   14180             :                 }
   14181             : 
   14182          14 :             case StatisticExtRelationId:
   14183             : 
   14184             :                 /*
   14185             :                  * Give the extended-stats machinery a chance to fix anything
   14186             :                  * that this column type change would break.
   14187             :                  */
   14188          14 :                 RememberStatisticsForRebuilding(foundObject.objectId, tab);
   14189          14 :                 break;
   14190             : 
   14191           0 :             default:
   14192             : 
   14193             :                 /*
   14194             :                  * We don't expect any other sorts of objects to depend on a
   14195             :                  * column.
   14196             :                  */
   14197           0 :                 elog(ERROR, "unexpected object depending on column: %s",
   14198             :                      getObjectDescription(&foundObject, false));
   14199             :                 break;
   14200             :         }
   14201             :     }
   14202             : 
   14203         990 :     systable_endscan(scan);
   14204         990 :     table_close(depRel, NoLock);
   14205         990 : }
   14206             : 
   14207             : /*
   14208             :  * Subroutine for ATExecAlterColumnType: remember that a replica identity
   14209             :  * needs to be reset.
   14210             :  */
   14211             : static void
   14212         434 : RememberReplicaIdentityForRebuilding(Oid indoid, AlteredTableInfo *tab)
   14213             : {
   14214         434 :     if (!get_index_isreplident(indoid))
   14215         416 :         return;
   14216             : 
   14217          18 :     if (tab->replicaIdentityIndex)
   14218           0 :         elog(ERROR, "relation %u has multiple indexes marked as replica identity", tab->relid);
   14219             : 
   14220          18 :     tab->replicaIdentityIndex = get_rel_name(indoid);
   14221             : }
   14222             : 
   14223             : /*
   14224             :  * Subroutine for ATExecAlterColumnType: remember any clustered index.
   14225             :  */
   14226             : static void
   14227         434 : RememberClusterOnForRebuilding(Oid indoid, AlteredTableInfo *tab)
   14228             : {
   14229         434 :     if (!get_index_isclustered(indoid))
   14230         416 :         return;
   14231             : 
   14232          18 :     if (tab->clusterOnIndex)
   14233           0 :         elog(ERROR, "relation %u has multiple clustered indexes", tab->relid);
   14234             : 
   14235          18 :     tab->clusterOnIndex = get_rel_name(indoid);
   14236             : }
   14237             : 
   14238             : /*
   14239             :  * Subroutine for ATExecAlterColumnType: remember that a constraint needs
   14240             :  * to be rebuilt (which we might already know).
   14241             :  */
   14242             : static void
   14243         536 : RememberConstraintForRebuilding(Oid conoid, AlteredTableInfo *tab)
   14244             : {
   14245             :     /*
   14246             :      * This de-duplication check is critical for two independent reasons: we
   14247             :      * mustn't try to recreate the same constraint twice, and if a constraint
   14248             :      * depends on more than one column whose type is to be altered, we must
   14249             :      * capture its definition string before applying any of the column type
   14250             :      * changes.  ruleutils.c will get confused if we ask again later.
   14251             :      */
   14252         536 :     if (!list_member_oid(tab->changedConstraintOids, conoid))
   14253             :     {
   14254             :         /* OK, capture the constraint's existing definition string */
   14255         446 :         char       *defstring = pg_get_constraintdef_command(conoid);
   14256             :         Oid         indoid;
   14257             : 
   14258         446 :         tab->changedConstraintOids = lappend_oid(tab->changedConstraintOids,
   14259             :                                                  conoid);
   14260         446 :         tab->changedConstraintDefs = lappend(tab->changedConstraintDefs,
   14261             :                                              defstring);
   14262             : 
   14263             :         /*
   14264             :          * For the index of a constraint, if any, remember if it is used for
   14265             :          * the table's replica identity or if it is a clustered index, so that
   14266             :          * ATPostAlterTypeCleanup() can queue up commands necessary to restore
   14267             :          * those properties.
   14268             :          */
   14269         446 :         indoid = get_constraint_index(conoid);
   14270         446 :         if (OidIsValid(indoid))
   14271             :         {
   14272         222 :             RememberReplicaIdentityForRebuilding(indoid, tab);
   14273         222 :             RememberClusterOnForRebuilding(indoid, tab);
   14274             :         }
   14275             :     }
   14276         536 : }
   14277             : 
   14278             : /*
   14279             :  * Subroutine for ATExecAlterColumnType: remember that an index needs
   14280             :  * to be rebuilt (which we might already know).
   14281             :  */
   14282             : static void
   14283         232 : RememberIndexForRebuilding(Oid indoid, AlteredTableInfo *tab)
   14284             : {
   14285             :     /*
   14286             :      * This de-duplication check is critical for two independent reasons: we
   14287             :      * mustn't try to recreate the same index twice, and if an index depends
   14288             :      * on more than one column whose type is to be altered, we must capture
   14289             :      * its definition string before applying any of the column type changes.
   14290             :      * ruleutils.c will get confused if we ask again later.
   14291             :      */
   14292         232 :     if (!list_member_oid(tab->changedIndexOids, indoid))
   14293             :     {
   14294             :         /*
   14295             :          * Before adding it as an index-to-rebuild, we'd better see if it
   14296             :          * belongs to a constraint, and if so rebuild the constraint instead.
   14297             :          * Typically this check fails, because constraint indexes normally
   14298             :          * have only dependencies on their constraint.  But it's possible for
   14299             :          * such an index to also have direct dependencies on table columns,
   14300             :          * for example with a partial exclusion constraint.
   14301             :          */
   14302         224 :         Oid         conoid = get_index_constraint(indoid);
   14303             : 
   14304         224 :         if (OidIsValid(conoid))
   14305             :         {
   14306          12 :             RememberConstraintForRebuilding(conoid, tab);
   14307             :         }
   14308             :         else
   14309             :         {
   14310             :             /* OK, capture the index's existing definition string */
   14311         212 :             char       *defstring = pg_get_indexdef_string(indoid);
   14312             : 
   14313         212 :             tab->changedIndexOids = lappend_oid(tab->changedIndexOids,
   14314             :                                                 indoid);
   14315         212 :             tab->changedIndexDefs = lappend(tab->changedIndexDefs,
   14316             :                                             defstring);
   14317             : 
   14318             :             /*
   14319             :              * Remember if this index is used for the table's replica identity
   14320             :              * or if it is a clustered index, so that ATPostAlterTypeCleanup()
   14321             :              * can queue up commands necessary to restore those properties.
   14322             :              */
   14323         212 :             RememberReplicaIdentityForRebuilding(indoid, tab);
   14324         212 :             RememberClusterOnForRebuilding(indoid, tab);
   14325             :         }
   14326             :     }
   14327         232 : }
   14328             : 
   14329             : /*
   14330             :  * Subroutine for ATExecAlterColumnType: remember that a statistics object
   14331             :  * needs to be rebuilt (which we might already know).
   14332             :  */
   14333             : static void
   14334          14 : RememberStatisticsForRebuilding(Oid stxoid, AlteredTableInfo *tab)
   14335             : {
   14336             :     /*
   14337             :      * This de-duplication check is critical for two independent reasons: we
   14338             :      * mustn't try to recreate the same statistics object twice, and if the
   14339             :      * statistics object depends on more than one column whose type is to be
   14340             :      * altered, we must capture its definition string before applying any of
   14341             :      * the type changes. ruleutils.c will get confused if we ask again later.
   14342             :      */
   14343          14 :     if (!list_member_oid(tab->changedStatisticsOids, stxoid))
   14344             :     {
   14345             :         /* OK, capture the statistics object's existing definition string */
   14346          14 :         char       *defstring = pg_get_statisticsobjdef_string(stxoid);
   14347             : 
   14348          14 :         tab->changedStatisticsOids = lappend_oid(tab->changedStatisticsOids,
   14349             :                                                  stxoid);
   14350          14 :         tab->changedStatisticsDefs = lappend(tab->changedStatisticsDefs,
   14351             :                                              defstring);
   14352             :     }
   14353          14 : }
   14354             : 
   14355             : /*
   14356             :  * Cleanup after we've finished all the ALTER TYPE or SET EXPRESSION
   14357             :  * operations for a particular relation.  We have to drop and recreate all the
   14358             :  * indexes and constraints that depend on the altered columns.  We do the
   14359             :  * actual dropping here, but re-creation is managed by adding work queue
   14360             :  * entries to do those steps later.
   14361             :  */
   14362             : static void
   14363         960 : ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab, LOCKMODE lockmode)
   14364             : {
   14365             :     ObjectAddress obj;
   14366             :     ObjectAddresses *objects;
   14367             :     ListCell   *def_item;
   14368             :     ListCell   *oid_item;
   14369             : 
   14370             :     /*
   14371             :      * Collect all the constraints and indexes to drop so we can process them
   14372             :      * in a single call.  That way we don't have to worry about dependencies
   14373             :      * among them.
   14374             :      */
   14375         960 :     objects = new_object_addresses();
   14376             : 
   14377             :     /*
   14378             :      * Re-parse the index and constraint definitions, and attach them to the
   14379             :      * appropriate work queue entries.  We do this before dropping because in
   14380             :      * the case of a FOREIGN KEY constraint, we might not yet have exclusive
   14381             :      * lock on the table the constraint is attached to, and we need to get
   14382             :      * that before reparsing/dropping.
   14383             :      *
   14384             :      * We can't rely on the output of deparsing to tell us which relation to
   14385             :      * operate on, because concurrent activity might have made the name
   14386             :      * resolve differently.  Instead, we've got to use the OID of the
   14387             :      * constraint or index we're processing to figure out which relation to
   14388             :      * operate on.
   14389             :      */
   14390        1406 :     forboth(oid_item, tab->changedConstraintOids,
   14391             :             def_item, tab->changedConstraintDefs)
   14392             :     {
   14393         446 :         Oid         oldId = lfirst_oid(oid_item);
   14394             :         HeapTuple   tup;
   14395             :         Form_pg_constraint con;
   14396             :         Oid         relid;
   14397             :         Oid         confrelid;
   14398             :         char        contype;
   14399             :         bool        conislocal;
   14400             : 
   14401         446 :         tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(oldId));
   14402         446 :         if (!HeapTupleIsValid(tup)) /* should not happen */
   14403           0 :             elog(ERROR, "cache lookup failed for constraint %u", oldId);
   14404         446 :         con = (Form_pg_constraint) GETSTRUCT(tup);
   14405         446 :         if (OidIsValid(con->conrelid))
   14406         432 :             relid = con->conrelid;
   14407             :         else
   14408             :         {
   14409             :             /* must be a domain constraint */
   14410          14 :             relid = get_typ_typrelid(getBaseType(con->contypid));
   14411          14 :             if (!OidIsValid(relid))
   14412           0 :                 elog(ERROR, "could not identify relation associated with constraint %u", oldId);
   14413             :         }
   14414         446 :         confrelid = con->confrelid;
   14415         446 :         contype = con->contype;
   14416         446 :         conislocal = con->conislocal;
   14417         446 :         ReleaseSysCache(tup);
   14418             : 
   14419         446 :         ObjectAddressSet(obj, ConstraintRelationId, oldId);
   14420         446 :         add_exact_object_address(&obj, objects);
   14421             : 
   14422             :         /*
   14423             :          * If the constraint is inherited (only), we don't want to inject a
   14424             :          * new definition here; it'll get recreated when
   14425             :          * ATAddCheckNNConstraint recurses from adding the parent table's
   14426             :          * constraint.  But we had to carry the info this far so that we can
   14427             :          * drop the constraint below.
   14428             :          */
   14429         446 :         if (!conislocal)
   14430          16 :             continue;
   14431             : 
   14432             :         /*
   14433             :          * When rebuilding an FK constraint that references the table we're
   14434             :          * modifying, we might not yet have any lock on the FK's table, so get
   14435             :          * one now.  We'll need AccessExclusiveLock for the DROP CONSTRAINT
   14436             :          * step, so there's no value in asking for anything weaker.
   14437             :          */
   14438         430 :         if (relid != tab->relid && contype == CONSTRAINT_FOREIGN)
   14439          36 :             LockRelationOid(relid, AccessExclusiveLock);
   14440             : 
   14441         430 :         ATPostAlterTypeParse(oldId, relid, confrelid,
   14442         430 :                              (char *) lfirst(def_item),
   14443         430 :                              wqueue, lockmode, tab->rewrite);
   14444             :     }
   14445        1172 :     forboth(oid_item, tab->changedIndexOids,
   14446             :             def_item, tab->changedIndexDefs)
   14447             :     {
   14448         212 :         Oid         oldId = lfirst_oid(oid_item);
   14449             :         Oid         relid;
   14450             : 
   14451         212 :         relid = IndexGetRelation(oldId, false);
   14452         212 :         ATPostAlterTypeParse(oldId, relid, InvalidOid,
   14453         212 :                              (char *) lfirst(def_item),
   14454         212 :                              wqueue, lockmode, tab->rewrite);
   14455             : 
   14456         212 :         ObjectAddressSet(obj, RelationRelationId, oldId);
   14457         212 :         add_exact_object_address(&obj, objects);
   14458             :     }
   14459             : 
   14460             :     /* add dependencies for new statistics */
   14461         974 :     forboth(oid_item, tab->changedStatisticsOids,
   14462             :             def_item, tab->changedStatisticsDefs)
   14463             :     {
   14464          14 :         Oid         oldId = lfirst_oid(oid_item);
   14465             :         Oid         relid;
   14466             : 
   14467          14 :         relid = StatisticsGetRelation(oldId, false);
   14468          14 :         ATPostAlterTypeParse(oldId, relid, InvalidOid,
   14469          14 :                              (char *) lfirst(def_item),
   14470          14 :                              wqueue, lockmode, tab->rewrite);
   14471             : 
   14472          14 :         ObjectAddressSet(obj, StatisticExtRelationId, oldId);
   14473          14 :         add_exact_object_address(&obj, objects);
   14474             :     }
   14475             : 
   14476             :     /*
   14477             :      * Queue up command to restore replica identity index marking
   14478             :      */
   14479         960 :     if (tab->replicaIdentityIndex)
   14480             :     {
   14481          18 :         AlterTableCmd *cmd = makeNode(AlterTableCmd);
   14482          18 :         ReplicaIdentityStmt *subcmd = makeNode(ReplicaIdentityStmt);
   14483             : 
   14484          18 :         subcmd->identity_type = REPLICA_IDENTITY_INDEX;
   14485          18 :         subcmd->name = tab->replicaIdentityIndex;
   14486          18 :         cmd->subtype = AT_ReplicaIdentity;
   14487          18 :         cmd->def = (Node *) subcmd;
   14488             : 
   14489             :         /* do it after indexes and constraints */
   14490          18 :         tab->subcmds[AT_PASS_OLD_CONSTR] =
   14491          18 :             lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
   14492             :     }
   14493             : 
   14494             :     /*
   14495             :      * Queue up command to restore marking of index used for cluster.
   14496             :      */
   14497         960 :     if (tab->clusterOnIndex)
   14498             :     {
   14499          18 :         AlterTableCmd *cmd = makeNode(AlterTableCmd);
   14500             : 
   14501          18 :         cmd->subtype = AT_ClusterOn;
   14502          18 :         cmd->name = tab->clusterOnIndex;
   14503             : 
   14504             :         /* do it after indexes and constraints */
   14505          18 :         tab->subcmds[AT_PASS_OLD_CONSTR] =
   14506          18 :             lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
   14507             :     }
   14508             : 
   14509             :     /*
   14510             :      * It should be okay to use DROP_RESTRICT here, since nothing else should
   14511             :      * be depending on these objects.
   14512             :      */
   14513         960 :     performMultipleDeletions(objects, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
   14514             : 
   14515         960 :     free_object_addresses(objects);
   14516             : 
   14517             :     /*
   14518             :      * The objects will get recreated during subsequent passes over the work
   14519             :      * queue.
   14520             :      */
   14521         960 : }
   14522             : 
   14523             : /*
   14524             :  * Parse the previously-saved definition string for a constraint, index or
   14525             :  * statistics object against the newly-established column data type(s), and
   14526             :  * queue up the resulting command parsetrees for execution.
   14527             :  *
   14528             :  * This might fail if, for example, you have a WHERE clause that uses an
   14529             :  * operator that's not available for the new column type.
   14530             :  */
   14531             : static void
   14532         656 : ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
   14533             :                      List **wqueue, LOCKMODE lockmode, bool rewrite)
   14534             : {
   14535             :     List       *raw_parsetree_list;
   14536             :     List       *querytree_list;
   14537             :     ListCell   *list_item;
   14538             :     Relation    rel;
   14539             : 
   14540             :     /*
   14541             :      * We expect that we will get only ALTER TABLE and CREATE INDEX
   14542             :      * statements. Hence, there is no need to pass them through
   14543             :      * parse_analyze_*() or the rewriter, but instead we need to pass them
   14544             :      * through parse_utilcmd.c to make them ready for execution.
   14545             :      */
   14546         656 :     raw_parsetree_list = raw_parser(cmd, RAW_PARSE_DEFAULT);
   14547         656 :     querytree_list = NIL;
   14548        1312 :     foreach(list_item, raw_parsetree_list)
   14549             :     {
   14550         656 :         RawStmt    *rs = lfirst_node(RawStmt, list_item);
   14551         656 :         Node       *stmt = rs->stmt;
   14552             : 
   14553         656 :         if (IsA(stmt, IndexStmt))
   14554         212 :             querytree_list = lappend(querytree_list,
   14555         212 :                                      transformIndexStmt(oldRelId,
   14556             :                                                         (IndexStmt *) stmt,
   14557             :                                                         cmd));
   14558         444 :         else if (IsA(stmt, AlterTableStmt))
   14559             :         {
   14560             :             List       *beforeStmts;
   14561             :             List       *afterStmts;
   14562             : 
   14563         416 :             stmt = (Node *) transformAlterTableStmt(oldRelId,
   14564             :                                                     (AlterTableStmt *) stmt,
   14565             :                                                     cmd,
   14566             :                                                     &beforeStmts,
   14567             :                                                     &afterStmts);
   14568         416 :             querytree_list = list_concat(querytree_list, beforeStmts);
   14569         416 :             querytree_list = lappend(querytree_list, stmt);
   14570         416 :             querytree_list = list_concat(querytree_list, afterStmts);
   14571             :         }
   14572          28 :         else if (IsA(stmt, CreateStatsStmt))
   14573          14 :             querytree_list = lappend(querytree_list,
   14574          14 :                                      transformStatsStmt(oldRelId,
   14575             :                                                         (CreateStatsStmt *) stmt,
   14576             :                                                         cmd));
   14577             :         else
   14578          14 :             querytree_list = lappend(querytree_list, stmt);
   14579             :     }
   14580             : 
   14581             :     /* Caller should already have acquired whatever lock we need. */
   14582         656 :     rel = relation_open(oldRelId, NoLock);
   14583             : 
   14584             :     /*
   14585             :      * Attach each generated command to the proper place in the work queue.
   14586             :      * Note this could result in creation of entirely new work-queue entries.
   14587             :      *
   14588             :      * Also note that we have to tweak the command subtypes, because it turns
   14589             :      * out that re-creation of indexes and constraints has to act a bit
   14590             :      * differently from initial creation.
   14591             :      */
   14592        1312 :     foreach(list_item, querytree_list)
   14593             :     {
   14594         656 :         Node       *stm = (Node *) lfirst(list_item);
   14595             :         AlteredTableInfo *tab;
   14596             : 
   14597         656 :         tab = ATGetQueueEntry(wqueue, rel);
   14598             : 
   14599         656 :         if (IsA(stm, IndexStmt))
   14600             :         {
   14601         212 :             IndexStmt  *stmt = (IndexStmt *) stm;
   14602             :             AlterTableCmd *newcmd;
   14603             : 
   14604         212 :             if (!rewrite)
   14605          54 :                 TryReuseIndex(oldId, stmt);
   14606         212 :             stmt->reset_default_tblspc = true;
   14607             :             /* keep the index's comment */
   14608         212 :             stmt->idxcomment = GetComment(oldId, RelationRelationId, 0);
   14609             : 
   14610         212 :             newcmd = makeNode(AlterTableCmd);
   14611         212 :             newcmd->subtype = AT_ReAddIndex;
   14612         212 :             newcmd->def = (Node *) stmt;
   14613         212 :             tab->subcmds[AT_PASS_OLD_INDEX] =
   14614         212 :                 lappend(tab->subcmds[AT_PASS_OLD_INDEX], newcmd);
   14615             :         }
   14616         444 :         else if (IsA(stm, AlterTableStmt))
   14617             :         {
   14618         416 :             AlterTableStmt *stmt = (AlterTableStmt *) stm;
   14619             :             ListCell   *lcmd;
   14620             : 
   14621         988 :             foreach(lcmd, stmt->cmds)
   14622             :             {
   14623         572 :                 AlterTableCmd *cmd = lfirst_node(AlterTableCmd, lcmd);
   14624             : 
   14625         572 :                 if (cmd->subtype == AT_AddIndex)
   14626             :                 {
   14627             :                     IndexStmt  *indstmt;
   14628             :                     Oid         indoid;
   14629             : 
   14630         222 :                     indstmt = castNode(IndexStmt, cmd->def);
   14631         222 :                     indoid = get_constraint_index(oldId);
   14632             : 
   14633         222 :                     if (!rewrite)
   14634          48 :                         TryReuseIndex(indoid, indstmt);
   14635             :                     /* keep any comment on the index */
   14636         222 :                     indstmt->idxcomment = GetComment(indoid,
   14637             :                                                      RelationRelationId, 0);
   14638         222 :                     indstmt->reset_default_tblspc = true;
   14639             : 
   14640         222 :                     cmd->subtype = AT_ReAddIndex;
   14641         222 :                     tab->subcmds[AT_PASS_OLD_INDEX] =
   14642         222 :                         lappend(tab->subcmds[AT_PASS_OLD_INDEX], cmd);
   14643             : 
   14644             :                     /* recreate any comment on the constraint */
   14645         222 :                     RebuildConstraintComment(tab,
   14646             :                                              AT_PASS_OLD_INDEX,
   14647             :                                              oldId,
   14648             :                                              rel,
   14649             :                                              NIL,
   14650         222 :                                              indstmt->idxname);
   14651             :                 }
   14652         350 :                 else if (cmd->subtype == AT_AddConstraint)
   14653             :                 {
   14654         194 :                     Constraint *con = castNode(Constraint, cmd->def);
   14655             : 
   14656         194 :                     con->old_pktable_oid = refRelId;
   14657             :                     /* rewriting neither side of a FK */
   14658         194 :                     if (con->contype == CONSTR_FOREIGN &&
   14659          72 :                         !rewrite && tab->rewrite == 0)
   14660           6 :                         TryReuseForeignKey(oldId, con);
   14661         194 :                     con->reset_default_tblspc = true;
   14662         194 :                     cmd->subtype = AT_ReAddConstraint;
   14663         194 :                     tab->subcmds[AT_PASS_OLD_CONSTR] =
   14664         194 :                         lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
   14665             : 
   14666             :                     /* recreate any comment on the constraint */
   14667         194 :                     RebuildConstraintComment(tab,
   14668             :                                              AT_PASS_OLD_CONSTR,
   14669             :                                              oldId,
   14670             :                                              rel,
   14671             :                                              NIL,
   14672         194 :                                              con->conname);
   14673             :                 }
   14674         156 :                 else if (cmd->subtype == AT_SetAttNotNull)
   14675             :                 {
   14676             :                     /*
   14677             :                      * We see this subtype when a primary key is created
   14678             :                      * internally, for example when it is replaced with a new
   14679             :                      * constraint (say because one of the columns changes
   14680             :                      * type); in this case we need to reinstate attnotnull,
   14681             :                      * because it was removed because of the drop of the old
   14682             :                      * PK.  Schedule this subcommand to an upcoming AT pass.
   14683             :                      */
   14684         156 :                     cmd->subtype = AT_SetAttNotNull;
   14685         156 :                     tab->subcmds[AT_PASS_OLD_COL_ATTRS] =
   14686         156 :                         lappend(tab->subcmds[AT_PASS_OLD_COL_ATTRS], cmd);
   14687             :                 }
   14688             :                 else
   14689           0 :                     elog(ERROR, "unexpected statement subtype: %d",
   14690             :                          (int) cmd->subtype);
   14691             :             }
   14692             :         }
   14693          28 :         else if (IsA(stm, AlterDomainStmt))
   14694             :         {
   14695          14 :             AlterDomainStmt *stmt = (AlterDomainStmt *) stm;
   14696             : 
   14697          14 :             if (stmt->subtype == 'C')    /* ADD CONSTRAINT */
   14698             :             {
   14699          14 :                 Constraint *con = castNode(Constraint, stmt->def);
   14700          14 :                 AlterTableCmd *cmd = makeNode(AlterTableCmd);
   14701             : 
   14702          14 :                 cmd->subtype = AT_ReAddDomainConstraint;
   14703          14 :                 cmd->def = (Node *) stmt;
   14704          14 :                 tab->subcmds[AT_PASS_OLD_CONSTR] =
   14705          14 :                     lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
   14706             : 
   14707             :                 /* recreate any comment on the constraint */
   14708          14 :                 RebuildConstraintComment(tab,
   14709             :                                          AT_PASS_OLD_CONSTR,
   14710             :                                          oldId,
   14711             :                                          NULL,
   14712             :                                          stmt->typeName,
   14713          14 :                                          con->conname);
   14714             :             }
   14715             :             else
   14716           0 :                 elog(ERROR, "unexpected statement subtype: %d",
   14717             :                      (int) stmt->subtype);
   14718             :         }
   14719          14 :         else if (IsA(stm, CreateStatsStmt))
   14720             :         {
   14721          14 :             CreateStatsStmt *stmt = (CreateStatsStmt *) stm;
   14722             :             AlterTableCmd *newcmd;
   14723             : 
   14724             :             /* keep the statistics object's comment */
   14725          14 :             stmt->stxcomment = GetComment(oldId, StatisticExtRelationId, 0);
   14726             : 
   14727          14 :             newcmd = makeNode(AlterTableCmd);
   14728          14 :             newcmd->subtype = AT_ReAddStatistics;
   14729          14 :             newcmd->def = (Node *) stmt;
   14730          14 :             tab->subcmds[AT_PASS_MISC] =
   14731          14 :                 lappend(tab->subcmds[AT_PASS_MISC], newcmd);
   14732             :         }
   14733             :         else
   14734           0 :             elog(ERROR, "unexpected statement type: %d",
   14735             :                  (int) nodeTag(stm));
   14736             :     }
   14737             : 
   14738         656 :     relation_close(rel, NoLock);
   14739         656 : }
   14740             : 
   14741             : /*
   14742             :  * Subroutine for ATPostAlterTypeParse() to recreate any existing comment
   14743             :  * for a table or domain constraint that is being rebuilt.
   14744             :  *
   14745             :  * objid is the OID of the constraint.
   14746             :  * Pass "rel" for a table constraint, or "domname" (domain's qualified name
   14747             :  * as a string list) for a domain constraint.
   14748             :  * (We could dig that info, as well as the conname, out of the pg_constraint
   14749             :  * entry; but callers already have them so might as well pass them.)
   14750             :  */
   14751             : static void
   14752         430 : RebuildConstraintComment(AlteredTableInfo *tab, AlterTablePass pass, Oid objid,
   14753             :                          Relation rel, List *domname,
   14754             :                          const char *conname)
   14755             : {
   14756             :     CommentStmt *cmd;
   14757             :     char       *comment_str;
   14758             :     AlterTableCmd *newcmd;
   14759             : 
   14760             :     /* Look for comment for object wanted, and leave if none */
   14761         430 :     comment_str = GetComment(objid, ConstraintRelationId, 0);
   14762         430 :     if (comment_str == NULL)
   14763         364 :         return;
   14764             : 
   14765             :     /* Build CommentStmt node, copying all input data for safety */
   14766          66 :     cmd = makeNode(CommentStmt);
   14767          66 :     if (rel)
   14768             :     {
   14769          54 :         cmd->objtype = OBJECT_TABCONSTRAINT;
   14770          54 :         cmd->object = (Node *)
   14771          54 :             list_make3(makeString(get_namespace_name(RelationGetNamespace(rel))),
   14772             :                        makeString(pstrdup(RelationGetRelationName(rel))),
   14773             :                        makeString(pstrdup(conname)));
   14774             :     }
   14775             :     else
   14776             :     {
   14777          12 :         cmd->objtype = OBJECT_DOMCONSTRAINT;
   14778          12 :         cmd->object = (Node *)
   14779          12 :             list_make2(makeTypeNameFromNameList(copyObject(domname)),
   14780             :                        makeString(pstrdup(conname)));
   14781             :     }
   14782          66 :     cmd->comment = comment_str;
   14783             : 
   14784             :     /* Append it to list of commands */
   14785          66 :     newcmd = makeNode(AlterTableCmd);
   14786          66 :     newcmd->subtype = AT_ReAddComment;
   14787          66 :     newcmd->def = (Node *) cmd;
   14788          66 :     tab->subcmds[pass] = lappend(tab->subcmds[pass], newcmd);
   14789             : }
   14790             : 
   14791             : /*
   14792             :  * Subroutine for ATPostAlterTypeParse().  Calls out to CheckIndexCompatible()
   14793             :  * for the real analysis, then mutates the IndexStmt based on that verdict.
   14794             :  */
   14795             : static void
   14796         102 : TryReuseIndex(Oid oldId, IndexStmt *stmt)
   14797             : {
   14798         102 :     if (CheckIndexCompatible(oldId,
   14799         102 :                              stmt->accessMethod,
   14800         102 :                              stmt->indexParams,
   14801         102 :                              stmt->excludeOpNames,
   14802         102 :                              stmt->iswithoutoverlaps))
   14803             :     {
   14804         102 :         Relation    irel = index_open(oldId, NoLock);
   14805             : 
   14806             :         /* If it's a partitioned index, there is no storage to share. */
   14807         102 :         if (irel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
   14808             :         {
   14809          72 :             stmt->oldNumber = irel->rd_locator.relNumber;
   14810          72 :             stmt->oldCreateSubid = irel->rd_createSubid;
   14811          72 :             stmt->oldFirstRelfilelocatorSubid = irel->rd_firstRelfilelocatorSubid;
   14812             :         }
   14813         102 :         index_close(irel, NoLock);
   14814             :     }
   14815         102 : }
   14816             : 
   14817             : /*
   14818             :  * Subroutine for ATPostAlterTypeParse().
   14819             :  *
   14820             :  * Stash the old P-F equality operator into the Constraint node, for possible
   14821             :  * use by ATAddForeignKeyConstraint() in determining whether revalidation of
   14822             :  * this constraint can be skipped.
   14823             :  */
   14824             : static void
   14825           6 : TryReuseForeignKey(Oid oldId, Constraint *con)
   14826             : {
   14827             :     HeapTuple   tup;
   14828             :     Datum       adatum;
   14829             :     ArrayType  *arr;
   14830             :     Oid        *rawarr;
   14831             :     int         numkeys;
   14832             :     int         i;
   14833             : 
   14834             :     Assert(con->contype == CONSTR_FOREIGN);
   14835             :     Assert(con->old_conpfeqop == NIL);   /* already prepared this node */
   14836             : 
   14837           6 :     tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(oldId));
   14838           6 :     if (!HeapTupleIsValid(tup)) /* should not happen */
   14839           0 :         elog(ERROR, "cache lookup failed for constraint %u", oldId);
   14840             : 
   14841           6 :     adatum = SysCacheGetAttrNotNull(CONSTROID, tup,
   14842             :                                     Anum_pg_constraint_conpfeqop);
   14843           6 :     arr = DatumGetArrayTypeP(adatum);   /* ensure not toasted */
   14844           6 :     numkeys = ARR_DIMS(arr)[0];
   14845             :     /* test follows the one in ri_FetchConstraintInfo() */
   14846           6 :     if (ARR_NDIM(arr) != 1 ||
   14847           6 :         ARR_HASNULL(arr) ||
   14848           6 :         ARR_ELEMTYPE(arr) != OIDOID)
   14849           0 :         elog(ERROR, "conpfeqop is not a 1-D Oid array");
   14850           6 :     rawarr = (Oid *) ARR_DATA_PTR(arr);
   14851             : 
   14852             :     /* stash a List of the operator Oids in our Constraint node */
   14853          12 :     for (i = 0; i < numkeys; i++)
   14854           6 :         con->old_conpfeqop = lappend_oid(con->old_conpfeqop, rawarr[i]);
   14855             : 
   14856           6 :     ReleaseSysCache(tup);
   14857           6 : }
   14858             : 
   14859             : /*
   14860             :  * ALTER COLUMN .. OPTIONS ( ... )
   14861             :  *
   14862             :  * Returns the address of the modified column
   14863             :  */
   14864             : static ObjectAddress
   14865         164 : ATExecAlterColumnGenericOptions(Relation rel,
   14866             :                                 const char *colName,
   14867             :                                 List *options,
   14868             :                                 LOCKMODE lockmode)
   14869             : {
   14870             :     Relation    ftrel;
   14871             :     Relation    attrel;
   14872             :     ForeignServer *server;
   14873             :     ForeignDataWrapper *fdw;
   14874             :     HeapTuple   tuple;
   14875             :     HeapTuple   newtuple;
   14876             :     bool        isnull;
   14877             :     Datum       repl_val[Natts_pg_attribute];
   14878             :     bool        repl_null[Natts_pg_attribute];
   14879             :     bool        repl_repl[Natts_pg_attribute];
   14880             :     Datum       datum;
   14881             :     Form_pg_foreign_table fttableform;
   14882             :     Form_pg_attribute atttableform;
   14883             :     AttrNumber  attnum;
   14884             :     ObjectAddress address;
   14885             : 
   14886         164 :     if (options == NIL)
   14887           0 :         return InvalidObjectAddress;
   14888             : 
   14889             :     /* First, determine FDW validator associated to the foreign table. */
   14890         164 :     ftrel = table_open(ForeignTableRelationId, AccessShareLock);
   14891         164 :     tuple = SearchSysCache1(FOREIGNTABLEREL, ObjectIdGetDatum(rel->rd_id));
   14892         164 :     if (!HeapTupleIsValid(tuple))
   14893           0 :         ereport(ERROR,
   14894             :                 (errcode(ERRCODE_UNDEFINED_OBJECT),
   14895             :                  errmsg("foreign table \"%s\" does not exist",
   14896             :                         RelationGetRelationName(rel))));
   14897         164 :     fttableform = (Form_pg_foreign_table) GETSTRUCT(tuple);
   14898         164 :     server = GetForeignServer(fttableform->ftserver);
   14899         164 :     fdw = GetForeignDataWrapper(server->fdwid);
   14900             : 
   14901         164 :     table_close(ftrel, AccessShareLock);
   14902         164 :     ReleaseSysCache(tuple);
   14903             : 
   14904         164 :     attrel = table_open(AttributeRelationId, RowExclusiveLock);
   14905         164 :     tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
   14906         164 :     if (!HeapTupleIsValid(tuple))
   14907           0 :         ereport(ERROR,
   14908             :                 (errcode(ERRCODE_UNDEFINED_COLUMN),
   14909             :                  errmsg("column \"%s\" of relation \"%s\" does not exist",
   14910             :                         colName, RelationGetRelationName(rel))));
   14911             : 
   14912             :     /* Prevent them from altering a system attribute */
   14913         164 :     atttableform = (Form_pg_attribute) GETSTRUCT(tuple);
   14914         164 :     attnum = atttableform->attnum;
   14915         164 :     if (attnum <= 0)
   14916           6 :         ereport(ERROR,
   14917             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   14918             :                  errmsg("cannot alter system column \"%s\"", colName)));
   14919             : 
   14920             : 
   14921             :     /* Initialize buffers for new tuple values */
   14922         158 :     memset(repl_val, 0, sizeof(repl_val));
   14923         158 :     memset(repl_null, false, sizeof(repl_null));
   14924         158 :     memset(repl_repl, false, sizeof(repl_repl));
   14925             : 
   14926             :     /* Extract the current options */
   14927         158 :     datum = SysCacheGetAttr(ATTNAME,
   14928             :                             tuple,
   14929             :                             Anum_pg_attribute_attfdwoptions,
   14930             :                             &isnull);
   14931         158 :     if (isnull)
   14932         148 :         datum = PointerGetDatum(NULL);
   14933             : 
   14934             :     /* Transform the options */
   14935         158 :     datum = transformGenericOptions(AttributeRelationId,
   14936             :                                     datum,
   14937             :                                     options,
   14938             :                                     fdw->fdwvalidator);
   14939             : 
   14940         158 :     if (PointerIsValid(DatumGetPointer(datum)))
   14941         158 :         repl_val[Anum_pg_attribute_attfdwoptions - 1] = datum;
   14942             :     else
   14943           0 :         repl_null[Anum_pg_attribute_attfdwoptions - 1] = true;
   14944             : 
   14945         158 :     repl_repl[Anum_pg_attribute_attfdwoptions - 1] = true;
   14946             : 
   14947             :     /* Everything looks good - update the tuple */
   14948             : 
   14949         158 :     newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrel),
   14950             :                                  repl_val, repl_null, repl_repl);
   14951             : 
   14952         158 :     CatalogTupleUpdate(attrel, &newtuple->t_self, newtuple);
   14953             : 
   14954         158 :     InvokeObjectPostAlterHook(RelationRelationId,
   14955             :                               RelationGetRelid(rel),
   14956             :                               atttableform->attnum);
   14957         158 :     ObjectAddressSubSet(address, RelationRelationId,
   14958             :                         RelationGetRelid(rel), attnum);
   14959             : 
   14960         158 :     ReleaseSysCache(tuple);
   14961             : 
   14962         158 :     table_close(attrel, RowExclusiveLock);
   14963             : 
   14964         158 :     heap_freetuple(newtuple);
   14965             : 
   14966         158 :     return address;
   14967             : }
   14968             : 
   14969             : /*
   14970             :  * ALTER TABLE OWNER
   14971             :  *
   14972             :  * recursing is true if we are recursing from a table to its indexes,
   14973             :  * sequences, or toast table.  We don't allow the ownership of those things to
   14974             :  * be changed separately from the parent table.  Also, we can skip permission
   14975             :  * checks (this is necessary not just an optimization, else we'd fail to
   14976             :  * handle toast tables properly).
   14977             :  *
   14978             :  * recursing is also true if ALTER TYPE OWNER is calling us to fix up a
   14979             :  * free-standing composite type.
   14980             :  */
   14981             : void
   14982        1978 : ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing, LOCKMODE lockmode)
   14983             : {
   14984             :     Relation    target_rel;
   14985             :     Relation    class_rel;
   14986             :     HeapTuple   tuple;
   14987             :     Form_pg_class tuple_class;
   14988             : 
   14989             :     /*
   14990             :      * Get exclusive lock till end of transaction on the target table. Use
   14991             :      * relation_open so that we can work on indexes and sequences.
   14992             :      */
   14993        1978 :     target_rel = relation_open(relationOid, lockmode);
   14994             : 
   14995             :     /* Get its pg_class tuple, too */
   14996        1978 :     class_rel = table_open(RelationRelationId, RowExclusiveLock);
   14997             : 
   14998        1978 :     tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relationOid));
   14999        1978 :     if (!HeapTupleIsValid(tuple))
   15000           0 :         elog(ERROR, "cache lookup failed for relation %u", relationOid);
   15001        1978 :     tuple_class = (Form_pg_class) GETSTRUCT(tuple);
   15002             : 
   15003             :     /* Can we change the ownership of this tuple? */
   15004        1978 :     switch (tuple_class->relkind)
   15005             :     {
   15006        1740 :         case RELKIND_RELATION:
   15007             :         case RELKIND_VIEW:
   15008             :         case RELKIND_MATVIEW:
   15009             :         case RELKIND_FOREIGN_TABLE:
   15010             :         case RELKIND_PARTITIONED_TABLE:
   15011             :             /* ok to change owner */
   15012        1740 :             break;
   15013          84 :         case RELKIND_INDEX:
   15014          84 :             if (!recursing)
   15015             :             {
   15016             :                 /*
   15017             :                  * Because ALTER INDEX OWNER used to be allowed, and in fact
   15018             :                  * is generated by old versions of pg_dump, we give a warning
   15019             :                  * and do nothing rather than erroring out.  Also, to avoid
   15020             :                  * unnecessary chatter while restoring those old dumps, say
   15021             :                  * nothing at all if the command would be a no-op anyway.
   15022             :                  */
   15023           0 :                 if (tuple_class->relowner != newOwnerId)
   15024           0 :                     ereport(WARNING,
   15025             :                             (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   15026             :                              errmsg("cannot change owner of index \"%s\"",
   15027             :                                     NameStr(tuple_class->relname)),
   15028             :                              errhint("Change the ownership of the index's table instead.")));
   15029             :                 /* quick hack to exit via the no-op path */
   15030           0 :                 newOwnerId = tuple_class->relowner;
   15031             :             }
   15032          84 :             break;
   15033          20 :         case RELKIND_PARTITIONED_INDEX:
   15034          20 :             if (recursing)
   15035          20 :                 break;
   15036           0 :             ereport(ERROR,
   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             :             break;
   15042          92 :         case RELKIND_SEQUENCE:
   15043          92 :             if (!recursing &&
   15044          62 :                 tuple_class->relowner != newOwnerId)
   15045             :             {
   15046             :                 /* if it's an owned sequence, disallow changing it by itself */
   15047             :                 Oid         tableId;
   15048             :                 int32       colId;
   15049             : 
   15050           0 :                 if (sequenceIsOwned(relationOid, DEPENDENCY_AUTO, &tableId, &colId) ||
   15051           0 :                     sequenceIsOwned(relationOid, DEPENDENCY_INTERNAL, &tableId, &colId))
   15052           0 :                     ereport(ERROR,
   15053             :                             (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   15054             :                              errmsg("cannot change owner of sequence \"%s\"",
   15055             :                                     NameStr(tuple_class->relname)),
   15056             :                              errdetail("Sequence \"%s\" is linked to table \"%s\".",
   15057             :                                        NameStr(tuple_class->relname),
   15058             :                                        get_rel_name(tableId))));
   15059             :             }
   15060          92 :             break;
   15061           6 :         case RELKIND_COMPOSITE_TYPE:
   15062           6 :             if (recursing)
   15063           6 :                 break;
   15064           0 :             ereport(ERROR,
   15065             :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   15066             :                      errmsg("\"%s\" is a composite type",
   15067             :                             NameStr(tuple_class->relname)),
   15068             :             /* translator: %s is an SQL ALTER command */
   15069             :                      errhint("Use %s instead.",
   15070             :                              "ALTER TYPE")));
   15071             :             break;
   15072          36 :         case RELKIND_TOASTVALUE:
   15073          36 :             if (recursing)
   15074          36 :                 break;
   15075             :             /* FALL THRU */
   15076             :         default:
   15077           0 :             ereport(ERROR,
   15078             :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   15079             :                      errmsg("cannot change owner of relation \"%s\"",
   15080             :                             NameStr(tuple_class->relname)),
   15081             :                      errdetail_relkind_not_supported(tuple_class->relkind)));
   15082             :     }
   15083             : 
   15084             :     /*
   15085             :      * If the new owner is the same as the existing owner, consider the
   15086             :      * command to have succeeded.  This is for dump restoration purposes.
   15087             :      */
   15088        1978 :     if (tuple_class->relowner != newOwnerId)
   15089             :     {
   15090             :         Datum       repl_val[Natts_pg_class];
   15091             :         bool        repl_null[Natts_pg_class];
   15092             :         bool        repl_repl[Natts_pg_class];
   15093             :         Acl        *newAcl;
   15094             :         Datum       aclDatum;
   15095             :         bool        isNull;
   15096             :         HeapTuple   newtuple;
   15097             : 
   15098             :         /* skip permission checks when recursing to index or toast table */
   15099         450 :         if (!recursing)
   15100             :         {
   15101             :             /* Superusers can always do it */
   15102         274 :             if (!superuser())
   15103             :             {
   15104          42 :                 Oid         namespaceOid = tuple_class->relnamespace;
   15105             :                 AclResult   aclresult;
   15106             : 
   15107             :                 /* Otherwise, must be owner of the existing object */
   15108          42 :                 if (!object_ownercheck(RelationRelationId, relationOid, GetUserId()))
   15109           0 :                     aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relationOid)),
   15110           0 :                                    RelationGetRelationName(target_rel));
   15111             : 
   15112             :                 /* Must be able to become new owner */
   15113          42 :                 check_can_set_role(GetUserId(), newOwnerId);
   15114             : 
   15115             :                 /* New owner must have CREATE privilege on namespace */
   15116          30 :                 aclresult = object_aclcheck(NamespaceRelationId, namespaceOid, newOwnerId,
   15117             :                                             ACL_CREATE);
   15118          30 :                 if (aclresult != ACLCHECK_OK)
   15119           0 :                     aclcheck_error(aclresult, OBJECT_SCHEMA,
   15120           0 :                                    get_namespace_name(namespaceOid));
   15121             :             }
   15122             :         }
   15123             : 
   15124         438 :         memset(repl_null, false, sizeof(repl_null));
   15125         438 :         memset(repl_repl, false, sizeof(repl_repl));
   15126             : 
   15127         438 :         repl_repl[Anum_pg_class_relowner - 1] = true;
   15128         438 :         repl_val[Anum_pg_class_relowner - 1] = ObjectIdGetDatum(newOwnerId);
   15129             : 
   15130             :         /*
   15131             :          * Determine the modified ACL for the new owner.  This is only
   15132             :          * necessary when the ACL is non-null.
   15133             :          */
   15134         438 :         aclDatum = SysCacheGetAttr(RELOID, tuple,
   15135             :                                    Anum_pg_class_relacl,
   15136             :                                    &isNull);
   15137         438 :         if (!isNull)
   15138             :         {
   15139          32 :             newAcl = aclnewowner(DatumGetAclP(aclDatum),
   15140             :                                  tuple_class->relowner, newOwnerId);
   15141          32 :             repl_repl[Anum_pg_class_relacl - 1] = true;
   15142          32 :             repl_val[Anum_pg_class_relacl - 1] = PointerGetDatum(newAcl);
   15143             :         }
   15144             : 
   15145         438 :         newtuple = heap_modify_tuple(tuple, RelationGetDescr(class_rel), repl_val, repl_null, repl_repl);
   15146             : 
   15147         438 :         CatalogTupleUpdate(class_rel, &newtuple->t_self, newtuple);
   15148             : 
   15149         438 :         heap_freetuple(newtuple);
   15150             : 
   15151             :         /*
   15152             :          * We must similarly update any per-column ACLs to reflect the new
   15153             :          * owner; for neatness reasons that's split out as a subroutine.
   15154             :          */
   15155         438 :         change_owner_fix_column_acls(relationOid,
   15156             :                                      tuple_class->relowner,
   15157             :                                      newOwnerId);
   15158             : 
   15159             :         /*
   15160             :          * Update owner dependency reference, if any.  A composite type has
   15161             :          * none, because it's tracked for the pg_type entry instead of here;
   15162             :          * indexes and TOAST tables don't have their own entries either.
   15163             :          */
   15164         438 :         if (tuple_class->relkind != RELKIND_COMPOSITE_TYPE &&
   15165         432 :             tuple_class->relkind != RELKIND_INDEX &&
   15166         348 :             tuple_class->relkind != RELKIND_PARTITIONED_INDEX &&
   15167         328 :             tuple_class->relkind != RELKIND_TOASTVALUE)
   15168         292 :             changeDependencyOnOwner(RelationRelationId, relationOid,
   15169             :                                     newOwnerId);
   15170             : 
   15171             :         /*
   15172             :          * Also change the ownership of the table's row type, if it has one
   15173             :          */
   15174         438 :         if (OidIsValid(tuple_class->reltype))
   15175         280 :             AlterTypeOwnerInternal(tuple_class->reltype, newOwnerId);
   15176             : 
   15177             :         /*
   15178             :          * If we are operating on a table or materialized view, also change
   15179             :          * the ownership of any indexes and sequences that belong to the
   15180             :          * relation, as well as its toast table (if it has one).
   15181             :          */
   15182         438 :         if (tuple_class->relkind == RELKIND_RELATION ||
   15183         226 :             tuple_class->relkind == RELKIND_PARTITIONED_TABLE ||
   15184         188 :             tuple_class->relkind == RELKIND_MATVIEW ||
   15185         188 :             tuple_class->relkind == RELKIND_TOASTVALUE)
   15186             :         {
   15187             :             List       *index_oid_list;
   15188             :             ListCell   *i;
   15189             : 
   15190             :             /* Find all the indexes belonging to this relation */
   15191         286 :             index_oid_list = RelationGetIndexList(target_rel);
   15192             : 
   15193             :             /* For each index, recursively change its ownership */
   15194         390 :             foreach(i, index_oid_list)
   15195         104 :                 ATExecChangeOwner(lfirst_oid(i), newOwnerId, true, lockmode);
   15196             : 
   15197         286 :             list_free(index_oid_list);
   15198             :         }
   15199             : 
   15200             :         /* If it has a toast table, recurse to change its ownership */
   15201         438 :         if (tuple_class->reltoastrelid != InvalidOid)
   15202          36 :             ATExecChangeOwner(tuple_class->reltoastrelid, newOwnerId,
   15203             :                               true, lockmode);
   15204             : 
   15205             :         /* If it has dependent sequences, recurse to change them too */
   15206         438 :         change_owner_recurse_to_sequences(relationOid, newOwnerId, lockmode);
   15207             :     }
   15208             : 
   15209        1966 :     InvokeObjectPostAlterHook(RelationRelationId, relationOid, 0);
   15210             : 
   15211        1966 :     ReleaseSysCache(tuple);
   15212        1966 :     table_close(class_rel, RowExclusiveLock);
   15213        1966 :     relation_close(target_rel, NoLock);
   15214        1966 : }
   15215             : 
   15216             : /*
   15217             :  * change_owner_fix_column_acls
   15218             :  *
   15219             :  * Helper function for ATExecChangeOwner.  Scan the columns of the table
   15220             :  * and fix any non-null column ACLs to reflect the new owner.
   15221             :  */
   15222             : static void
   15223         438 : change_owner_fix_column_acls(Oid relationOid, Oid oldOwnerId, Oid newOwnerId)
   15224             : {
   15225             :     Relation    attRelation;
   15226             :     SysScanDesc scan;
   15227             :     ScanKeyData key[1];
   15228             :     HeapTuple   attributeTuple;
   15229             : 
   15230         438 :     attRelation = table_open(AttributeRelationId, RowExclusiveLock);
   15231         438 :     ScanKeyInit(&key[0],
   15232             :                 Anum_pg_attribute_attrelid,
   15233             :                 BTEqualStrategyNumber, F_OIDEQ,
   15234             :                 ObjectIdGetDatum(relationOid));
   15235         438 :     scan = systable_beginscan(attRelation, AttributeRelidNumIndexId,
   15236             :                               true, NULL, 1, key);
   15237        3012 :     while (HeapTupleIsValid(attributeTuple = systable_getnext(scan)))
   15238             :     {
   15239        2574 :         Form_pg_attribute att = (Form_pg_attribute) GETSTRUCT(attributeTuple);
   15240             :         Datum       repl_val[Natts_pg_attribute];
   15241             :         bool        repl_null[Natts_pg_attribute];
   15242             :         bool        repl_repl[Natts_pg_attribute];
   15243             :         Acl        *newAcl;
   15244             :         Datum       aclDatum;
   15245             :         bool        isNull;
   15246             :         HeapTuple   newtuple;
   15247             : 
   15248             :         /* Ignore dropped columns */
   15249        2574 :         if (att->attisdropped)
   15250        2574 :             continue;
   15251             : 
   15252        2574 :         aclDatum = heap_getattr(attributeTuple,
   15253             :                                 Anum_pg_attribute_attacl,
   15254             :                                 RelationGetDescr(attRelation),
   15255             :                                 &isNull);
   15256             :         /* Null ACLs do not require changes */
   15257        2574 :         if (isNull)
   15258        2574 :             continue;
   15259             : 
   15260           0 :         memset(repl_null, false, sizeof(repl_null));
   15261           0 :         memset(repl_repl, false, sizeof(repl_repl));
   15262             : 
   15263           0 :         newAcl = aclnewowner(DatumGetAclP(aclDatum),
   15264             :                              oldOwnerId, newOwnerId);
   15265           0 :         repl_repl[Anum_pg_attribute_attacl - 1] = true;
   15266           0 :         repl_val[Anum_pg_attribute_attacl - 1] = PointerGetDatum(newAcl);
   15267             : 
   15268           0 :         newtuple = heap_modify_tuple(attributeTuple,
   15269             :                                      RelationGetDescr(attRelation),
   15270             :                                      repl_val, repl_null, repl_repl);
   15271             : 
   15272           0 :         CatalogTupleUpdate(attRelation, &newtuple->t_self, newtuple);
   15273             : 
   15274           0 :         heap_freetuple(newtuple);
   15275             :     }
   15276         438 :     systable_endscan(scan);
   15277         438 :     table_close(attRelation, RowExclusiveLock);
   15278         438 : }
   15279             : 
   15280             : /*
   15281             :  * change_owner_recurse_to_sequences
   15282             :  *
   15283             :  * Helper function for ATExecChangeOwner.  Examines pg_depend searching
   15284             :  * for sequences that are dependent on serial columns, and changes their
   15285             :  * ownership.
   15286             :  */
   15287             : static void
   15288         438 : change_owner_recurse_to_sequences(Oid relationOid, Oid newOwnerId, LOCKMODE lockmode)
   15289             : {
   15290             :     Relation    depRel;
   15291             :     SysScanDesc scan;
   15292             :     ScanKeyData key[2];
   15293             :     HeapTuple   tup;
   15294             : 
   15295             :     /*
   15296             :      * SERIAL sequences are those having an auto dependency on one of the
   15297             :      * table's columns (we don't care *which* column, exactly).
   15298             :      */
   15299         438 :     depRel = table_open(DependRelationId, AccessShareLock);
   15300             : 
   15301         438 :     ScanKeyInit(&key[0],
   15302             :                 Anum_pg_depend_refclassid,
   15303             :                 BTEqualStrategyNumber, F_OIDEQ,
   15304             :                 ObjectIdGetDatum(RelationRelationId));
   15305         438 :     ScanKeyInit(&key[1],
   15306             :                 Anum_pg_depend_refobjid,
   15307             :                 BTEqualStrategyNumber, F_OIDEQ,
   15308             :                 ObjectIdGetDatum(relationOid));
   15309             :     /* we leave refobjsubid unspecified */
   15310             : 
   15311         438 :     scan = systable_beginscan(depRel, DependReferenceIndexId, true,
   15312             :                               NULL, 2, key);
   15313             : 
   15314        1216 :     while (HeapTupleIsValid(tup = systable_getnext(scan)))
   15315             :     {
   15316         778 :         Form_pg_depend depForm = (Form_pg_depend) GETSTRUCT(tup);
   15317             :         Relation    seqRel;
   15318             : 
   15319             :         /* skip dependencies other than auto dependencies on columns */
   15320         778 :         if (depForm->refobjsubid == 0 ||
   15321         268 :             depForm->classid != RelationRelationId ||
   15322         122 :             depForm->objsubid != 0 ||
   15323         122 :             !(depForm->deptype == DEPENDENCY_AUTO || depForm->deptype == DEPENDENCY_INTERNAL))
   15324         656 :             continue;
   15325             : 
   15326             :         /* Use relation_open just in case it's an index */
   15327         122 :         seqRel = relation_open(depForm->objid, lockmode);
   15328             : 
   15329             :         /* skip non-sequence relations */
   15330         122 :         if (RelationGetForm(seqRel)->relkind != RELKIND_SEQUENCE)
   15331             :         {
   15332             :             /* No need to keep the lock */
   15333         104 :             relation_close(seqRel, lockmode);
   15334         104 :             continue;
   15335             :         }
   15336             : 
   15337             :         /* We don't need to close the sequence while we alter it. */
   15338          18 :         ATExecChangeOwner(depForm->objid, newOwnerId, true, lockmode);
   15339             : 
   15340             :         /* Now we can close it.  Keep the lock till end of transaction. */
   15341          18 :         relation_close(seqRel, NoLock);
   15342             :     }
   15343             : 
   15344         438 :     systable_endscan(scan);
   15345             : 
   15346         438 :     relation_close(depRel, AccessShareLock);
   15347         438 : }
   15348             : 
   15349             : /*
   15350             :  * ALTER TABLE CLUSTER ON
   15351             :  *
   15352             :  * The only thing we have to do is to change the indisclustered bits.
   15353             :  *
   15354             :  * Return the address of the new clustering index.
   15355             :  */
   15356             : static ObjectAddress
   15357          64 : ATExecClusterOn(Relation rel, const char *indexName, LOCKMODE lockmode)
   15358             : {
   15359             :     Oid         indexOid;
   15360             :     ObjectAddress address;
   15361             : 
   15362          64 :     indexOid = get_relname_relid(indexName, rel->rd_rel->relnamespace);
   15363             : 
   15364          64 :     if (!OidIsValid(indexOid))
   15365           0 :         ereport(ERROR,
   15366             :                 (errcode(ERRCODE_UNDEFINED_OBJECT),
   15367             :                  errmsg("index \"%s\" for table \"%s\" does not exist",
   15368             :                         indexName, RelationGetRelationName(rel))));
   15369             : 
   15370             :     /* Check index is valid to cluster on */
   15371          64 :     check_index_is_clusterable(rel, indexOid, lockmode);
   15372             : 
   15373             :     /* And do the work */
   15374          64 :     mark_index_clustered(rel, indexOid, false);
   15375             : 
   15376          58 :     ObjectAddressSet(address,
   15377             :                      RelationRelationId, indexOid);
   15378             : 
   15379          58 :     return address;
   15380             : }
   15381             : 
   15382             : /*
   15383             :  * ALTER TABLE SET WITHOUT CLUSTER
   15384             :  *
   15385             :  * We have to find any indexes on the table that have indisclustered bit
   15386             :  * set and turn it off.
   15387             :  */
   15388             : static void
   15389          18 : ATExecDropCluster(Relation rel, LOCKMODE lockmode)
   15390             : {
   15391          18 :     mark_index_clustered(rel, InvalidOid, false);
   15392          12 : }
   15393             : 
   15394             : /*
   15395             :  * Preparation phase for SET ACCESS METHOD
   15396             :  *
   15397             :  * Check that the access method exists and determine whether a change is
   15398             :  * actually needed.
   15399             :  */
   15400             : static void
   15401         110 : ATPrepSetAccessMethod(AlteredTableInfo *tab, Relation rel, const char *amname)
   15402             : {
   15403             :     Oid         amoid;
   15404             : 
   15405             :     /*
   15406             :      * Look up the access method name and check that it differs from the
   15407             :      * table's current AM.  If DEFAULT was specified for a partitioned table
   15408             :      * (amname is NULL), set it to InvalidOid to reset the catalogued AM.
   15409             :      */
   15410         110 :     if (amname != NULL)
   15411          74 :         amoid = get_table_am_oid(amname, false);
   15412          36 :     else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
   15413          18 :         amoid = InvalidOid;
   15414             :     else
   15415          18 :         amoid = get_table_am_oid(default_table_access_method, false);
   15416             : 
   15417             :     /* if it's a match, phase 3 doesn't need to do anything */
   15418         110 :     if (rel->rd_rel->relam == amoid)
   15419          12 :         return;
   15420             : 
   15421             :     /* Save info for Phase 3 to do the real work */
   15422          98 :     tab->rewrite |= AT_REWRITE_ACCESS_METHOD;
   15423          98 :     tab->newAccessMethod = amoid;
   15424          98 :     tab->chgAccessMethod = true;
   15425             : }
   15426             : 
   15427             : /*
   15428             :  * Special handling of ALTER TABLE SET ACCESS METHOD for relations with no
   15429             :  * storage that have an interest in preserving AM.
   15430             :  *
   15431             :  * Since these have no storage, setting the access method is a catalog only
   15432             :  * operation.
   15433             :  */
   15434             : static void
   15435          44 : ATExecSetAccessMethodNoStorage(Relation rel, Oid newAccessMethodId)
   15436             : {
   15437             :     Relation    pg_class;
   15438             :     Oid         oldAccessMethodId;
   15439             :     HeapTuple   tuple;
   15440             :     Form_pg_class rd_rel;
   15441          44 :     Oid         reloid = RelationGetRelid(rel);
   15442             : 
   15443             :     /*
   15444             :      * Shouldn't be called on relations having storage; these are processed in
   15445             :      * phase 3.
   15446             :      */
   15447             :     Assert(!RELKIND_HAS_STORAGE(rel->rd_rel->relkind));
   15448             : 
   15449             :     /* Get a modifiable copy of the relation's pg_class row. */
   15450          44 :     pg_class = table_open(RelationRelationId, RowExclusiveLock);
   15451             : 
   15452          44 :     tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(reloid));
   15453          44 :     if (!HeapTupleIsValid(tuple))
   15454           0 :         elog(ERROR, "cache lookup failed for relation %u", reloid);
   15455          44 :     rd_rel = (Form_pg_class) GETSTRUCT(tuple);
   15456             : 
   15457             :     /* Update the pg_class row. */
   15458          44 :     oldAccessMethodId = rd_rel->relam;
   15459          44 :     rd_rel->relam = newAccessMethodId;
   15460             : 
   15461             :     /* Leave if no update required */
   15462          44 :     if (rd_rel->relam == oldAccessMethodId)
   15463             :     {
   15464           0 :         heap_freetuple(tuple);
   15465           0 :         table_close(pg_class, RowExclusiveLock);
   15466           0 :         return;
   15467             :     }
   15468             : 
   15469          44 :     CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
   15470             : 
   15471             :     /*
   15472             :      * Update the dependency on the new access method.  No dependency is added
   15473             :      * if the new access method is InvalidOid (default case).  Be very careful
   15474             :      * that this has to compare the previous value stored in pg_class with the
   15475             :      * new one.
   15476             :      */
   15477          44 :     if (!OidIsValid(oldAccessMethodId) && OidIsValid(rd_rel->relam))
   15478          20 :     {
   15479             :         ObjectAddress relobj,
   15480             :                     referenced;
   15481             : 
   15482             :         /*
   15483             :          * New access method is defined and there was no dependency
   15484             :          * previously, so record a new one.
   15485             :          */
   15486          20 :         ObjectAddressSet(relobj, RelationRelationId, reloid);
   15487          20 :         ObjectAddressSet(referenced, AccessMethodRelationId, rd_rel->relam);
   15488          20 :         recordDependencyOn(&relobj, &referenced, DEPENDENCY_NORMAL);
   15489             :     }
   15490          24 :     else if (OidIsValid(oldAccessMethodId) &&
   15491          24 :              !OidIsValid(rd_rel->relam))
   15492             :     {
   15493             :         /*
   15494             :          * There was an access method defined, and no new one, so just remove
   15495             :          * the existing dependency.
   15496             :          */
   15497          12 :         deleteDependencyRecordsForClass(RelationRelationId, reloid,
   15498             :                                         AccessMethodRelationId,
   15499             :                                         DEPENDENCY_NORMAL);
   15500             :     }
   15501             :     else
   15502             :     {
   15503             :         Assert(OidIsValid(oldAccessMethodId) &&
   15504             :                OidIsValid(rd_rel->relam));
   15505             : 
   15506             :         /* Both are valid, so update the dependency */
   15507          12 :         changeDependencyFor(RelationRelationId, reloid,
   15508             :                             AccessMethodRelationId,
   15509             :                             oldAccessMethodId, rd_rel->relam);
   15510             :     }
   15511             : 
   15512             :     /* make the relam and dependency changes visible */
   15513          44 :     CommandCounterIncrement();
   15514             : 
   15515          44 :     InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
   15516             : 
   15517          44 :     heap_freetuple(tuple);
   15518          44 :     table_close(pg_class, RowExclusiveLock);
   15519             : }
   15520             : 
   15521             : /*
   15522             :  * ALTER TABLE SET TABLESPACE
   15523             :  */
   15524             : static void
   15525         158 : ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel, const char *tablespacename, LOCKMODE lockmode)
   15526             : {
   15527             :     Oid         tablespaceId;
   15528             : 
   15529             :     /* Check that the tablespace exists */
   15530         158 :     tablespaceId = get_tablespace_oid(tablespacename, false);
   15531             : 
   15532             :     /* Check permissions except when moving to database's default */
   15533         158 :     if (OidIsValid(tablespaceId) && tablespaceId != MyDatabaseTableSpace)
   15534             :     {
   15535             :         AclResult   aclresult;
   15536             : 
   15537          66 :         aclresult = object_aclcheck(TableSpaceRelationId, tablespaceId, GetUserId(), ACL_CREATE);
   15538          66 :         if (aclresult != ACLCHECK_OK)
   15539           0 :             aclcheck_error(aclresult, OBJECT_TABLESPACE, tablespacename);
   15540             :     }
   15541             : 
   15542             :     /* Save info for Phase 3 to do the real work */
   15543         158 :     if (OidIsValid(tab->newTableSpace))
   15544           0 :         ereport(ERROR,
   15545             :                 (errcode(ERRCODE_SYNTAX_ERROR),
   15546             :                  errmsg("cannot have multiple SET TABLESPACE subcommands")));
   15547             : 
   15548         158 :     tab->newTableSpace = tablespaceId;
   15549         158 : }
   15550             : 
   15551             : /*
   15552             :  * Set, reset, or replace reloptions.
   15553             :  */
   15554             : static void
   15555         934 : ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation,
   15556             :                     LOCKMODE lockmode)
   15557             : {
   15558             :     Oid         relid;
   15559             :     Relation    pgclass;
   15560             :     HeapTuple   tuple;
   15561             :     HeapTuple   newtuple;
   15562             :     Datum       datum;
   15563             :     bool        isnull;
   15564             :     Datum       newOptions;
   15565             :     Datum       repl_val[Natts_pg_class];
   15566             :     bool        repl_null[Natts_pg_class];
   15567             :     bool        repl_repl[Natts_pg_class];
   15568             :     static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
   15569             : 
   15570         934 :     if (defList == NIL && operation != AT_ReplaceRelOptions)
   15571           0 :         return;                 /* nothing to do */
   15572             : 
   15573         934 :     pgclass = table_open(RelationRelationId, RowExclusiveLock);
   15574             : 
   15575             :     /* Fetch heap tuple */
   15576         934 :     relid = RelationGetRelid(rel);
   15577         934 :     tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
   15578         934 :     if (!HeapTupleIsValid(tuple))
   15579           0 :         elog(ERROR, "cache lookup failed for relation %u", relid);
   15580             : 
   15581         934 :     if (operation == AT_ReplaceRelOptions)
   15582             :     {
   15583             :         /*
   15584             :          * If we're supposed to replace the reloptions list, we just pretend
   15585             :          * there were none before.
   15586             :          */
   15587         194 :         datum = (Datum) 0;
   15588         194 :         isnull = true;
   15589             :     }
   15590             :     else
   15591             :     {
   15592             :         /* Get the old reloptions */
   15593         740 :         datum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions,
   15594             :                                 &isnull);
   15595             :     }
   15596             : 
   15597             :     /* Generate new proposed reloptions (text array) */
   15598         934 :     newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
   15599             :                                      defList, NULL, validnsps, false,
   15600             :                                      operation == AT_ResetRelOptions);
   15601             : 
   15602             :     /* Validate */
   15603         928 :     switch (rel->rd_rel->relkind)
   15604             :     {
   15605         512 :         case RELKIND_RELATION:
   15606             :         case RELKIND_TOASTVALUE:
   15607             :         case RELKIND_MATVIEW:
   15608         512 :             (void) heap_reloptions(rel->rd_rel->relkind, newOptions, true);
   15609         512 :             break;
   15610           6 :         case RELKIND_PARTITIONED_TABLE:
   15611           6 :             (void) partitioned_table_reloptions(newOptions, true);
   15612           0 :             break;
   15613         296 :         case RELKIND_VIEW:
   15614         296 :             (void) view_reloptions(newOptions, true);
   15615         278 :             break;
   15616         114 :         case RELKIND_INDEX:
   15617             :         case RELKIND_PARTITIONED_INDEX:
   15618         114 :             (void) index_reloptions(rel->rd_indam->amoptions, newOptions, true);
   15619          92 :             break;
   15620           0 :         default:
   15621           0 :             ereport(ERROR,
   15622             :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   15623             :                      errmsg("cannot set options for relation \"%s\"",
   15624             :                             RelationGetRelationName(rel)),
   15625             :                      errdetail_relkind_not_supported(rel->rd_rel->relkind)));
   15626             :             break;
   15627             :     }
   15628             : 
   15629             :     /* Special-case validation of view options */
   15630         882 :     if (rel->rd_rel->relkind == RELKIND_VIEW)
   15631             :     {
   15632         278 :         Query      *view_query = get_view_query(rel);
   15633         278 :         List       *view_options = untransformRelOptions(newOptions);
   15634             :         ListCell   *cell;
   15635         278 :         bool        check_option = false;
   15636             : 
   15637         380 :         foreach(cell, view_options)
   15638             :         {
   15639         102 :             DefElem    *defel = (DefElem *) lfirst(cell);
   15640             : 
   15641         102 :             if (strcmp(defel->defname, "check_option") == 0)
   15642          24 :                 check_option = true;
   15643             :         }
   15644             : 
   15645             :         /*
   15646             :          * If the check option is specified, look to see if the view is
   15647             :          * actually auto-updatable or not.
   15648             :          */
   15649         278 :         if (check_option)
   15650             :         {
   15651             :             const char *view_updatable_error =
   15652          24 :                 view_query_is_auto_updatable(view_query, true);
   15653             : 
   15654          24 :             if (view_updatable_error)
   15655           0 :                 ereport(ERROR,
   15656             :                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   15657             :                          errmsg("WITH CHECK OPTION is supported only on automatically updatable views"),
   15658             :                          errhint("%s", _(view_updatable_error))));
   15659             :         }
   15660             :     }
   15661             : 
   15662             :     /*
   15663             :      * All we need do here is update the pg_class row; the new options will be
   15664             :      * propagated into relcaches during post-commit cache inval.
   15665             :      */
   15666         882 :     memset(repl_val, 0, sizeof(repl_val));
   15667         882 :     memset(repl_null, false, sizeof(repl_null));
   15668         882 :     memset(repl_repl, false, sizeof(repl_repl));
   15669             : 
   15670         882 :     if (newOptions != (Datum) 0)
   15671         594 :         repl_val[Anum_pg_class_reloptions - 1] = newOptions;
   15672             :     else
   15673         288 :         repl_null[Anum_pg_class_reloptions - 1] = true;
   15674             : 
   15675         882 :     repl_repl[Anum_pg_class_reloptions - 1] = true;
   15676             : 
   15677         882 :     newtuple = heap_modify_tuple(tuple, RelationGetDescr(pgclass),
   15678             :                                  repl_val, repl_null, repl_repl);
   15679             : 
   15680         882 :     CatalogTupleUpdate(pgclass, &newtuple->t_self, newtuple);
   15681             : 
   15682         882 :     InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
   15683             : 
   15684         882 :     heap_freetuple(newtuple);
   15685             : 
   15686         882 :     ReleaseSysCache(tuple);
   15687             : 
   15688             :     /* repeat the whole exercise for the toast table, if there's one */
   15689         882 :     if (OidIsValid(rel->rd_rel->reltoastrelid))
   15690             :     {
   15691             :         Relation    toastrel;
   15692         256 :         Oid         toastid = rel->rd_rel->reltoastrelid;
   15693             : 
   15694         256 :         toastrel = table_open(toastid, lockmode);
   15695             : 
   15696             :         /* Fetch heap tuple */
   15697         256 :         tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(toastid));
   15698         256 :         if (!HeapTupleIsValid(tuple))
   15699           0 :             elog(ERROR, "cache lookup failed for relation %u", toastid);
   15700             : 
   15701         256 :         if (operation == AT_ReplaceRelOptions)
   15702             :         {
   15703             :             /*
   15704             :              * If we're supposed to replace the reloptions list, we just
   15705             :              * pretend there were none before.
   15706             :              */
   15707           0 :             datum = (Datum) 0;
   15708           0 :             isnull = true;
   15709             :         }
   15710             :         else
   15711             :         {
   15712             :             /* Get the old reloptions */
   15713         256 :             datum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions,
   15714             :                                     &isnull);
   15715             :         }
   15716             : 
   15717         256 :         newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
   15718             :                                          defList, "toast", validnsps, false,
   15719             :                                          operation == AT_ResetRelOptions);
   15720             : 
   15721         256 :         (void) heap_reloptions(RELKIND_TOASTVALUE, newOptions, true);
   15722             : 
   15723         256 :         memset(repl_val, 0, sizeof(repl_val));
   15724         256 :         memset(repl_null, false, sizeof(repl_null));
   15725         256 :         memset(repl_repl, false, sizeof(repl_repl));
   15726             : 
   15727         256 :         if (newOptions != (Datum) 0)
   15728          42 :             repl_val[Anum_pg_class_reloptions - 1] = newOptions;
   15729             :         else
   15730         214 :             repl_null[Anum_pg_class_reloptions - 1] = true;
   15731             : 
   15732         256 :         repl_repl[Anum_pg_class_reloptions - 1] = true;
   15733             : 
   15734         256 :         newtuple = heap_modify_tuple(tuple, RelationGetDescr(pgclass),
   15735             :                                      repl_val, repl_null, repl_repl);
   15736             : 
   15737         256 :         CatalogTupleUpdate(pgclass, &newtuple->t_self, newtuple);
   15738             : 
   15739         256 :         InvokeObjectPostAlterHookArg(RelationRelationId,
   15740             :                                      RelationGetRelid(toastrel), 0,
   15741             :                                      InvalidOid, true);
   15742             : 
   15743         256 :         heap_freetuple(newtuple);
   15744             : 
   15745         256 :         ReleaseSysCache(tuple);
   15746             : 
   15747         256 :         table_close(toastrel, NoLock);
   15748             :     }
   15749             : 
   15750         882 :     table_close(pgclass, RowExclusiveLock);
   15751             : }
   15752             : 
   15753             : /*
   15754             :  * Execute ALTER TABLE SET TABLESPACE for cases where there is no tuple
   15755             :  * rewriting to be done, so we just want to copy the data as fast as possible.
   15756             :  */
   15757             : static void
   15758         162 : ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode)
   15759             : {
   15760             :     Relation    rel;
   15761             :     Oid         reltoastrelid;
   15762             :     RelFileNumber newrelfilenumber;
   15763             :     RelFileLocator newrlocator;
   15764         162 :     List       *reltoastidxids = NIL;
   15765             :     ListCell   *lc;
   15766             : 
   15767             :     /*
   15768             :      * Need lock here in case we are recursing to toast table or index
   15769             :      */
   15770         162 :     rel = relation_open(tableOid, lockmode);
   15771             : 
   15772             :     /* Check first if relation can be moved to new tablespace */
   15773         162 :     if (!CheckRelationTableSpaceMove(rel, newTableSpace))
   15774             :     {
   15775           2 :         InvokeObjectPostAlterHook(RelationRelationId,
   15776             :                                   RelationGetRelid(rel), 0);
   15777           2 :         relation_close(rel, NoLock);
   15778           2 :         return;
   15779             :     }
   15780             : 
   15781         160 :     reltoastrelid = rel->rd_rel->reltoastrelid;
   15782             :     /* Fetch the list of indexes on toast relation if necessary */
   15783         160 :     if (OidIsValid(reltoastrelid))
   15784             :     {
   15785          20 :         Relation    toastRel = relation_open(reltoastrelid, lockmode);
   15786             : 
   15787          20 :         reltoastidxids = RelationGetIndexList(toastRel);
   15788          20 :         relation_close(toastRel, lockmode);
   15789             :     }
   15790             : 
   15791             :     /*
   15792             :      * Relfilenumbers are not unique in databases across tablespaces, so we
   15793             :      * need to allocate a new one in the new tablespace.
   15794             :      */
   15795         160 :     newrelfilenumber = GetNewRelFileNumber(newTableSpace, NULL,
   15796         160 :                                            rel->rd_rel->relpersistence);
   15797             : 
   15798             :     /* Open old and new relation */
   15799         160 :     newrlocator = rel->rd_locator;
   15800         160 :     newrlocator.relNumber = newrelfilenumber;
   15801         160 :     newrlocator.spcOid = newTableSpace;
   15802             : 
   15803             :     /* hand off to AM to actually create new rel storage and copy the data */
   15804         160 :     if (rel->rd_rel->relkind == RELKIND_INDEX)
   15805             :     {
   15806          62 :         index_copy_data(rel, newrlocator);
   15807             :     }
   15808             :     else
   15809             :     {
   15810             :         Assert(RELKIND_HAS_TABLE_AM(rel->rd_rel->relkind));
   15811          98 :         table_relation_copy_data(rel, &newrlocator);
   15812             :     }
   15813             : 
   15814             :     /*
   15815             :      * Update the pg_class row.
   15816             :      *
   15817             :      * NB: This wouldn't work if ATExecSetTableSpace() were allowed to be
   15818             :      * executed on pg_class or its indexes (the above copy wouldn't contain
   15819             :      * the updated pg_class entry), but that's forbidden with
   15820             :      * CheckRelationTableSpaceMove().
   15821             :      */
   15822         160 :     SetRelationTableSpace(rel, newTableSpace, newrelfilenumber);
   15823             : 
   15824         160 :     InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
   15825             : 
   15826         160 :     RelationAssumeNewRelfilelocator(rel);
   15827             : 
   15828         160 :     relation_close(rel, NoLock);
   15829             : 
   15830             :     /* Make sure the reltablespace change is visible */
   15831         160 :     CommandCounterIncrement();
   15832             : 
   15833             :     /* Move associated toast relation and/or indexes, too */
   15834         160 :     if (OidIsValid(reltoastrelid))
   15835          20 :         ATExecSetTableSpace(reltoastrelid, newTableSpace, lockmode);
   15836         180 :     foreach(lc, reltoastidxids)
   15837          20 :         ATExecSetTableSpace(lfirst_oid(lc), newTableSpace, lockmode);
   15838             : 
   15839             :     /* Clean up */
   15840         160 :     list_free(reltoastidxids);
   15841             : }
   15842             : 
   15843             : /*
   15844             :  * Special handling of ALTER TABLE SET TABLESPACE for relations with no
   15845             :  * storage that have an interest in preserving tablespace.
   15846             :  *
   15847             :  * Since these have no storage the tablespace can be updated with a simple
   15848             :  * metadata only operation to update the tablespace.
   15849             :  */
   15850             : static void
   15851          36 : ATExecSetTableSpaceNoStorage(Relation rel, Oid newTableSpace)
   15852             : {
   15853             :     /*
   15854             :      * Shouldn't be called on relations having storage; these are processed in
   15855             :      * phase 3.
   15856             :      */
   15857             :     Assert(!RELKIND_HAS_STORAGE(rel->rd_rel->relkind));
   15858             : 
   15859             :     /* check if relation can be moved to its new tablespace */
   15860          36 :     if (!CheckRelationTableSpaceMove(rel, newTableSpace))
   15861             :     {
   15862           0 :         InvokeObjectPostAlterHook(RelationRelationId,
   15863             :                                   RelationGetRelid(rel),
   15864             :                                   0);
   15865           0 :         return;
   15866             :     }
   15867             : 
   15868             :     /* Update can be done, so change reltablespace */
   15869          30 :     SetRelationTableSpace(rel, newTableSpace, InvalidOid);
   15870             : 
   15871          30 :     InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
   15872             : 
   15873             :     /* Make sure the reltablespace change is visible */
   15874          30 :     CommandCounterIncrement();
   15875             : }
   15876             : 
   15877             : /*
   15878             :  * Alter Table ALL ... SET TABLESPACE
   15879             :  *
   15880             :  * Allows a user to move all objects of some type in a given tablespace in the
   15881             :  * current database to another tablespace.  Objects can be chosen based on the
   15882             :  * owner of the object also, to allow users to move only their objects.
   15883             :  * The user must have CREATE rights on the new tablespace, as usual.   The main
   15884             :  * permissions handling is done by the lower-level table move function.
   15885             :  *
   15886             :  * All to-be-moved objects are locked first. If NOWAIT is specified and the
   15887             :  * lock can't be acquired then we ereport(ERROR).
   15888             :  */
   15889             : Oid
   15890          30 : AlterTableMoveAll(AlterTableMoveAllStmt *stmt)
   15891             : {
   15892          30 :     List       *relations = NIL;
   15893             :     ListCell   *l;
   15894             :     ScanKeyData key[1];
   15895             :     Relation    rel;
   15896             :     TableScanDesc scan;
   15897             :     HeapTuple   tuple;
   15898             :     Oid         orig_tablespaceoid;
   15899             :     Oid         new_tablespaceoid;
   15900          30 :     List       *role_oids = roleSpecsToIds(stmt->roles);
   15901             : 
   15902             :     /* Ensure we were not asked to move something we can't */
   15903          30 :     if (stmt->objtype != OBJECT_TABLE && stmt->objtype != OBJECT_INDEX &&
   15904          12 :         stmt->objtype != OBJECT_MATVIEW)
   15905           0 :         ereport(ERROR,
   15906             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
   15907             :                  errmsg("only tables, indexes, and materialized views exist in tablespaces")));
   15908             : 
   15909             :     /* Get the orig and new tablespace OIDs */
   15910          30 :     orig_tablespaceoid = get_tablespace_oid(stmt->orig_tablespacename, false);
   15911          30 :     new_tablespaceoid = get_tablespace_oid(stmt->new_tablespacename, false);
   15912             : 
   15913             :     /* Can't move shared relations in to or out of pg_global */
   15914             :     /* This is also checked by ATExecSetTableSpace, but nice to stop earlier */
   15915          30 :     if (orig_tablespaceoid == GLOBALTABLESPACE_OID ||
   15916             :         new_tablespaceoid == GLOBALTABLESPACE_OID)
   15917           0 :         ereport(ERROR,
   15918             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
   15919             :                  errmsg("cannot move relations in to or out of pg_global tablespace")));
   15920             : 
   15921             :     /*
   15922             :      * Must have CREATE rights on the new tablespace, unless it is the
   15923             :      * database default tablespace (which all users implicitly have CREATE
   15924             :      * rights on).
   15925             :      */
   15926          30 :     if (OidIsValid(new_tablespaceoid) && new_tablespaceoid != MyDatabaseTableSpace)
   15927             :     {
   15928             :         AclResult   aclresult;
   15929             : 
   15930           0 :         aclresult = object_aclcheck(TableSpaceRelationId, new_tablespaceoid, GetUserId(),
   15931             :                                     ACL_CREATE);
   15932           0 :         if (aclresult != ACLCHECK_OK)
   15933           0 :             aclcheck_error(aclresult, OBJECT_TABLESPACE,
   15934           0 :                            get_tablespace_name(new_tablespaceoid));
   15935             :     }
   15936             : 
   15937             :     /*
   15938             :      * Now that the checks are done, check if we should set either to
   15939             :      * InvalidOid because it is our database's default tablespace.
   15940             :      */
   15941          30 :     if (orig_tablespaceoid == MyDatabaseTableSpace)
   15942           0 :         orig_tablespaceoid = InvalidOid;
   15943             : 
   15944          30 :     if (new_tablespaceoid == MyDatabaseTableSpace)
   15945          30 :         new_tablespaceoid = InvalidOid;
   15946             : 
   15947             :     /* no-op */
   15948          30 :     if (orig_tablespaceoid == new_tablespaceoid)
   15949           0 :         return new_tablespaceoid;
   15950             : 
   15951             :     /*
   15952             :      * Walk the list of objects in the tablespace and move them. This will
   15953             :      * only find objects in our database, of course.
   15954             :      */
   15955          30 :     ScanKeyInit(&key[0],
   15956             :                 Anum_pg_class_reltablespace,
   15957             :                 BTEqualStrategyNumber, F_OIDEQ,
   15958             :                 ObjectIdGetDatum(orig_tablespaceoid));
   15959             : 
   15960          30 :     rel = table_open(RelationRelationId, AccessShareLock);
   15961          30 :     scan = table_beginscan_catalog(rel, 1, key);
   15962         132 :     while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
   15963             :     {
   15964         102 :         Form_pg_class relForm = (Form_pg_class) GETSTRUCT(tuple);
   15965         102 :         Oid         relOid = relForm->oid;
   15966             : 
   15967             :         /*
   15968             :          * Do not move objects in pg_catalog as part of this, if an admin
   15969             :          * really wishes to do so, they can issue the individual ALTER
   15970             :          * commands directly.
   15971             :          *
   15972             :          * Also, explicitly avoid any shared tables, temp tables, or TOAST
   15973             :          * (TOAST will be moved with the main table).
   15974             :          */
   15975         102 :         if (IsCatalogNamespace(relForm->relnamespace) ||
   15976         204 :             relForm->relisshared ||
   15977         204 :             isAnyTempNamespace(relForm->relnamespace) ||
   15978         102 :             IsToastNamespace(relForm->relnamespace))
   15979           0 :             continue;
   15980             : 
   15981             :         /* Only move the object type requested */
   15982         102 :         if ((stmt->objtype == OBJECT_TABLE &&
   15983          60 :              relForm->relkind != RELKIND_RELATION &&
   15984          36 :              relForm->relkind != RELKIND_PARTITIONED_TABLE) ||
   15985          66 :             (stmt->objtype == OBJECT_INDEX &&
   15986          36 :              relForm->relkind != RELKIND_INDEX &&
   15987           6 :              relForm->relkind != RELKIND_PARTITIONED_INDEX) ||
   15988          60 :             (stmt->objtype == OBJECT_MATVIEW &&
   15989           6 :              relForm->relkind != RELKIND_MATVIEW))
   15990          42 :             continue;
   15991             : 
   15992             :         /* Check if we are only moving objects owned by certain roles */
   15993          60 :         if (role_oids != NIL && !list_member_oid(role_oids, relForm->relowner))
   15994           0 :             continue;
   15995             : 
   15996             :         /*
   15997             :          * Handle permissions-checking here since we are locking the tables
   15998             :          * and also to avoid doing a bunch of work only to fail part-way. Note
   15999             :          * that permissions will also be checked by AlterTableInternal().
   16000             :          *
   16001             :          * Caller must be considered an owner on the table to move it.
   16002             :          */
   16003          60 :         if (!object_ownercheck(RelationRelationId, relOid, GetUserId()))
   16004           0 :             aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relOid)),
   16005           0 :                            NameStr(relForm->relname));
   16006             : 
   16007          60 :         if (stmt->nowait &&
   16008           0 :             !ConditionalLockRelationOid(relOid, AccessExclusiveLock))
   16009           0 :             ereport(ERROR,
   16010             :                     (errcode(ERRCODE_OBJECT_IN_USE),
   16011             :                      errmsg("aborting because lock on relation \"%s.%s\" is not available",
   16012             :                             get_namespace_name(relForm->relnamespace),
   16013             :                             NameStr(relForm->relname))));
   16014             :         else
   16015          60 :             LockRelationOid(relOid, AccessExclusiveLock);
   16016             : 
   16017             :         /* Add to our list of objects to move */
   16018          60 :         relations = lappend_oid(relations, relOid);
   16019             :     }
   16020             : 
   16021          30 :     table_endscan(scan);
   16022          30 :     table_close(rel, AccessShareLock);
   16023             : 
   16024          30 :     if (relations == NIL)
   16025          12 :         ereport(NOTICE,
   16026             :                 (errcode(ERRCODE_NO_DATA_FOUND),
   16027             :                  errmsg("no matching relations in tablespace \"%s\" found",
   16028             :                         orig_tablespaceoid == InvalidOid ? "(database default)" :
   16029             :                         get_tablespace_name(orig_tablespaceoid))));
   16030             : 
   16031             :     /* Everything is locked, loop through and move all of the relations. */
   16032          90 :     foreach(l, relations)
   16033             :     {
   16034          60 :         List       *cmds = NIL;
   16035          60 :         AlterTableCmd *cmd = makeNode(AlterTableCmd);
   16036             : 
   16037          60 :         cmd->subtype = AT_SetTableSpace;
   16038          60 :         cmd->name = stmt->new_tablespacename;
   16039             : 
   16040          60 :         cmds = lappend(cmds, cmd);
   16041             : 
   16042          60 :         EventTriggerAlterTableStart((Node *) stmt);
   16043             :         /* OID is set by AlterTableInternal */
   16044          60 :         AlterTableInternal(lfirst_oid(l), cmds, false);
   16045          60 :         EventTriggerAlterTableEnd();
   16046             :     }
   16047             : 
   16048          30 :     return new_tablespaceoid;
   16049             : }
   16050             : 
   16051             : static void
   16052          62 : index_copy_data(Relation rel, RelFileLocator newrlocator)
   16053             : {
   16054             :     SMgrRelation dstrel;
   16055             : 
   16056             :     /*
   16057             :      * Since we copy the file directly without looking at the shared buffers,
   16058             :      * we'd better first flush out any pages of the source relation that are
   16059             :      * in shared buffers.  We assume no new changes will be made while we are
   16060             :      * holding exclusive lock on the rel.
   16061             :      */
   16062          62 :     FlushRelationBuffers(rel);
   16063             : 
   16064             :     /*
   16065             :      * Create and copy all forks of the relation, and schedule unlinking of
   16066             :      * old physical files.
   16067             :      *
   16068             :      * NOTE: any conflict in relfilenumber value will be caught in
   16069             :      * RelationCreateStorage().
   16070             :      */
   16071          62 :     dstrel = RelationCreateStorage(newrlocator, rel->rd_rel->relpersistence, true);
   16072             : 
   16073             :     /* copy main fork */
   16074          62 :     RelationCopyStorage(RelationGetSmgr(rel), dstrel, MAIN_FORKNUM,
   16075          62 :                         rel->rd_rel->relpersistence);
   16076             : 
   16077             :     /* copy those extra forks that exist */
   16078         248 :     for (ForkNumber forkNum = MAIN_FORKNUM + 1;
   16079         186 :          forkNum <= MAX_FORKNUM; forkNum++)
   16080             :     {
   16081         186 :         if (smgrexists(RelationGetSmgr(rel), forkNum))
   16082             :         {
   16083           0 :             smgrcreate(dstrel, forkNum, false);
   16084             : 
   16085             :             /*
   16086             :              * WAL log creation if the relation is persistent, or this is the
   16087             :              * init fork of an unlogged relation.
   16088             :              */
   16089           0 :             if (RelationIsPermanent(rel) ||
   16090           0 :                 (rel->rd_rel->relpersistence == RELPERSISTENCE_UNLOGGED &&
   16091             :                  forkNum == INIT_FORKNUM))
   16092           0 :                 log_smgrcreate(&newrlocator, forkNum);
   16093           0 :             RelationCopyStorage(RelationGetSmgr(rel), dstrel, forkNum,
   16094           0 :                                 rel->rd_rel->relpersistence);
   16095             :         }
   16096             :     }
   16097             : 
   16098             :     /* drop old relation, and close new one */
   16099          62 :     RelationDropStorage(rel);
   16100          62 :     smgrclose(dstrel);
   16101          62 : }
   16102             : 
   16103             : /*
   16104             :  * ALTER TABLE ENABLE/DISABLE TRIGGER
   16105             :  *
   16106             :  * We just pass this off to trigger.c.
   16107             :  */
   16108             : static void
   16109         340 : ATExecEnableDisableTrigger(Relation rel, const char *trigname,
   16110             :                            char fires_when, bool skip_system, bool recurse,
   16111             :                            LOCKMODE lockmode)
   16112             : {
   16113         340 :     EnableDisableTrigger(rel, trigname, InvalidOid,
   16114             :                          fires_when, skip_system, recurse,
   16115             :                          lockmode);
   16116             : 
   16117         340 :     InvokeObjectPostAlterHook(RelationRelationId,
   16118             :                               RelationGetRelid(rel), 0);
   16119         340 : }
   16120             : 
   16121             : /*
   16122             :  * ALTER TABLE ENABLE/DISABLE RULE
   16123             :  *
   16124             :  * We just pass this off to rewriteDefine.c.
   16125             :  */
   16126             : static void
   16127          46 : ATExecEnableDisableRule(Relation rel, const char *rulename,
   16128             :                         char fires_when, LOCKMODE lockmode)
   16129             : {
   16130          46 :     EnableDisableRule(rel, rulename, fires_when);
   16131             : 
   16132          46 :     InvokeObjectPostAlterHook(RelationRelationId,
   16133             :                               RelationGetRelid(rel), 0);
   16134          46 : }
   16135             : 
   16136             : /*
   16137             :  * ALTER TABLE INHERIT
   16138             :  *
   16139             :  * Add a parent to the child's parents. This verifies that all the columns and
   16140             :  * check constraints of the parent appear in the child and that they have the
   16141             :  * same data types and expressions.
   16142             :  */
   16143             : static void
   16144         338 : ATPrepAddInherit(Relation child_rel)
   16145             : {
   16146         338 :     if (child_rel->rd_rel->reloftype)
   16147           6 :         ereport(ERROR,
   16148             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   16149             :                  errmsg("cannot change inheritance of typed table")));
   16150             : 
   16151         332 :     if (child_rel->rd_rel->relispartition)
   16152           6 :         ereport(ERROR,
   16153             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   16154             :                  errmsg("cannot change inheritance of a partition")));
   16155             : 
   16156         326 :     if (child_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
   16157           6 :         ereport(ERROR,
   16158             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   16159             :                  errmsg("cannot change inheritance of partitioned table")));
   16160         320 : }
   16161             : 
   16162             : /*
   16163             :  * Return the address of the new parent relation.
   16164             :  */
   16165             : static ObjectAddress
   16166         320 : ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode)
   16167             : {
   16168             :     Relation    parent_rel;
   16169             :     List       *children;
   16170             :     ObjectAddress address;
   16171             :     const char *trigger_name;
   16172             : 
   16173             :     /*
   16174             :      * A self-exclusive lock is needed here.  See the similar case in
   16175             :      * MergeAttributes() for a full explanation.
   16176             :      */
   16177         320 :     parent_rel = table_openrv(parent, ShareUpdateExclusiveLock);
   16178             : 
   16179             :     /*
   16180             :      * Must be owner of both parent and child -- child was checked by
   16181             :      * ATSimplePermissions call in ATPrepCmd
   16182             :      */
   16183         320 :     ATSimplePermissions(AT_AddInherit, parent_rel, ATT_TABLE | ATT_FOREIGN_TABLE);
   16184             : 
   16185             :     /* Permanent rels cannot inherit from temporary ones */
   16186         320 :     if (parent_rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
   16187           6 :         child_rel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
   16188           0 :         ereport(ERROR,
   16189             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   16190             :                  errmsg("cannot inherit from temporary relation \"%s\"",
   16191             :                         RelationGetRelationName(parent_rel))));
   16192             : 
   16193             :     /* If parent rel is temp, it must belong to this session */
   16194         320 :     if (parent_rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
   16195           6 :         !parent_rel->rd_islocaltemp)
   16196           0 :         ereport(ERROR,
   16197             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   16198             :                  errmsg("cannot inherit from temporary relation of another session")));
   16199             : 
   16200             :     /* Ditto for the child */
   16201         320 :     if (child_rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
   16202           6 :         !child_rel->rd_islocaltemp)
   16203           0 :         ereport(ERROR,
   16204             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   16205             :                  errmsg("cannot inherit to temporary relation of another session")));
   16206             : 
   16207             :     /* Prevent partitioned tables from becoming inheritance parents */
   16208         320 :     if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
   16209           6 :         ereport(ERROR,
   16210             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   16211             :                  errmsg("cannot inherit from partitioned table \"%s\"",
   16212             :                         parent->relname)));
   16213             : 
   16214             :     /* Likewise for partitions */
   16215         314 :     if (parent_rel->rd_rel->relispartition)
   16216           6 :         ereport(ERROR,
   16217             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   16218             :                  errmsg("cannot inherit from a partition")));
   16219             : 
   16220             :     /*
   16221             :      * Prevent circularity by seeing if proposed parent inherits from child.
   16222             :      * (In particular, this disallows making a rel inherit from itself.)
   16223             :      *
   16224             :      * This is not completely bulletproof because of race conditions: in
   16225             :      * multi-level inheritance trees, someone else could concurrently be
   16226             :      * making another inheritance link that closes the loop but does not join
   16227             :      * either of the rels we have locked.  Preventing that seems to require
   16228             :      * exclusive locks on the entire inheritance tree, which is a cure worse
   16229             :      * than the disease.  find_all_inheritors() will cope with circularity
   16230             :      * anyway, so don't sweat it too much.
   16231             :      *
   16232             :      * We use weakest lock we can on child's children, namely AccessShareLock.
   16233             :      */
   16234         308 :     children = find_all_inheritors(RelationGetRelid(child_rel),
   16235             :                                    AccessShareLock, NULL);
   16236             : 
   16237         308 :     if (list_member_oid(children, RelationGetRelid(parent_rel)))
   16238          12 :         ereport(ERROR,
   16239             :                 (errcode(ERRCODE_DUPLICATE_TABLE),
   16240             :                  errmsg("circular inheritance not allowed"),
   16241             :                  errdetail("\"%s\" is already a child of \"%s\".",
   16242             :                            parent->relname,
   16243             :                            RelationGetRelationName(child_rel))));
   16244             : 
   16245             :     /*
   16246             :      * If child_rel has row-level triggers with transition tables, we
   16247             :      * currently don't allow it to become an inheritance child.  See also
   16248             :      * prohibitions in ATExecAttachPartition() and CreateTrigger().
   16249             :      */
   16250         296 :     trigger_name = FindTriggerIncompatibleWithInheritance(child_rel->trigdesc);
   16251         296 :     if (trigger_name != NULL)
   16252           6 :         ereport(ERROR,
   16253             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   16254             :                  errmsg("trigger \"%s\" prevents table \"%s\" from becoming an inheritance child",
   16255             :                         trigger_name, RelationGetRelationName(child_rel)),
   16256             :                  errdetail("ROW triggers with transition tables are not supported in inheritance hierarchies.")));
   16257             : 
   16258             :     /* OK to create inheritance */
   16259         290 :     CreateInheritance(child_rel, parent_rel, false);
   16260             : 
   16261             :     /*
   16262             :      * If parent_rel has a primary key, then child_rel has not-null
   16263             :      * constraints that make these columns as non nullable.  Make those
   16264             :      * constraints as inherited.
   16265             :      */
   16266         236 :     ATInheritAdjustNotNulls(parent_rel, child_rel, 1);
   16267             : 
   16268         230 :     ObjectAddressSet(address, RelationRelationId,
   16269             :                      RelationGetRelid(parent_rel));
   16270             : 
   16271             :     /* keep our lock on the parent relation until commit */
   16272         230 :     table_close(parent_rel, NoLock);
   16273             : 
   16274         230 :     return address;
   16275             : }
   16276             : 
   16277             : /*
   16278             :  * CreateInheritance
   16279             :  *      Catalog manipulation portion of creating inheritance between a child
   16280             :  *      table and a parent table.
   16281             :  *
   16282             :  * Common to ATExecAddInherit() and ATExecAttachPartition().
   16283             :  */
   16284             : static void
   16285        2742 : CreateInheritance(Relation child_rel, Relation parent_rel, bool ispartition)
   16286             : {
   16287             :     Relation    catalogRelation;
   16288             :     SysScanDesc scan;
   16289             :     ScanKeyData key;
   16290             :     HeapTuple   inheritsTuple;
   16291             :     int32       inhseqno;
   16292             : 
   16293             :     /* Note: get RowExclusiveLock because we will write pg_inherits below. */
   16294        2742 :     catalogRelation = table_open(InheritsRelationId, RowExclusiveLock);
   16295             : 
   16296             :     /*
   16297             :      * Check for duplicates in the list of parents, and determine the highest
   16298             :      * inhseqno already present; we'll use the next one for the new parent.
   16299             :      * Also, if proposed child is a partition, it cannot already be
   16300             :      * inheriting.
   16301             :      *
   16302             :      * Note: we do not reject the case where the child already inherits from
   16303             :      * the parent indirectly; CREATE TABLE doesn't reject comparable cases.
   16304             :      */
   16305        2742 :     ScanKeyInit(&key,
   16306             :                 Anum_pg_inherits_inhrelid,
   16307             :                 BTEqualStrategyNumber, F_OIDEQ,
   16308             :                 ObjectIdGetDatum(RelationGetRelid(child_rel)));
   16309        2742 :     scan = systable_beginscan(catalogRelation, InheritsRelidSeqnoIndexId,
   16310             :                               true, NULL, 1, &key);
   16311             : 
   16312             :     /* inhseqno sequences start at 1 */
   16313        2742 :     inhseqno = 0;
   16314        2788 :     while (HeapTupleIsValid(inheritsTuple = systable_getnext(scan)))
   16315             :     {
   16316          52 :         Form_pg_inherits inh = (Form_pg_inherits) GETSTRUCT(inheritsTuple);
   16317             : 
   16318          52 :         if (inh->inhparent == RelationGetRelid(parent_rel))
   16319           6 :             ereport(ERROR,
   16320             :                     (errcode(ERRCODE_DUPLICATE_TABLE),
   16321             :                      errmsg("relation \"%s\" would be inherited from more than once",
   16322             :                             RelationGetRelationName(parent_rel))));
   16323             : 
   16324          46 :         if (inh->inhseqno > inhseqno)
   16325          46 :             inhseqno = inh->inhseqno;
   16326             :     }
   16327        2736 :     systable_endscan(scan);
   16328             : 
   16329             :     /* Match up the columns and bump attinhcount as needed */
   16330        2736 :     MergeAttributesIntoExisting(child_rel, parent_rel, ispartition);
   16331             : 
   16332             :     /* Match up the constraints and bump coninhcount as needed */
   16333        2658 :     MergeConstraintsIntoExisting(child_rel, parent_rel);
   16334             : 
   16335             :     /*
   16336             :      * OK, it looks valid.  Make the catalog entries that show inheritance.
   16337             :      */
   16338        2616 :     StoreCatalogInheritance1(RelationGetRelid(child_rel),
   16339             :                              RelationGetRelid(parent_rel),
   16340             :                              inhseqno + 1,
   16341             :                              catalogRelation,
   16342        2616 :                              parent_rel->rd_rel->relkind ==
   16343             :                              RELKIND_PARTITIONED_TABLE);
   16344             : 
   16345             :     /* Now we're done with pg_inherits */
   16346        2616 :     table_close(catalogRelation, RowExclusiveLock);
   16347        2616 : }
   16348             : 
   16349             : /*
   16350             :  * Obtain the source-text form of the constraint expression for a check
   16351             :  * constraint, given its pg_constraint tuple
   16352             :  */
   16353             : static char *
   16354         168 : decompile_conbin(HeapTuple contup, TupleDesc tupdesc)
   16355             : {
   16356             :     Form_pg_constraint con;
   16357             :     bool        isnull;
   16358             :     Datum       attr;
   16359             :     Datum       expr;
   16360             : 
   16361         168 :     con = (Form_pg_constraint) GETSTRUCT(contup);
   16362         168 :     attr = heap_getattr(contup, Anum_pg_constraint_conbin, tupdesc, &isnull);
   16363         168 :     if (isnull)
   16364           0 :         elog(ERROR, "null conbin for constraint %u", con->oid);
   16365             : 
   16366         168 :     expr = DirectFunctionCall2(pg_get_expr, attr,
   16367             :                                ObjectIdGetDatum(con->conrelid));
   16368         168 :     return TextDatumGetCString(expr);
   16369             : }
   16370             : 
   16371             : /*
   16372             :  * Determine whether two check constraints are functionally equivalent
   16373             :  *
   16374             :  * The test we apply is to see whether they reverse-compile to the same
   16375             :  * source string.  This insulates us from issues like whether attributes
   16376             :  * have the same physical column numbers in parent and child relations.
   16377             :  */
   16378             : static bool
   16379          84 : constraints_equivalent(HeapTuple a, HeapTuple b, TupleDesc tupleDesc)
   16380             : {
   16381          84 :     Form_pg_constraint acon = (Form_pg_constraint) GETSTRUCT(a);
   16382          84 :     Form_pg_constraint bcon = (Form_pg_constraint) GETSTRUCT(b);
   16383             : 
   16384          84 :     if (acon->condeferrable != bcon->condeferrable ||
   16385          84 :         acon->condeferred != bcon->condeferred ||
   16386          84 :         strcmp(decompile_conbin(a, tupleDesc),
   16387          84 :                decompile_conbin(b, tupleDesc)) != 0)
   16388           6 :         return false;
   16389             :     else
   16390          78 :         return true;
   16391             : }
   16392             : 
   16393             : /*
   16394             :  * Check columns in child table match up with columns in parent, and increment
   16395             :  * their attinhcount.
   16396             :  *
   16397             :  * Called by CreateInheritance
   16398             :  *
   16399             :  * Currently all parent columns must be found in child. Missing columns are an
   16400             :  * error.  One day we might consider creating new columns like CREATE TABLE
   16401             :  * does.  However, that is widely unpopular --- in the common use case of
   16402             :  * partitioned tables it's a foot-gun.
   16403             :  *
   16404             :  * The data type must match exactly. If the parent column is NOT NULL then
   16405             :  * the child must be as well. Defaults are not compared, however.
   16406             :  */
   16407             : static void
   16408        2736 : MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel, bool ispartition)
   16409             : {
   16410             :     Relation    attrrel;
   16411             :     TupleDesc   parent_desc;
   16412             : 
   16413        2736 :     attrrel = table_open(AttributeRelationId, RowExclusiveLock);
   16414        2736 :     parent_desc = RelationGetDescr(parent_rel);
   16415             : 
   16416        9364 :     for (AttrNumber parent_attno = 1; parent_attno <= parent_desc->natts; parent_attno++)
   16417             :     {
   16418        6706 :         Form_pg_attribute parent_att = TupleDescAttr(parent_desc, parent_attno - 1);
   16419        6706 :         char       *parent_attname = NameStr(parent_att->attname);
   16420             :         HeapTuple   tuple;
   16421             : 
   16422             :         /* Ignore dropped columns in the parent. */
   16423        6706 :         if (parent_att->attisdropped)
   16424         296 :             continue;
   16425             : 
   16426             :         /* Find same column in child (matching on column name). */
   16427        6410 :         tuple = SearchSysCacheCopyAttName(RelationGetRelid(child_rel), parent_attname);
   16428        6410 :         if (HeapTupleIsValid(tuple))
   16429             :         {
   16430        6398 :             Form_pg_attribute child_att = (Form_pg_attribute) GETSTRUCT(tuple);
   16431             : 
   16432        6398 :             if (parent_att->atttypid != child_att->atttypid ||
   16433        6392 :                 parent_att->atttypmod != child_att->atttypmod)
   16434          12 :                 ereport(ERROR,
   16435             :                         (errcode(ERRCODE_DATATYPE_MISMATCH),
   16436             :                          errmsg("child table \"%s\" has different type for column \"%s\"",
   16437             :                                 RelationGetRelationName(child_rel), parent_attname)));
   16438             : 
   16439        6386 :             if (parent_att->attcollation != child_att->attcollation)
   16440           6 :                 ereport(ERROR,
   16441             :                         (errcode(ERRCODE_COLLATION_MISMATCH),
   16442             :                          errmsg("child table \"%s\" has different collation for column \"%s\"",
   16443             :                                 RelationGetRelationName(child_rel), parent_attname)));
   16444             : 
   16445             :             /*
   16446             :              * If the parent has a not-null constraint that's not NO INHERIT,
   16447             :              * make sure the child has one too.
   16448             :              *
   16449             :              * Other constraints are checked elsewhere.
   16450             :              */
   16451        6380 :             if (parent_att->attnotnull && !child_att->attnotnull)
   16452             :             {
   16453             :                 HeapTuple   contup;
   16454             : 
   16455          32 :                 contup = findNotNullConstraintAttnum(RelationGetRelid(parent_rel),
   16456          32 :                                                      parent_att->attnum);
   16457          32 :                 if (HeapTupleIsValid(contup) &&
   16458          20 :                     !((Form_pg_constraint) GETSTRUCT(contup))->connoinherit)
   16459          18 :                     ereport(ERROR,
   16460             :                             errcode(ERRCODE_DATATYPE_MISMATCH),
   16461             :                             errmsg("column \"%s\" in child table must be marked NOT NULL",
   16462             :                                    parent_attname));
   16463             :             }
   16464             : 
   16465             :             /*
   16466             :              * Child column must be generated if and only if parent column is.
   16467             :              */
   16468        6362 :             if (parent_att->attgenerated && !child_att->attgenerated)
   16469          18 :                 ereport(ERROR,
   16470             :                         (errcode(ERRCODE_DATATYPE_MISMATCH),
   16471             :                          errmsg("column \"%s\" in child table must be a generated column", parent_attname)));
   16472        6344 :             if (child_att->attgenerated && !parent_att->attgenerated)
   16473          12 :                 ereport(ERROR,
   16474             :                         (errcode(ERRCODE_DATATYPE_MISMATCH),
   16475             :                          errmsg("column \"%s\" in child table must not be a generated column", parent_attname)));
   16476             : 
   16477             :             /*
   16478             :              * Regular inheritance children are independent enough not to
   16479             :              * inherit identity columns.  But partitions are integral part of
   16480             :              * a partitioned table and inherit identity column.
   16481             :              */
   16482        6332 :             if (ispartition)
   16483        5796 :                 child_att->attidentity = parent_att->attidentity;
   16484             : 
   16485             :             /*
   16486             :              * OK, bump the child column's inheritance count.  (If we fail
   16487             :              * later on, this change will just roll back.)
   16488             :              */
   16489        6332 :             child_att->attinhcount++;
   16490        6332 :             if (child_att->attinhcount < 0)
   16491           0 :                 ereport(ERROR,
   16492             :                         errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
   16493             :                         errmsg("too many inheritance parents"));
   16494             : 
   16495             :             /*
   16496             :              * In case of partitions, we must enforce that value of attislocal
   16497             :              * is same in all partitions. (Note: there are only inherited
   16498             :              * attributes in partitions)
   16499             :              */
   16500        6332 :             if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
   16501             :             {
   16502             :                 Assert(child_att->attinhcount == 1);
   16503        5796 :                 child_att->attislocal = false;
   16504             :             }
   16505             : 
   16506        6332 :             CatalogTupleUpdate(attrrel, &tuple->t_self, tuple);
   16507        6332 :             heap_freetuple(tuple);
   16508             :         }
   16509             :         else
   16510             :         {
   16511          12 :             ereport(ERROR,
   16512             :                     (errcode(ERRCODE_DATATYPE_MISMATCH),
   16513             :                      errmsg("child table is missing column \"%s\"", parent_attname)));
   16514             :         }
   16515             :     }
   16516             : 
   16517        2658 :     table_close(attrrel, RowExclusiveLock);
   16518        2658 : }
   16519             : 
   16520             : /*
   16521             :  * Check constraints in child table match up with constraints in parent,
   16522             :  * and increment their coninhcount.
   16523             :  *
   16524             :  * Constraints that are marked ONLY in the parent are ignored.
   16525             :  *
   16526             :  * Called by CreateInheritance
   16527             :  *
   16528             :  * Currently all constraints in parent must be present in the child. One day we
   16529             :  * may consider adding new constraints like CREATE TABLE does.
   16530             :  *
   16531             :  * XXX This is O(N^2) which may be an issue with tables with hundreds of
   16532             :  * constraints. As long as tables have more like 10 constraints it shouldn't be
   16533             :  * a problem though. Even 100 constraints ought not be the end of the world.
   16534             :  *
   16535             :  * XXX See MergeWithExistingConstraint too if you change this code.
   16536             :  */
   16537             : static void
   16538        2658 : MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel)
   16539             : {
   16540             :     Relation    constraintrel;
   16541             :     SysScanDesc parent_scan;
   16542             :     ScanKeyData parent_key;
   16543             :     HeapTuple   parent_tuple;
   16544        2658 :     Oid         parent_relid = RelationGetRelid(parent_rel);
   16545             : 
   16546        2658 :     constraintrel = table_open(ConstraintRelationId, RowExclusiveLock);
   16547             : 
   16548             :     /* Outer loop scans through the parent's constraint definitions */
   16549        2658 :     ScanKeyInit(&parent_key,
   16550             :                 Anum_pg_constraint_conrelid,
   16551             :                 BTEqualStrategyNumber, F_OIDEQ,
   16552             :                 ObjectIdGetDatum(parent_relid));
   16553        2658 :     parent_scan = systable_beginscan(constraintrel, ConstraintRelidTypidNameIndexId,
   16554             :                                      true, NULL, 1, &parent_key);
   16555             : 
   16556        4028 :     while (HeapTupleIsValid(parent_tuple = systable_getnext(parent_scan)))
   16557             :     {
   16558        1412 :         Form_pg_constraint parent_con = (Form_pg_constraint) GETSTRUCT(parent_tuple);
   16559             :         SysScanDesc child_scan;
   16560             :         ScanKeyData child_key;
   16561             :         HeapTuple   child_tuple;
   16562        1412 :         bool        found = false;
   16563             : 
   16564        1412 :         if (parent_con->contype != CONSTRAINT_CHECK &&
   16565        1284 :             parent_con->contype != CONSTRAINT_NOTNULL)
   16566         828 :             continue;
   16567             : 
   16568             :         /* if the parent's constraint is marked NO INHERIT, it's not inherited */
   16569         684 :         if (parent_con->connoinherit)
   16570         100 :             continue;
   16571             : 
   16572             :         /* Search for a child constraint matching this one */
   16573         584 :         ScanKeyInit(&child_key,
   16574             :                     Anum_pg_constraint_conrelid,
   16575             :                     BTEqualStrategyNumber, F_OIDEQ,
   16576             :                     ObjectIdGetDatum(RelationGetRelid(child_rel)));
   16577         584 :         child_scan = systable_beginscan(constraintrel, ConstraintRelidTypidNameIndexId,
   16578             :                                         true, NULL, 1, &child_key);
   16579             : 
   16580         996 :         while (HeapTupleIsValid(child_tuple = systable_getnext(child_scan)))
   16581             :         {
   16582         966 :             Form_pg_constraint child_con = (Form_pg_constraint) GETSTRUCT(child_tuple);
   16583             :             HeapTuple   child_copy;
   16584             : 
   16585         966 :             if (child_con->contype != parent_con->contype)
   16586         216 :                 continue;
   16587             : 
   16588             :             /*
   16589             :              * CHECK constraint are matched by name, NOT NULL ones by
   16590             :              * attribute number
   16591             :              */
   16592         750 :             if (child_con->contype == CONSTRAINT_CHECK)
   16593             :             {
   16594         114 :                 if (strcmp(NameStr(parent_con->conname),
   16595         114 :                            NameStr(child_con->conname)) != 0)
   16596          30 :                     continue;
   16597             :             }
   16598         636 :             else if (child_con->contype == CONSTRAINT_NOTNULL)
   16599             :             {
   16600         636 :                 AttrNumber  parent_attno = extractNotNullColumn(parent_tuple);
   16601         636 :                 AttrNumber  child_attno = extractNotNullColumn(child_tuple);
   16602             : 
   16603         636 :                 if (strcmp(get_attname(parent_relid, parent_attno, false),
   16604         636 :                            get_attname(RelationGetRelid(child_rel), child_attno,
   16605             :                                        false)) != 0)
   16606         166 :                     continue;
   16607             :             }
   16608             : 
   16609         554 :             if (child_con->contype == CONSTRAINT_CHECK &&
   16610          84 :                 !constraints_equivalent(parent_tuple, child_tuple, RelationGetDescr(constraintrel)))
   16611           6 :                 ereport(ERROR,
   16612             :                         (errcode(ERRCODE_DATATYPE_MISMATCH),
   16613             :                          errmsg("child table \"%s\" has different definition for check constraint \"%s\"",
   16614             :                                 RelationGetRelationName(child_rel), NameStr(parent_con->conname))));
   16615             : 
   16616             :             /*
   16617             :              * If the CHECK child constraint is "no inherit" then cannot
   16618             :              * merge.
   16619             :              *
   16620             :              * This is not desirable for not-null constraints, mostly because
   16621             :              * it breaks our pg_upgrade strategy, but it also makes sense on
   16622             :              * its own: if a child has its own not-null constraint and then
   16623             :              * acquires a parent with the same constraint, then we start to
   16624             :              * enforce that constraint for all the descendants of that child
   16625             :              * too, if any.
   16626             :              */
   16627         548 :             if (child_con->contype == CONSTRAINT_CHECK &&
   16628          78 :                 child_con->connoinherit)
   16629           0 :                 ereport(ERROR,
   16630             :                         (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
   16631             :                          errmsg("constraint \"%s\" conflicts with non-inherited constraint on child table \"%s\"",
   16632             :                                 NameStr(child_con->conname), RelationGetRelationName(child_rel))));
   16633             : 
   16634             :             /*
   16635             :              * If the child constraint is "not valid" then cannot merge with a
   16636             :              * valid parent constraint
   16637             :              */
   16638         548 :             if (parent_con->convalidated && !child_con->convalidated)
   16639           0 :                 ereport(ERROR,
   16640             :                         (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
   16641             :                          errmsg("constraint \"%s\" conflicts with NOT VALID constraint on child table \"%s\"",
   16642             :                                 NameStr(child_con->conname), RelationGetRelationName(child_rel))));
   16643             : 
   16644             :             /*
   16645             :              * OK, bump the child constraint's inheritance count.  (If we fail
   16646             :              * later on, this change will just roll back.)
   16647             :              */
   16648         548 :             child_copy = heap_copytuple(child_tuple);
   16649         548 :             child_con = (Form_pg_constraint) GETSTRUCT(child_copy);
   16650         548 :             child_con->coninhcount++;
   16651         548 :             if (child_con->coninhcount < 0)
   16652           0 :                 ereport(ERROR,
   16653             :                         errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
   16654             :                         errmsg("too many inheritance parents"));
   16655         548 :             if (child_con->contype == CONSTRAINT_NOTNULL &&
   16656         470 :                 child_con->connoinherit)
   16657             :             {
   16658             :                 /*
   16659             :                  * If the child has children, it's not possible to turn a NO
   16660             :                  * INHERIT constraint into an inheritable one: we would need
   16661             :                  * to recurse to create constraints in those children, but
   16662             :                  * this is not a good place to do that.
   16663             :                  */
   16664           6 :                 if (child_rel->rd_rel->relhassubclass)
   16665           6 :                     ereport(ERROR,
   16666             :                             errmsg("cannot add NOT NULL constraint to column \"%s\" of relation \"%s\" with inheritance children",
   16667             :                                    get_attname(RelationGetRelid(child_rel),
   16668             :                                                extractNotNullColumn(child_tuple),
   16669             :                                                false),
   16670             :                                    RelationGetRelationName(child_rel)),
   16671             :                             errdetail("Existing constraint \"%s\" is marked NO INHERIT.",
   16672             :                                       NameStr(child_con->conname)));
   16673             : 
   16674           0 :                 child_con->connoinherit = false;
   16675             :             }
   16676             : 
   16677             :             /*
   16678             :              * In case of partitions, an inherited constraint must be
   16679             :              * inherited only once since it cannot have multiple parents and
   16680             :              * it is never considered local.
   16681             :              */
   16682         542 :             if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
   16683             :             {
   16684             :                 Assert(child_con->coninhcount == 1);
   16685         468 :                 child_con->conislocal = false;
   16686             :             }
   16687             : 
   16688         542 :             CatalogTupleUpdate(constraintrel, &child_copy->t_self, child_copy);
   16689         542 :             heap_freetuple(child_copy);
   16690             : 
   16691         542 :             found = true;
   16692         542 :             break;
   16693             :         }
   16694             : 
   16695         572 :         systable_endscan(child_scan);
   16696             : 
   16697         572 :         if (!found)
   16698             :         {
   16699          30 :             if (parent_con->contype == CONSTRAINT_NOTNULL)
   16700           6 :                 ereport(ERROR,
   16701             :                         errcode(ERRCODE_DATATYPE_MISMATCH),
   16702             :                         errmsg("column \"%s\" in child table must be marked NOT NULL",
   16703             :                                get_attname(parent_relid,
   16704             :                                            extractNotNullColumn(parent_tuple),
   16705             :                                            false)));
   16706             : 
   16707          24 :             ereport(ERROR,
   16708             :                     (errcode(ERRCODE_DATATYPE_MISMATCH),
   16709             :                      errmsg("child table is missing constraint \"%s\"",
   16710             :                             NameStr(parent_con->conname))));
   16711             :         }
   16712             :     }
   16713             : 
   16714        2616 :     systable_endscan(parent_scan);
   16715        2616 :     table_close(constraintrel, RowExclusiveLock);
   16716        2616 : }
   16717             : 
   16718             : /*
   16719             :  * ALTER TABLE NO INHERIT
   16720             :  *
   16721             :  * Return value is the address of the relation that is no longer parent.
   16722             :  */
   16723             : static ObjectAddress
   16724          44 : ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode)
   16725             : {
   16726             :     ObjectAddress address;
   16727             :     Relation    parent_rel;
   16728             : 
   16729          44 :     if (rel->rd_rel->relispartition)
   16730           0 :         ereport(ERROR,
   16731             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   16732             :                  errmsg("cannot change inheritance of a partition")));
   16733             : 
   16734             :     /*
   16735             :      * AccessShareLock on the parent is probably enough, seeing that DROP
   16736             :      * TABLE doesn't lock parent tables at all.  We need some lock since we'll
   16737             :      * be inspecting the parent's schema.
   16738             :      */
   16739          44 :     parent_rel = table_openrv(parent, AccessShareLock);
   16740             : 
   16741             :     /*
   16742             :      * We don't bother to check ownership of the parent table --- ownership of
   16743             :      * the child is presumed enough rights.
   16744             :      */
   16745             : 
   16746             :     /* Off to RemoveInheritance() where most of the work happens */
   16747          44 :     RemoveInheritance(rel, parent_rel, false);
   16748             : 
   16749             :     /*
   16750             :      * If parent_rel has a primary key, then child_rel has not-null
   16751             :      * constraints that make these columns as non nullable.  Mark those
   16752             :      * constraints as no longer inherited by this parent.
   16753             :      */
   16754          38 :     ATInheritAdjustNotNulls(parent_rel, rel, -1);
   16755             : 
   16756             :     /*
   16757             :      * If the parent has a primary key, then we decrement counts for all NOT
   16758             :      * NULL constraints
   16759             :      */
   16760             : 
   16761          38 :     ObjectAddressSet(address, RelationRelationId,
   16762             :                      RelationGetRelid(parent_rel));
   16763             : 
   16764             :     /* keep our lock on the parent relation until commit */
   16765          38 :     table_close(parent_rel, NoLock);
   16766             : 
   16767          38 :     return address;
   16768             : }
   16769             : 
   16770             : /*
   16771             :  * MarkInheritDetached
   16772             :  *
   16773             :  * Set inhdetachpending for a partition, for ATExecDetachPartition
   16774             :  * in concurrent mode.  While at it, verify that no other partition is
   16775             :  * already pending detach.
   16776             :  */
   16777             : static void
   16778         146 : MarkInheritDetached(Relation child_rel, Relation parent_rel)
   16779             : {
   16780             :     Relation    catalogRelation;
   16781             :     SysScanDesc scan;
   16782             :     ScanKeyData key;
   16783             :     HeapTuple   inheritsTuple;
   16784         146 :     bool        found = false;
   16785             : 
   16786             :     Assert(parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
   16787             : 
   16788             :     /*
   16789             :      * Find pg_inherits entries by inhparent.  (We need to scan them all in
   16790             :      * order to verify that no other partition is pending detach.)
   16791             :      */
   16792         146 :     catalogRelation = table_open(InheritsRelationId, RowExclusiveLock);
   16793         146 :     ScanKeyInit(&key,
   16794             :                 Anum_pg_inherits_inhparent,
   16795             :                 BTEqualStrategyNumber, F_OIDEQ,
   16796             :                 ObjectIdGetDatum(RelationGetRelid(parent_rel)));
   16797         146 :     scan = systable_beginscan(catalogRelation, InheritsParentIndexId,
   16798             :                               true, NULL, 1, &key);
   16799             : 
   16800         430 :     while (HeapTupleIsValid(inheritsTuple = systable_getnext(scan)))
   16801             :     {
   16802             :         Form_pg_inherits inhForm;
   16803             : 
   16804         286 :         inhForm = (Form_pg_inherits) GETSTRUCT(inheritsTuple);
   16805         286 :         if (inhForm->inhdetachpending)
   16806           2 :             ereport(ERROR,
   16807             :                     errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
   16808             :                     errmsg("partition \"%s\" already pending detach in partitioned table \"%s.%s\"",
   16809             :                            get_rel_name(inhForm->inhrelid),
   16810             :                            get_namespace_name(parent_rel->rd_rel->relnamespace),
   16811             :                            RelationGetRelationName(parent_rel)),
   16812             :                     errhint("Use ALTER TABLE ... DETACH PARTITION ... FINALIZE to complete the pending detach operation."));
   16813             : 
   16814         284 :         if (inhForm->inhrelid == RelationGetRelid(child_rel))
   16815             :         {
   16816             :             HeapTuple   newtup;
   16817             : 
   16818         144 :             newtup = heap_copytuple(inheritsTuple);
   16819         144 :             ((Form_pg_inherits) GETSTRUCT(newtup))->inhdetachpending = true;
   16820             : 
   16821         144 :             CatalogTupleUpdate(catalogRelation,
   16822             :                                &inheritsTuple->t_self,
   16823             :                                newtup);
   16824         144 :             found = true;
   16825         144 :             heap_freetuple(newtup);
   16826             :             /* keep looking, to ensure we catch others pending detach */
   16827             :         }
   16828             :     }
   16829             : 
   16830             :     /* Done */
   16831         144 :     systable_endscan(scan);
   16832         144 :     table_close(catalogRelation, RowExclusiveLock);
   16833             : 
   16834         144 :     if (!found)
   16835           0 :         ereport(ERROR,
   16836             :                 (errcode(ERRCODE_UNDEFINED_TABLE),
   16837             :                  errmsg("relation \"%s\" is not a partition of relation \"%s\"",
   16838             :                         RelationGetRelationName(child_rel),
   16839             :                         RelationGetRelationName(parent_rel))));
   16840         144 : }
   16841             : 
   16842             : /*
   16843             :  * RemoveInheritance
   16844             :  *
   16845             :  * Drop a parent from the child's parents. This just adjusts the attinhcount
   16846             :  * and attislocal of the columns and removes the pg_inherit and pg_depend
   16847             :  * entries.  expect_detached is passed down to DeleteInheritsTuple, q.v..
   16848             :  *
   16849             :  * If attinhcount goes to 0 then attislocal gets set to true. If it goes back
   16850             :  * up attislocal stays true, which means if a child is ever removed from a
   16851             :  * parent then its columns will never be automatically dropped which may
   16852             :  * surprise. But at least we'll never surprise by dropping columns someone
   16853             :  * isn't expecting to be dropped which would actually mean data loss.
   16854             :  *
   16855             :  * coninhcount and conislocal for inherited constraints are adjusted in
   16856             :  * exactly the same way.
   16857             :  *
   16858             :  * Common to ATExecDropInherit() and ATExecDetachPartition().
   16859             :  */
   16860             : static void
   16861         782 : RemoveInheritance(Relation child_rel, Relation parent_rel, bool expect_detached)
   16862             : {
   16863             :     Relation    catalogRelation;
   16864             :     SysScanDesc scan;
   16865             :     ScanKeyData key[3];
   16866             :     HeapTuple   attributeTuple,
   16867             :                 constraintTuple;
   16868             :     List       *connames;
   16869             :     List       *nncolumns;
   16870             :     bool        found;
   16871             :     bool        is_partitioning;
   16872             : 
   16873         782 :     is_partitioning = (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
   16874             : 
   16875         782 :     found = DeleteInheritsTuple(RelationGetRelid(child_rel),
   16876             :                                 RelationGetRelid(parent_rel),
   16877             :                                 expect_detached,
   16878         782 :                                 RelationGetRelationName(child_rel));
   16879         782 :     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         758 :     catalogRelation = table_open(AttributeRelationId, RowExclusiveLock);
   16899         758 :     ScanKeyInit(&key[0],
   16900             :                 Anum_pg_attribute_attrelid,
   16901             :                 BTEqualStrategyNumber, F_OIDEQ,
   16902             :                 ObjectIdGetDatum(RelationGetRelid(child_rel)));
   16903         758 :     scan = systable_beginscan(catalogRelation, AttributeRelidNumIndexId,
   16904             :                               true, NULL, 1, key);
   16905        7178 :     while (HeapTupleIsValid(attributeTuple = systable_getnext(scan)))
   16906             :     {
   16907        6420 :         Form_pg_attribute att = (Form_pg_attribute) GETSTRUCT(attributeTuple);
   16908             : 
   16909             :         /* Ignore if dropped or not inherited */
   16910        6420 :         if (att->attisdropped)
   16911          36 :             continue;
   16912        6384 :         if (att->attinhcount <= 0)
   16913        4566 :             continue;
   16914             : 
   16915        1818 :         if (SearchSysCacheExistsAttName(RelationGetRelid(parent_rel),
   16916        1818 :                                         NameStr(att->attname)))
   16917             :         {
   16918             :             /* Decrement inhcount and possibly set islocal to true */
   16919        1806 :             HeapTuple   copyTuple = heap_copytuple(attributeTuple);
   16920        1806 :             Form_pg_attribute copy_att = (Form_pg_attribute) GETSTRUCT(copyTuple);
   16921             : 
   16922        1806 :             copy_att->attinhcount--;
   16923        1806 :             if (copy_att->attinhcount == 0)
   16924        1806 :                 copy_att->attislocal = true;
   16925             : 
   16926        1806 :             CatalogTupleUpdate(catalogRelation, &copyTuple->t_self, copyTuple);
   16927        1806 :             heap_freetuple(copyTuple);
   16928             :         }
   16929             :     }
   16930         758 :     systable_endscan(scan);
   16931         758 :     table_close(catalogRelation, RowExclusiveLock);
   16932             : 
   16933             :     /*
   16934             :      * Likewise, find inherited check constraints and disinherit them. To do
   16935             :      * this, we first need a list of the names of the parent's check
   16936             :      * 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.
   16940             :      */
   16941         758 :     catalogRelation = table_open(ConstraintRelationId, RowExclusiveLock);
   16942         758 :     ScanKeyInit(&key[0],
   16943             :                 Anum_pg_constraint_conrelid,
   16944             :                 BTEqualStrategyNumber, F_OIDEQ,
   16945             :                 ObjectIdGetDatum(RelationGetRelid(parent_rel)));
   16946         758 :     scan = systable_beginscan(catalogRelation, ConstraintRelidTypidNameIndexId,
   16947             :                               true, NULL, 1, key);
   16948             : 
   16949         758 :     connames = NIL;
   16950         758 :     nncolumns = NIL;
   16951             : 
   16952        1074 :     while (HeapTupleIsValid(constraintTuple = systable_getnext(scan)))
   16953             :     {
   16954         316 :         Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(constraintTuple);
   16955             : 
   16956         316 :         if (con->contype == CONSTRAINT_CHECK)
   16957          18 :             connames = lappend(connames, pstrdup(NameStr(con->conname)));
   16958         316 :         if (con->contype == CONSTRAINT_NOTNULL)
   16959          80 :             nncolumns = lappend_int(nncolumns, extractNotNullColumn(constraintTuple));
   16960             :     }
   16961             : 
   16962         758 :     systable_endscan(scan);
   16963             : 
   16964             :     /* Now scan the child's constraints */
   16965         758 :     ScanKeyInit(&key[0],
   16966             :                 Anum_pg_constraint_conrelid,
   16967             :                 BTEqualStrategyNumber, F_OIDEQ,
   16968             :                 ObjectIdGetDatum(RelationGetRelid(child_rel)));
   16969         758 :     scan = systable_beginscan(catalogRelation, ConstraintRelidTypidNameIndexId,
   16970             :                               true, NULL, 1, key);
   16971             : 
   16972        1270 :     while (HeapTupleIsValid(constraintTuple = systable_getnext(scan)))
   16973             :     {
   16974         512 :         Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(constraintTuple);
   16975         512 :         bool        match = false;
   16976             :         ListCell   *lc;
   16977             : 
   16978             :         /*
   16979             :          * Match CHECK constraints by name, not-null constraints by column
   16980             :          * number, and ignore all others.
   16981             :          */
   16982         512 :         if (con->contype == CONSTRAINT_CHECK)
   16983             :         {
   16984         190 :             foreach(lc, connames)
   16985             :             {
   16986          30 :                 if (con->contype == CONSTRAINT_CHECK &&
   16987          30 :                     strcmp(NameStr(con->conname), (char *) lfirst(lc)) == 0)
   16988             :                 {
   16989          18 :                     match = true;
   16990          18 :                     break;
   16991             :                 }
   16992             :             }
   16993             :         }
   16994         334 :         else if (con->contype == CONSTRAINT_NOTNULL)
   16995             :         {
   16996          98 :             AttrNumber  child_attno = extractNotNullColumn(constraintTuple);
   16997             : 
   16998         128 :             foreach(lc, nncolumns)
   16999             :             {
   17000         110 :                 if (lfirst_int(lc) == child_attno)
   17001             :                 {
   17002          80 :                     match = true;
   17003          80 :                     break;
   17004             :                 }
   17005             :             }
   17006             :         }
   17007             :         else
   17008         236 :             continue;
   17009             : 
   17010         276 :         if (match)
   17011             :         {
   17012             :             /* Decrement inhcount and possibly set islocal to true */
   17013          98 :             HeapTuple   copyTuple = heap_copytuple(constraintTuple);
   17014          98 :             Form_pg_constraint copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
   17015             : 
   17016          98 :             if (copy_con->coninhcount <= 0) /* shouldn't happen */
   17017           0 :                 elog(ERROR, "relation %u has non-inherited constraint \"%s\"",
   17018             :                      RelationGetRelid(child_rel), NameStr(copy_con->conname));
   17019             : 
   17020          98 :             copy_con->coninhcount--;
   17021          98 :             if (copy_con->coninhcount == 0)
   17022          98 :                 copy_con->conislocal = true;
   17023             : 
   17024          98 :             CatalogTupleUpdate(catalogRelation, &copyTuple->t_self, copyTuple);
   17025          98 :             heap_freetuple(copyTuple);
   17026             :         }
   17027             :     }
   17028             : 
   17029         758 :     systable_endscan(scan);
   17030         758 :     table_close(catalogRelation, RowExclusiveLock);
   17031             : 
   17032         758 :     drop_parent_dependency(RelationGetRelid(child_rel),
   17033             :                            RelationRelationId,
   17034             :                            RelationGetRelid(parent_rel),
   17035             :                            child_dependency_type(is_partitioning));
   17036             : 
   17037             :     /*
   17038             :      * Post alter hook of this inherits. Since object_access_hook doesn't take
   17039             :      * multiple object identifiers, we relay oid of parent relation using
   17040             :      * auxiliary_id argument.
   17041             :      */
   17042         758 :     InvokeObjectPostAlterHookArg(InheritsRelationId,
   17043             :                                  RelationGetRelid(child_rel), 0,
   17044             :                                  RelationGetRelid(parent_rel), false);
   17045         758 : }
   17046             : 
   17047             : /*
   17048             :  * Adjust coninhcount of not-null constraints upwards or downwards when a
   17049             :  * table is marked as inheriting or no longer doing so a table with a primary
   17050             :  * key.
   17051             :  *
   17052             :  * Note: these constraints are not dropped, even if their inhcount goes to zero
   17053             :  * and conislocal is false.  Instead we mark the constraints as locally defined.
   17054             :  * This is seen as more useful behavior, with no downsides.  The user can always
   17055             :  * drop them afterwards.
   17056             :  */
   17057             : static void
   17058         274 : ATInheritAdjustNotNulls(Relation parent_rel, Relation child_rel, int inhcount)
   17059             : {
   17060             :     Bitmapset  *pkattnos;
   17061             : 
   17062             :     /* Quick exit when parent has no PK */
   17063         274 :     if (!parent_rel->rd_rel->relhasindex)
   17064         232 :         return;
   17065             : 
   17066          42 :     pkattnos = RelationGetIndexAttrBitmap(parent_rel,
   17067             :                                           INDEX_ATTR_BITMAP_PRIMARY_KEY);
   17068          42 :     if (pkattnos != NULL)
   17069             :     {
   17070          36 :         Bitmapset  *childattnums = NULL;
   17071             :         AttrMap    *attmap;
   17072             :         int         i;
   17073             : 
   17074          36 :         attmap = build_attrmap_by_name(RelationGetDescr(parent_rel),
   17075             :                                        RelationGetDescr(child_rel), true);
   17076             : 
   17077          36 :         i = -1;
   17078          72 :         while ((i = bms_next_member(pkattnos, i)) >= 0)
   17079             :         {
   17080          36 :             childattnums = bms_add_member(childattnums,
   17081          36 :                                           attmap->attnums[i + FirstLowInvalidHeapAttributeNumber - 1]);
   17082             :         }
   17083             : 
   17084             :         /*
   17085             :          * CCI is needed in case there's a NOT NULL PRIMARY KEY column in the
   17086             :          * parent: the relevant not-null constraint in the child already had
   17087             :          * its inhcount modified earlier.
   17088             :          */
   17089          36 :         CommandCounterIncrement();
   17090          36 :         AdjustNotNullInheritance(RelationGetRelid(child_rel), childattnums,
   17091             :                                  inhcount);
   17092             :     }
   17093             : }
   17094             : 
   17095             : /*
   17096             :  * Drop the dependency created by StoreCatalogInheritance1 (CREATE TABLE
   17097             :  * INHERITS/ALTER TABLE INHERIT -- refclassid will be RelationRelationId) or
   17098             :  * heap_create_with_catalog (CREATE TABLE OF/ALTER TABLE OF -- refclassid will
   17099             :  * be TypeRelationId).  There's no convenient way to do this, so go trawling
   17100             :  * through pg_depend.
   17101             :  */
   17102             : static void
   17103         770 : drop_parent_dependency(Oid relid, Oid refclassid, Oid refobjid,
   17104             :                        DependencyType deptype)
   17105             : {
   17106             :     Relation    catalogRelation;
   17107             :     SysScanDesc scan;
   17108             :     ScanKeyData key[3];
   17109             :     HeapTuple   depTuple;
   17110             : 
   17111         770 :     catalogRelation = table_open(DependRelationId, RowExclusiveLock);
   17112             : 
   17113         770 :     ScanKeyInit(&key[0],
   17114             :                 Anum_pg_depend_classid,
   17115             :                 BTEqualStrategyNumber, F_OIDEQ,
   17116             :                 ObjectIdGetDatum(RelationRelationId));
   17117         770 :     ScanKeyInit(&key[1],
   17118             :                 Anum_pg_depend_objid,
   17119             :                 BTEqualStrategyNumber, F_OIDEQ,
   17120             :                 ObjectIdGetDatum(relid));
   17121         770 :     ScanKeyInit(&key[2],
   17122             :                 Anum_pg_depend_objsubid,
   17123             :                 BTEqualStrategyNumber, F_INT4EQ,
   17124             :                 Int32GetDatum(0));
   17125             : 
   17126         770 :     scan = systable_beginscan(catalogRelation, DependDependerIndexId, true,
   17127             :                               NULL, 3, key);
   17128             : 
   17129        2340 :     while (HeapTupleIsValid(depTuple = systable_getnext(scan)))
   17130             :     {
   17131        1570 :         Form_pg_depend dep = (Form_pg_depend) GETSTRUCT(depTuple);
   17132             : 
   17133        1570 :         if (dep->refclassid == refclassid &&
   17134         782 :             dep->refobjid == refobjid &&
   17135         770 :             dep->refobjsubid == 0 &&
   17136         770 :             dep->deptype == deptype)
   17137         770 :             CatalogTupleDelete(catalogRelation, &depTuple->t_self);
   17138             :     }
   17139             : 
   17140         770 :     systable_endscan(scan);
   17141         770 :     table_close(catalogRelation, RowExclusiveLock);
   17142         770 : }
   17143             : 
   17144             : /*
   17145             :  * ALTER TABLE OF
   17146             :  *
   17147             :  * Attach a table to a composite type, as though it had been created with CREATE
   17148             :  * TABLE OF.  All attname, atttypid, atttypmod and attcollation must match.  The
   17149             :  * subject table must not have inheritance parents.  These restrictions ensure
   17150             :  * that you cannot create a configuration impossible with CREATE TABLE OF alone.
   17151             :  *
   17152             :  * The address of the type is returned.
   17153             :  */
   17154             : static ObjectAddress
   17155          66 : ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode)
   17156             : {
   17157          66 :     Oid         relid = RelationGetRelid(rel);
   17158             :     Type        typetuple;
   17159             :     Form_pg_type typeform;
   17160             :     Oid         typeid;
   17161             :     Relation    inheritsRelation,
   17162             :                 relationRelation;
   17163             :     SysScanDesc scan;
   17164             :     ScanKeyData key;
   17165             :     AttrNumber  table_attno,
   17166             :                 type_attno;
   17167             :     TupleDesc   typeTupleDesc,
   17168             :                 tableTupleDesc;
   17169             :     ObjectAddress tableobj,
   17170             :                 typeobj;
   17171             :     HeapTuple   classtuple;
   17172             : 
   17173             :     /* Validate the type. */
   17174          66 :     typetuple = typenameType(NULL, ofTypename, NULL);
   17175          66 :     check_of_type(typetuple);
   17176          66 :     typeform = (Form_pg_type) GETSTRUCT(typetuple);
   17177          66 :     typeid = typeform->oid;
   17178             : 
   17179             :     /* Fail if the table has any inheritance parents. */
   17180          66 :     inheritsRelation = table_open(InheritsRelationId, AccessShareLock);
   17181          66 :     ScanKeyInit(&key,
   17182             :                 Anum_pg_inherits_inhrelid,
   17183             :                 BTEqualStrategyNumber, F_OIDEQ,
   17184             :                 ObjectIdGetDatum(relid));
   17185          66 :     scan = systable_beginscan(inheritsRelation, InheritsRelidSeqnoIndexId,
   17186             :                               true, NULL, 1, &key);
   17187          66 :     if (HeapTupleIsValid(systable_getnext(scan)))
   17188           6 :         ereport(ERROR,
   17189             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   17190             :                  errmsg("typed tables cannot inherit")));
   17191          60 :     systable_endscan(scan);
   17192          60 :     table_close(inheritsRelation, AccessShareLock);
   17193             : 
   17194             :     /*
   17195             :      * Check the tuple descriptors for compatibility.  Unlike inheritance, we
   17196             :      * require that the order also match.  However, attnotnull need not match.
   17197             :      */
   17198          60 :     typeTupleDesc = lookup_rowtype_tupdesc(typeid, -1);
   17199          60 :     tableTupleDesc = RelationGetDescr(rel);
   17200          60 :     table_attno = 1;
   17201         190 :     for (type_attno = 1; type_attno <= typeTupleDesc->natts; type_attno++)
   17202             :     {
   17203             :         Form_pg_attribute type_attr,
   17204             :                     table_attr;
   17205             :         const char *type_attname,
   17206             :                    *table_attname;
   17207             : 
   17208             :         /* Get the next non-dropped type attribute. */
   17209         154 :         type_attr = TupleDescAttr(typeTupleDesc, type_attno - 1);
   17210         154 :         if (type_attr->attisdropped)
   17211          44 :             continue;
   17212         110 :         type_attname = NameStr(type_attr->attname);
   17213             : 
   17214             :         /* Get the next non-dropped table attribute. */
   17215             :         do
   17216             :         {
   17217         122 :             if (table_attno > tableTupleDesc->natts)
   17218           6 :                 ereport(ERROR,
   17219             :                         (errcode(ERRCODE_DATATYPE_MISMATCH),
   17220             :                          errmsg("table is missing column \"%s\"",
   17221             :                                 type_attname)));
   17222         116 :             table_attr = TupleDescAttr(tableTupleDesc, table_attno - 1);
   17223         116 :             table_attno++;
   17224         116 :         } while (table_attr->attisdropped);
   17225         104 :         table_attname = NameStr(table_attr->attname);
   17226             : 
   17227             :         /* Compare name. */
   17228         104 :         if (strncmp(table_attname, type_attname, NAMEDATALEN) != 0)
   17229           6 :             ereport(ERROR,
   17230             :                     (errcode(ERRCODE_DATATYPE_MISMATCH),
   17231             :                      errmsg("table has column \"%s\" where type requires \"%s\"",
   17232             :                             table_attname, type_attname)));
   17233             : 
   17234             :         /* Compare type. */
   17235          98 :         if (table_attr->atttypid != type_attr->atttypid ||
   17236          92 :             table_attr->atttypmod != type_attr->atttypmod ||
   17237          86 :             table_attr->attcollation != type_attr->attcollation)
   17238          12 :             ereport(ERROR,
   17239             :                     (errcode(ERRCODE_DATATYPE_MISMATCH),
   17240             :                      errmsg("table \"%s\" has different type for column \"%s\"",
   17241             :                             RelationGetRelationName(rel), type_attname)));
   17242             :     }
   17243          36 :     ReleaseTupleDesc(typeTupleDesc);
   17244             : 
   17245             :     /* Any remaining columns at the end of the table had better be dropped. */
   17246          36 :     for (; table_attno <= tableTupleDesc->natts; table_attno++)
   17247             :     {
   17248           6 :         Form_pg_attribute table_attr = TupleDescAttr(tableTupleDesc,
   17249             :                                                      table_attno - 1);
   17250             : 
   17251           6 :         if (!table_attr->attisdropped)
   17252           6 :             ereport(ERROR,
   17253             :                     (errcode(ERRCODE_DATATYPE_MISMATCH),
   17254             :                      errmsg("table has extra column \"%s\"",
   17255             :                             NameStr(table_attr->attname))));
   17256             :     }
   17257             : 
   17258             :     /* If the table was already typed, drop the existing dependency. */
   17259          30 :     if (rel->rd_rel->reloftype)
   17260           6 :         drop_parent_dependency(relid, TypeRelationId, rel->rd_rel->reloftype,
   17261             :                                DEPENDENCY_NORMAL);
   17262             : 
   17263             :     /* Record a dependency on the new type. */
   17264          30 :     tableobj.classId = RelationRelationId;
   17265          30 :     tableobj.objectId = relid;
   17266          30 :     tableobj.objectSubId = 0;
   17267          30 :     typeobj.classId = TypeRelationId;
   17268          30 :     typeobj.objectId = typeid;
   17269          30 :     typeobj.objectSubId = 0;
   17270          30 :     recordDependencyOn(&tableobj, &typeobj, DEPENDENCY_NORMAL);
   17271             : 
   17272             :     /* Update pg_class.reloftype */
   17273          30 :     relationRelation = table_open(RelationRelationId, RowExclusiveLock);
   17274          30 :     classtuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
   17275          30 :     if (!HeapTupleIsValid(classtuple))
   17276           0 :         elog(ERROR, "cache lookup failed for relation %u", relid);
   17277          30 :     ((Form_pg_class) GETSTRUCT(classtuple))->reloftype = typeid;
   17278          30 :     CatalogTupleUpdate(relationRelation, &classtuple->t_self, classtuple);
   17279             : 
   17280          30 :     InvokeObjectPostAlterHook(RelationRelationId, relid, 0);
   17281             : 
   17282          30 :     heap_freetuple(classtuple);
   17283          30 :     table_close(relationRelation, RowExclusiveLock);
   17284             : 
   17285          30 :     ReleaseSysCache(typetuple);
   17286             : 
   17287          30 :     return typeobj;
   17288             : }
   17289             : 
   17290             : /*
   17291             :  * ALTER TABLE NOT OF
   17292             :  *
   17293             :  * Detach a typed table from its originating type.  Just clear reloftype and
   17294             :  * remove the dependency.
   17295             :  */
   17296             : static void
   17297           6 : ATExecDropOf(Relation rel, LOCKMODE lockmode)
   17298             : {
   17299           6 :     Oid         relid = RelationGetRelid(rel);
   17300             :     Relation    relationRelation;
   17301             :     HeapTuple   tuple;
   17302             : 
   17303           6 :     if (!OidIsValid(rel->rd_rel->reloftype))
   17304           0 :         ereport(ERROR,
   17305             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   17306             :                  errmsg("\"%s\" is not a typed table",
   17307             :                         RelationGetRelationName(rel))));
   17308             : 
   17309             :     /*
   17310             :      * We don't bother to check ownership of the type --- ownership of the
   17311             :      * table is presumed enough rights.  No lock required on the type, either.
   17312             :      */
   17313             : 
   17314           6 :     drop_parent_dependency(relid, TypeRelationId, rel->rd_rel->reloftype,
   17315             :                            DEPENDENCY_NORMAL);
   17316             : 
   17317             :     /* Clear pg_class.reloftype */
   17318           6 :     relationRelation = table_open(RelationRelationId, RowExclusiveLock);
   17319           6 :     tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
   17320           6 :     if (!HeapTupleIsValid(tuple))
   17321           0 :         elog(ERROR, "cache lookup failed for relation %u", relid);
   17322           6 :     ((Form_pg_class) GETSTRUCT(tuple))->reloftype = InvalidOid;
   17323           6 :     CatalogTupleUpdate(relationRelation, &tuple->t_self, tuple);
   17324             : 
   17325           6 :     InvokeObjectPostAlterHook(RelationRelationId, relid, 0);
   17326             : 
   17327           6 :     heap_freetuple(tuple);
   17328           6 :     table_close(relationRelation, RowExclusiveLock);
   17329           6 : }
   17330             : 
   17331             : /*
   17332             :  * relation_mark_replica_identity: Update a table's replica identity
   17333             :  *
   17334             :  * Iff ri_type = REPLICA_IDENTITY_INDEX, indexOid must be the Oid of a suitable
   17335             :  * index. Otherwise, it must be InvalidOid.
   17336             :  *
   17337             :  * Caller had better hold an exclusive lock on the relation, as the results
   17338             :  * of running two of these concurrently wouldn't be pretty.
   17339             :  */
   17340             : static void
   17341         420 : relation_mark_replica_identity(Relation rel, char ri_type, Oid indexOid,
   17342             :                                bool is_internal)
   17343             : {
   17344             :     Relation    pg_index;
   17345             :     Relation    pg_class;
   17346             :     HeapTuple   pg_class_tuple;
   17347             :     HeapTuple   pg_index_tuple;
   17348             :     Form_pg_class pg_class_form;
   17349             :     Form_pg_index pg_index_form;
   17350             :     ListCell   *index;
   17351             : 
   17352             :     /*
   17353             :      * Check whether relreplident has changed, and update it if so.
   17354             :      */
   17355         420 :     pg_class = table_open(RelationRelationId, RowExclusiveLock);
   17356         420 :     pg_class_tuple = SearchSysCacheCopy1(RELOID,
   17357             :                                          ObjectIdGetDatum(RelationGetRelid(rel)));
   17358         420 :     if (!HeapTupleIsValid(pg_class_tuple))
   17359           0 :         elog(ERROR, "cache lookup failed for relation \"%s\"",
   17360             :              RelationGetRelationName(rel));
   17361         420 :     pg_class_form = (Form_pg_class) GETSTRUCT(pg_class_tuple);
   17362         420 :     if (pg_class_form->relreplident != ri_type)
   17363             :     {
   17364         370 :         pg_class_form->relreplident = ri_type;
   17365         370 :         CatalogTupleUpdate(pg_class, &pg_class_tuple->t_self, pg_class_tuple);
   17366             :     }
   17367         420 :     table_close(pg_class, RowExclusiveLock);
   17368         420 :     heap_freetuple(pg_class_tuple);
   17369             : 
   17370             :     /*
   17371             :      * Update the per-index indisreplident flags correctly.
   17372             :      */
   17373         420 :     pg_index = table_open(IndexRelationId, RowExclusiveLock);
   17374        1130 :     foreach(index, RelationGetIndexList(rel))
   17375             :     {
   17376         710 :         Oid         thisIndexOid = lfirst_oid(index);
   17377         710 :         bool        dirty = false;
   17378             : 
   17379         710 :         pg_index_tuple = SearchSysCacheCopy1(INDEXRELID,
   17380             :                                              ObjectIdGetDatum(thisIndexOid));
   17381         710 :         if (!HeapTupleIsValid(pg_index_tuple))
   17382           0 :             elog(ERROR, "cache lookup failed for index %u", thisIndexOid);
   17383         710 :         pg_index_form = (Form_pg_index) GETSTRUCT(pg_index_tuple);
   17384             : 
   17385         710 :         if (thisIndexOid == indexOid)
   17386             :         {
   17387             :             /* Set the bit if not already set. */
   17388         234 :             if (!pg_index_form->indisreplident)
   17389             :             {
   17390         216 :                 dirty = true;
   17391         216 :                 pg_index_form->indisreplident = true;
   17392             :             }
   17393             :         }
   17394             :         else
   17395             :         {
   17396             :             /* Unset the bit if set. */
   17397         476 :             if (pg_index_form->indisreplident)
   17398             :             {
   17399          52 :                 dirty = true;
   17400          52 :                 pg_index_form->indisreplident = false;
   17401             :             }
   17402             :         }
   17403             : 
   17404         710 :         if (dirty)
   17405             :         {
   17406         268 :             CatalogTupleUpdate(pg_index, &pg_index_tuple->t_self, pg_index_tuple);
   17407         268 :             InvokeObjectPostAlterHookArg(IndexRelationId, thisIndexOid, 0,
   17408             :                                          InvalidOid, is_internal);
   17409             : 
   17410             :             /*
   17411             :              * Invalidate the relcache for the table, so that after we commit
   17412             :              * all sessions will refresh the table's replica identity index
   17413             :              * before attempting any UPDATE or DELETE on the table.  (If we
   17414             :              * changed the table's pg_class row above, then a relcache inval
   17415             :              * is already queued due to that; but we might not have.)
   17416             :              */
   17417         268 :             CacheInvalidateRelcache(rel);
   17418             :         }
   17419         710 :         heap_freetuple(pg_index_tuple);
   17420             :     }
   17421             : 
   17422         420 :     table_close(pg_index, RowExclusiveLock);
   17423         420 : }
   17424             : 
   17425             : /*
   17426             :  * ALTER TABLE <name> REPLICA IDENTITY ...
   17427             :  */
   17428             : static void
   17429         468 : ATExecReplicaIdentity(Relation rel, ReplicaIdentityStmt *stmt, LOCKMODE lockmode)
   17430             : {
   17431             :     Oid         indexOid;
   17432             :     Relation    indexRel;
   17433             :     int         key;
   17434             : 
   17435         468 :     if (stmt->identity_type == REPLICA_IDENTITY_DEFAULT)
   17436             :     {
   17437           6 :         relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
   17438           6 :         return;
   17439             :     }
   17440         462 :     else if (stmt->identity_type == REPLICA_IDENTITY_FULL)
   17441             :     {
   17442         144 :         relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
   17443         144 :         return;
   17444             :     }
   17445         318 :     else if (stmt->identity_type == REPLICA_IDENTITY_NOTHING)
   17446             :     {
   17447          36 :         relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
   17448          36 :         return;
   17449             :     }
   17450         282 :     else if (stmt->identity_type == REPLICA_IDENTITY_INDEX)
   17451             :     {
   17452             :          /* fallthrough */ ;
   17453             :     }
   17454             :     else
   17455           0 :         elog(ERROR, "unexpected identity type %u", stmt->identity_type);
   17456             : 
   17457             :     /* Check that the index exists */
   17458         282 :     indexOid = get_relname_relid(stmt->name, rel->rd_rel->relnamespace);
   17459         282 :     if (!OidIsValid(indexOid))
   17460           0 :         ereport(ERROR,
   17461             :                 (errcode(ERRCODE_UNDEFINED_OBJECT),
   17462             :                  errmsg("index \"%s\" for table \"%s\" does not exist",
   17463             :                         stmt->name, RelationGetRelationName(rel))));
   17464             : 
   17465         282 :     indexRel = index_open(indexOid, ShareLock);
   17466             : 
   17467             :     /* Check that the index is on the relation we're altering. */
   17468         282 :     if (indexRel->rd_index == NULL ||
   17469         282 :         indexRel->rd_index->indrelid != RelationGetRelid(rel))
   17470           6 :         ereport(ERROR,
   17471             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   17472             :                  errmsg("\"%s\" is not an index for table \"%s\"",
   17473             :                         RelationGetRelationName(indexRel),
   17474             :                         RelationGetRelationName(rel))));
   17475             :     /* The AM must support uniqueness, and the index must in fact be unique. */
   17476         276 :     if (!indexRel->rd_indam->amcanunique ||
   17477         270 :         !indexRel->rd_index->indisunique)
   17478          12 :         ereport(ERROR,
   17479             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   17480             :                  errmsg("cannot use non-unique index \"%s\" as replica identity",
   17481             :                         RelationGetRelationName(indexRel))));
   17482             :     /* Deferred indexes are not guaranteed to be always unique. */
   17483         264 :     if (!indexRel->rd_index->indimmediate)
   17484          12 :         ereport(ERROR,
   17485             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   17486             :                  errmsg("cannot use non-immediate index \"%s\" as replica identity",
   17487             :                         RelationGetRelationName(indexRel))));
   17488             :     /* Expression indexes aren't supported. */
   17489         252 :     if (RelationGetIndexExpressions(indexRel) != NIL)
   17490           6 :         ereport(ERROR,
   17491             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   17492             :                  errmsg("cannot use expression index \"%s\" as replica identity",
   17493             :                         RelationGetRelationName(indexRel))));
   17494             :     /* Predicate indexes aren't supported. */
   17495         246 :     if (RelationGetIndexPredicate(indexRel) != NIL)
   17496           6 :         ereport(ERROR,
   17497             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   17498             :                  errmsg("cannot use partial index \"%s\" as replica identity",
   17499             :                         RelationGetRelationName(indexRel))));
   17500             : 
   17501             :     /* Check index for nullable columns. */
   17502         526 :     for (key = 0; key < IndexRelationGetNumberOfKeyAttributes(indexRel); key++)
   17503             :     {
   17504         292 :         int16       attno = indexRel->rd_index->indkey.values[key];
   17505             :         Form_pg_attribute attr;
   17506             : 
   17507             :         /*
   17508             :          * Reject any other system columns.  (Going forward, we'll disallow
   17509             :          * indexes containing such columns in the first place, but they might
   17510             :          * exist in older branches.)
   17511             :          */
   17512         292 :         if (attno <= 0)
   17513           0 :             ereport(ERROR,
   17514             :                     (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
   17515             :                      errmsg("index \"%s\" cannot be used as replica identity because column %d is a system column",
   17516             :                             RelationGetRelationName(indexRel), attno)));
   17517             : 
   17518         292 :         attr = TupleDescAttr(rel->rd_att, attno - 1);
   17519         292 :         if (!attr->attnotnull)
   17520           6 :             ereport(ERROR,
   17521             :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   17522             :                      errmsg("index \"%s\" cannot be used as replica identity because column \"%s\" is nullable",
   17523             :                             RelationGetRelationName(indexRel),
   17524             :                             NameStr(attr->attname))));
   17525             :     }
   17526             : 
   17527             :     /* This index is suitable for use as a replica identity. Mark it. */
   17528         234 :     relation_mark_replica_identity(rel, stmt->identity_type, indexOid, true);
   17529             : 
   17530         234 :     index_close(indexRel, NoLock);
   17531             : }
   17532             : 
   17533             : /*
   17534             :  * ALTER TABLE ENABLE/DISABLE ROW LEVEL SECURITY
   17535             :  */
   17536             : static void
   17537         288 : ATExecSetRowSecurity(Relation rel, bool rls)
   17538             : {
   17539             :     Relation    pg_class;
   17540             :     Oid         relid;
   17541             :     HeapTuple   tuple;
   17542             : 
   17543         288 :     relid = RelationGetRelid(rel);
   17544             : 
   17545             :     /* Pull the record for this relation and update it */
   17546         288 :     pg_class = table_open(RelationRelationId, RowExclusiveLock);
   17547             : 
   17548         288 :     tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
   17549             : 
   17550         288 :     if (!HeapTupleIsValid(tuple))
   17551           0 :         elog(ERROR, "cache lookup failed for relation %u", relid);
   17552             : 
   17553         288 :     ((Form_pg_class) GETSTRUCT(tuple))->relrowsecurity = rls;
   17554         288 :     CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
   17555             : 
   17556         288 :     InvokeObjectPostAlterHook(RelationRelationId,
   17557             :                               RelationGetRelid(rel), 0);
   17558             : 
   17559         288 :     table_close(pg_class, RowExclusiveLock);
   17560         288 :     heap_freetuple(tuple);
   17561         288 : }
   17562             : 
   17563             : /*
   17564             :  * ALTER TABLE FORCE/NO FORCE ROW LEVEL SECURITY
   17565             :  */
   17566             : static void
   17567         114 : ATExecForceNoForceRowSecurity(Relation rel, bool force_rls)
   17568             : {
   17569             :     Relation    pg_class;
   17570             :     Oid         relid;
   17571             :     HeapTuple   tuple;
   17572             : 
   17573         114 :     relid = RelationGetRelid(rel);
   17574             : 
   17575         114 :     pg_class = table_open(RelationRelationId, RowExclusiveLock);
   17576             : 
   17577         114 :     tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
   17578             : 
   17579         114 :     if (!HeapTupleIsValid(tuple))
   17580           0 :         elog(ERROR, "cache lookup failed for relation %u", relid);
   17581             : 
   17582         114 :     ((Form_pg_class) GETSTRUCT(tuple))->relforcerowsecurity = force_rls;
   17583         114 :     CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
   17584             : 
   17585         114 :     InvokeObjectPostAlterHook(RelationRelationId,
   17586             :                               RelationGetRelid(rel), 0);
   17587             : 
   17588         114 :     table_close(pg_class, RowExclusiveLock);
   17589         114 :     heap_freetuple(tuple);
   17590         114 : }
   17591             : 
   17592             : /*
   17593             :  * ALTER FOREIGN TABLE <name> OPTIONS (...)
   17594             :  */
   17595             : static void
   17596          50 : ATExecGenericOptions(Relation rel, List *options)
   17597             : {
   17598             :     Relation    ftrel;
   17599             :     ForeignServer *server;
   17600             :     ForeignDataWrapper *fdw;
   17601             :     HeapTuple   tuple;
   17602             :     bool        isnull;
   17603             :     Datum       repl_val[Natts_pg_foreign_table];
   17604             :     bool        repl_null[Natts_pg_foreign_table];
   17605             :     bool        repl_repl[Natts_pg_foreign_table];
   17606             :     Datum       datum;
   17607             :     Form_pg_foreign_table tableform;
   17608             : 
   17609          50 :     if (options == NIL)
   17610           0 :         return;
   17611             : 
   17612          50 :     ftrel = table_open(ForeignTableRelationId, RowExclusiveLock);
   17613             : 
   17614          50 :     tuple = SearchSysCacheCopy1(FOREIGNTABLEREL,
   17615             :                                 ObjectIdGetDatum(rel->rd_id));
   17616          50 :     if (!HeapTupleIsValid(tuple))
   17617           0 :         ereport(ERROR,
   17618             :                 (errcode(ERRCODE_UNDEFINED_OBJECT),
   17619             :                  errmsg("foreign table \"%s\" does not exist",
   17620             :                         RelationGetRelationName(rel))));
   17621          50 :     tableform = (Form_pg_foreign_table) GETSTRUCT(tuple);
   17622          50 :     server = GetForeignServer(tableform->ftserver);
   17623          50 :     fdw = GetForeignDataWrapper(server->fdwid);
   17624             : 
   17625          50 :     memset(repl_val, 0, sizeof(repl_val));
   17626          50 :     memset(repl_null, false, sizeof(repl_null));
   17627          50 :     memset(repl_repl, false, sizeof(repl_repl));
   17628             : 
   17629             :     /* Extract the current options */
   17630          50 :     datum = SysCacheGetAttr(FOREIGNTABLEREL,
   17631             :                             tuple,
   17632             :                             Anum_pg_foreign_table_ftoptions,
   17633             :                             &isnull);
   17634          50 :     if (isnull)
   17635           4 :         datum = PointerGetDatum(NULL);
   17636             : 
   17637             :     /* Transform the options */
   17638          50 :     datum = transformGenericOptions(ForeignTableRelationId,
   17639             :                                     datum,
   17640             :                                     options,
   17641             :                                     fdw->fdwvalidator);
   17642             : 
   17643          48 :     if (PointerIsValid(DatumGetPointer(datum)))
   17644          48 :         repl_val[Anum_pg_foreign_table_ftoptions - 1] = datum;
   17645             :     else
   17646           0 :         repl_null[Anum_pg_foreign_table_ftoptions - 1] = true;
   17647             : 
   17648          48 :     repl_repl[Anum_pg_foreign_table_ftoptions - 1] = true;
   17649             : 
   17650             :     /* Everything looks good - update the tuple */
   17651             : 
   17652          48 :     tuple = heap_modify_tuple(tuple, RelationGetDescr(ftrel),
   17653             :                               repl_val, repl_null, repl_repl);
   17654             : 
   17655          48 :     CatalogTupleUpdate(ftrel, &tuple->t_self, tuple);
   17656             : 
   17657             :     /*
   17658             :      * Invalidate relcache so that all sessions will refresh any cached plans
   17659             :      * that might depend on the old options.
   17660             :      */
   17661          48 :     CacheInvalidateRelcache(rel);
   17662             : 
   17663          48 :     InvokeObjectPostAlterHook(ForeignTableRelationId,
   17664             :                               RelationGetRelid(rel), 0);
   17665             : 
   17666          48 :     table_close(ftrel, RowExclusiveLock);
   17667             : 
   17668          48 :     heap_freetuple(tuple);
   17669             : }
   17670             : 
   17671             : /*
   17672             :  * ALTER TABLE ALTER COLUMN SET COMPRESSION
   17673             :  *
   17674             :  * Return value is the address of the modified column
   17675             :  */
   17676             : static ObjectAddress
   17677          66 : ATExecSetCompression(Relation rel,
   17678             :                      const char *column,
   17679             :                      Node *newValue,
   17680             :                      LOCKMODE lockmode)
   17681             : {
   17682             :     Relation    attrel;
   17683             :     HeapTuple   tuple;
   17684             :     Form_pg_attribute atttableform;
   17685             :     AttrNumber  attnum;
   17686             :     char       *compression;
   17687             :     char        cmethod;
   17688             :     ObjectAddress address;
   17689             : 
   17690          66 :     compression = strVal(newValue);
   17691             : 
   17692          66 :     attrel = table_open(AttributeRelationId, RowExclusiveLock);
   17693             : 
   17694             :     /* copy the cache entry so we can scribble on it below */
   17695          66 :     tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), column);
   17696          66 :     if (!HeapTupleIsValid(tuple))
   17697           0 :         ereport(ERROR,
   17698             :                 (errcode(ERRCODE_UNDEFINED_COLUMN),
   17699             :                  errmsg("column \"%s\" of relation \"%s\" does not exist",
   17700             :                         column, RelationGetRelationName(rel))));
   17701             : 
   17702             :     /* prevent them from altering a system attribute */
   17703          66 :     atttableform = (Form_pg_attribute) GETSTRUCT(tuple);
   17704          66 :     attnum = atttableform->attnum;
   17705          66 :     if (attnum <= 0)
   17706           0 :         ereport(ERROR,
   17707             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   17708             :                  errmsg("cannot alter system column \"%s\"", column)));
   17709             : 
   17710             :     /*
   17711             :      * Check that column type is compressible, then get the attribute
   17712             :      * compression method code
   17713             :      */
   17714          66 :     cmethod = GetAttributeCompression(atttableform->atttypid, compression);
   17715             : 
   17716             :     /* update pg_attribute entry */
   17717          60 :     atttableform->attcompression = cmethod;
   17718          60 :     CatalogTupleUpdate(attrel, &tuple->t_self, tuple);
   17719             : 
   17720          60 :     InvokeObjectPostAlterHook(RelationRelationId,
   17721             :                               RelationGetRelid(rel),
   17722             :                               attnum);
   17723             : 
   17724             :     /*
   17725             :      * Apply the change to indexes as well (only for simple index columns,
   17726             :      * matching behavior of index.c ConstructTupleDescriptor()).
   17727             :      */
   17728          60 :     SetIndexStorageProperties(rel, attrel, attnum,
   17729             :                               false, 0,
   17730             :                               true, cmethod,
   17731             :                               lockmode);
   17732             : 
   17733          60 :     heap_freetuple(tuple);
   17734             : 
   17735          60 :     table_close(attrel, RowExclusiveLock);
   17736             : 
   17737             :     /* make changes visible */
   17738          60 :     CommandCounterIncrement();
   17739             : 
   17740          60 :     ObjectAddressSubSet(address, RelationRelationId,
   17741             :                         RelationGetRelid(rel), attnum);
   17742          60 :     return address;
   17743             : }
   17744             : 
   17745             : 
   17746             : /*
   17747             :  * Preparation phase for SET LOGGED/UNLOGGED
   17748             :  *
   17749             :  * This verifies that we're not trying to change a temp table.  Also,
   17750             :  * existing foreign key constraints are checked to avoid ending up with
   17751             :  * permanent tables referencing unlogged tables.
   17752             :  *
   17753             :  * Return value is false if the operation is a no-op (in which case the
   17754             :  * checks are skipped), otherwise true.
   17755             :  */
   17756             : static bool
   17757          88 : ATPrepChangePersistence(Relation rel, bool toLogged)
   17758             : {
   17759             :     Relation    pg_constraint;
   17760             :     HeapTuple   tuple;
   17761             :     SysScanDesc scan;
   17762             :     ScanKeyData skey[1];
   17763             : 
   17764             :     /*
   17765             :      * Disallow changing status for a temp table.  Also verify whether we can
   17766             :      * get away with doing nothing; in such cases we don't need to run the
   17767             :      * checks below, either.
   17768             :      */
   17769          88 :     switch (rel->rd_rel->relpersistence)
   17770             :     {
   17771           0 :         case RELPERSISTENCE_TEMP:
   17772           0 :             ereport(ERROR,
   17773             :                     (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
   17774             :                      errmsg("cannot change logged status of table \"%s\" because it is temporary",
   17775             :                             RelationGetRelationName(rel)),
   17776             :                      errtable(rel)));
   17777             :             break;
   17778          50 :         case RELPERSISTENCE_PERMANENT:
   17779          50 :             if (toLogged)
   17780             :                 /* nothing to do */
   17781           6 :                 return false;
   17782          44 :             break;
   17783          38 :         case RELPERSISTENCE_UNLOGGED:
   17784          38 :             if (!toLogged)
   17785             :                 /* nothing to do */
   17786           6 :                 return false;
   17787          32 :             break;
   17788             :     }
   17789             : 
   17790             :     /*
   17791             :      * Check that the table is not part of any publication when changing to
   17792             :      * UNLOGGED, as UNLOGGED tables can't be published.
   17793             :      */
   17794         120 :     if (!toLogged &&
   17795          44 :         GetRelationPublications(RelationGetRelid(rel)) != NIL)
   17796           0 :         ereport(ERROR,
   17797             :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
   17798             :                  errmsg("cannot change table \"%s\" to unlogged because it is part of a publication",
   17799             :                         RelationGetRelationName(rel)),
   17800             :                  errdetail("Unlogged relations cannot be replicated.")));
   17801             : 
   17802             :     /*
   17803             :      * Check existing foreign key constraints to preserve the invariant that
   17804             :      * permanent tables cannot reference unlogged ones.  Self-referencing
   17805             :      * foreign keys can safely be ignored.
   17806             :      */
   17807          76 :     pg_constraint = table_open(ConstraintRelationId, AccessShareLock);
   17808             : 
   17809             :     /*
   17810             :      * Scan conrelid if changing to permanent, else confrelid.  This also
   17811             :      * determines whether a useful index exists.
   17812             :      */
   17813          76 :     ScanKeyInit(&skey[0],
   17814             :                 toLogged ? Anum_pg_constraint_conrelid :
   17815             :                 Anum_pg_constraint_confrelid,
   17816             :                 BTEqualStrategyNumber, F_OIDEQ,
   17817             :                 ObjectIdGetDatum(RelationGetRelid(rel)));
   17818          76 :     scan = systable_beginscan(pg_constraint,
   17819             :                               toLogged ? ConstraintRelidTypidNameIndexId : InvalidOid,
   17820             :                               true, NULL, 1, skey);
   17821             : 
   17822         130 :     while (HeapTupleIsValid(tuple = systable_getnext(scan)))
   17823             :     {
   17824          66 :         Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tuple);
   17825             : 
   17826          66 :         if (con->contype == CONSTRAINT_FOREIGN)
   17827             :         {
   17828             :             Oid         foreignrelid;
   17829             :             Relation    foreignrel;
   17830             : 
   17831             :             /* the opposite end of what we used as scankey */
   17832          30 :             foreignrelid = toLogged ? con->confrelid : con->conrelid;
   17833             : 
   17834             :             /* ignore if self-referencing */
   17835          30 :             if (RelationGetRelid(rel) == foreignrelid)
   17836          12 :                 continue;
   17837             : 
   17838          18 :             foreignrel = relation_open(foreignrelid, AccessShareLock);
   17839             : 
   17840          18 :             if (toLogged)
   17841             :             {
   17842           6 :                 if (!RelationIsPermanent(foreignrel))
   17843           6 :                     ereport(ERROR,
   17844             :                             (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
   17845             :                              errmsg("could not change table \"%s\" to logged because it references unlogged table \"%s\"",
   17846             :                                     RelationGetRelationName(rel),
   17847             :                                     RelationGetRelationName(foreignrel)),
   17848             :                              errtableconstraint(rel, NameStr(con->conname))));
   17849             :             }
   17850             :             else
   17851             :             {
   17852          12 :                 if (RelationIsPermanent(foreignrel))
   17853           6 :                     ereport(ERROR,
   17854             :                             (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
   17855             :                              errmsg("could not change table \"%s\" to unlogged because it references logged table \"%s\"",
   17856             :                                     RelationGetRelationName(rel),
   17857             :                                     RelationGetRelationName(foreignrel)),
   17858             :                              errtableconstraint(rel, NameStr(con->conname))));
   17859             :             }
   17860             : 
   17861           6 :             relation_close(foreignrel, AccessShareLock);
   17862             :         }
   17863             :     }
   17864             : 
   17865          64 :     systable_endscan(scan);
   17866             : 
   17867          64 :     table_close(pg_constraint, AccessShareLock);
   17868             : 
   17869          64 :     return true;
   17870             : }
   17871             : 
   17872             : /*
   17873             :  * Execute ALTER TABLE SET SCHEMA
   17874             :  */
   17875             : ObjectAddress
   17876         104 : AlterTableNamespace(AlterObjectSchemaStmt *stmt, Oid *oldschema)
   17877             : {
   17878             :     Relation    rel;
   17879             :     Oid         relid;
   17880             :     Oid         oldNspOid;
   17881             :     Oid         nspOid;
   17882             :     RangeVar   *newrv;
   17883             :     ObjectAddresses *objsMoved;
   17884             :     ObjectAddress myself;
   17885             : 
   17886         104 :     relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
   17887         104 :                                      stmt->missing_ok ? RVR_MISSING_OK : 0,
   17888             :                                      RangeVarCallbackForAlterRelation,
   17889             :                                      (void *) stmt);
   17890             : 
   17891         102 :     if (!OidIsValid(relid))
   17892             :     {
   17893          12 :         ereport(NOTICE,
   17894             :                 (errmsg("relation \"%s\" does not exist, skipping",
   17895             :                         stmt->relation->relname)));
   17896          12 :         return InvalidObjectAddress;
   17897             :     }
   17898             : 
   17899          90 :     rel = relation_open(relid, NoLock);
   17900             : 
   17901          90 :     oldNspOid = RelationGetNamespace(rel);
   17902             : 
   17903             :     /* If it's an owned sequence, disallow moving it by itself. */
   17904          90 :     if (rel->rd_rel->relkind == RELKIND_SEQUENCE)
   17905             :     {
   17906             :         Oid         tableId;
   17907             :         int32       colId;
   17908             : 
   17909          10 :         if (sequenceIsOwned(relid, DEPENDENCY_AUTO, &tableId, &colId) ||
   17910           2 :             sequenceIsOwned(relid, DEPENDENCY_INTERNAL, &tableId, &colId))
   17911           6 :             ereport(ERROR,
   17912             :                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   17913             :                      errmsg("cannot move an owned sequence into another schema"),
   17914             :                      errdetail("Sequence \"%s\" is linked to table \"%s\".",
   17915             :                                RelationGetRelationName(rel),
   17916             :                                get_rel_name(tableId))));
   17917             :     }
   17918             : 
   17919             :     /* Get and lock schema OID and check its permissions. */
   17920          84 :     newrv = makeRangeVar(stmt->newschema, RelationGetRelationName(rel), -1);
   17921          84 :     nspOid = RangeVarGetAndCheckCreationNamespace(newrv, NoLock, NULL);
   17922             : 
   17923             :     /* common checks on switching namespaces */
   17924          84 :     CheckSetNamespace(oldNspOid, nspOid);
   17925             : 
   17926          84 :     objsMoved = new_object_addresses();
   17927          84 :     AlterTableNamespaceInternal(rel, oldNspOid, nspOid, objsMoved);
   17928          84 :     free_object_addresses(objsMoved);
   17929             : 
   17930          84 :     ObjectAddressSet(myself, RelationRelationId, relid);
   17931             : 
   17932          84 :     if (oldschema)
   17933          84 :         *oldschema = oldNspOid;
   17934             : 
   17935             :     /* close rel, but keep lock until commit */
   17936          84 :     relation_close(rel, NoLock);
   17937             : 
   17938          84 :     return myself;
   17939             : }
   17940             : 
   17941             : /*
   17942             :  * The guts of relocating a table or materialized view to another namespace:
   17943             :  * besides moving the relation itself, its dependent objects are relocated to
   17944             :  * the new schema.
   17945             :  */
   17946             : void
   17947          84 : AlterTableNamespaceInternal(Relation rel, Oid oldNspOid, Oid nspOid,
   17948             :                             ObjectAddresses *objsMoved)
   17949             : {
   17950             :     Relation    classRel;
   17951             : 
   17952             :     Assert(objsMoved != NULL);
   17953             : 
   17954             :     /* OK, modify the pg_class row and pg_depend entry */
   17955          84 :     classRel = table_open(RelationRelationId, RowExclusiveLock);
   17956             : 
   17957          84 :     AlterRelationNamespaceInternal(classRel, RelationGetRelid(rel), oldNspOid,
   17958             :                                    nspOid, true, objsMoved);
   17959             : 
   17960             :     /* Fix the table's row type too, if it has one */
   17961          84 :     if (OidIsValid(rel->rd_rel->reltype))
   17962          82 :         AlterTypeNamespaceInternal(rel->rd_rel->reltype,
   17963             :                                    nspOid, false, false, objsMoved);
   17964             : 
   17965             :     /* Fix other dependent stuff */
   17966          84 :     AlterIndexNamespaces(classRel, rel, oldNspOid, nspOid, objsMoved);
   17967          84 :     AlterSeqNamespaces(classRel, rel, oldNspOid, nspOid,
   17968             :                        objsMoved, AccessExclusiveLock);
   17969          84 :     AlterConstraintNamespaces(RelationGetRelid(rel), oldNspOid, nspOid,
   17970             :                               false, objsMoved);
   17971             : 
   17972          84 :     table_close(classRel, RowExclusiveLock);
   17973          84 : }
   17974             : 
   17975             : /*
   17976             :  * The guts of relocating a relation to another namespace: fix the pg_class
   17977             :  * entry, and the pg_depend entry if any.  Caller must already have
   17978             :  * opened and write-locked pg_class.
   17979             :  */
   17980             : void
   17981         182 : AlterRelationNamespaceInternal(Relation classRel, Oid relOid,
   17982             :                                Oid oldNspOid, Oid newNspOid,
   17983             :                                bool hasDependEntry,
   17984             :                                ObjectAddresses *objsMoved)
   17985             : {
   17986             :     HeapTuple   classTup;
   17987             :     Form_pg_class classForm;
   17988             :     ObjectAddress thisobj;
   17989         182 :     bool        already_done = false;
   17990             : 
   17991         182 :     classTup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relOid));
   17992         182 :     if (!HeapTupleIsValid(classTup))
   17993           0 :         elog(ERROR, "cache lookup failed for relation %u", relOid);
   17994         182 :     classForm = (Form_pg_class) GETSTRUCT(classTup);
   17995             : 
   17996             :     Assert(classForm->relnamespace == oldNspOid);
   17997             : 
   17998         182 :     thisobj.classId = RelationRelationId;
   17999         182 :     thisobj.objectId = relOid;
   18000         182 :     thisobj.objectSubId = 0;
   18001             : 
   18002             :     /*
   18003             :      * If the object has already been moved, don't move it again.  If it's
   18004             :      * already in the right place, don't move it, but still fire the object
   18005             :      * access hook.
   18006             :      */
   18007         182 :     already_done = object_address_present(&thisobj, objsMoved);
   18008         182 :     if (!already_done && oldNspOid != newNspOid)
   18009             :     {
   18010             :         /* check for duplicate name (more friendly than unique-index failure) */
   18011         140 :         if (get_relname_relid(NameStr(classForm->relname),
   18012             :                               newNspOid) != InvalidOid)
   18013           0 :             ereport(ERROR,
   18014             :                     (errcode(ERRCODE_DUPLICATE_TABLE),
   18015             :                      errmsg("relation \"%s\" already exists in schema \"%s\"",
   18016             :                             NameStr(classForm->relname),
   18017             :                             get_namespace_name(newNspOid))));
   18018             : 
   18019             :         /* classTup is a copy, so OK to scribble on */
   18020         140 :         classForm->relnamespace = newNspOid;
   18021             : 
   18022         140 :         CatalogTupleUpdate(classRel, &classTup->t_self, classTup);
   18023             : 
   18024             :         /* Update dependency on schema if caller said so */
   18025         242 :         if (hasDependEntry &&
   18026         102 :             changeDependencyFor(RelationRelationId,
   18027             :                                 relOid,
   18028             :                                 NamespaceRelationId,
   18029             :                                 oldNspOid,
   18030             :                                 newNspOid) != 1)
   18031           0 :             elog(ERROR, "could not change schema dependency for relation \"%s\"",
   18032             :                  NameStr(classForm->relname));
   18033             :     }
   18034         182 :     if (!already_done)
   18035             :     {
   18036         182 :         add_exact_object_address(&thisobj, objsMoved);
   18037             : 
   18038         182 :         InvokeObjectPostAlterHook(RelationRelationId, relOid, 0);
   18039             :     }
   18040             : 
   18041         182 :     heap_freetuple(classTup);
   18042         182 : }
   18043             : 
   18044             : /*
   18045             :  * Move all indexes for the specified relation to another namespace.
   18046             :  *
   18047             :  * Note: we assume adequate permission checking was done by the caller,
   18048             :  * and that the caller has a suitable lock on the owning relation.
   18049             :  */
   18050             : static void
   18051          84 : AlterIndexNamespaces(Relation classRel, Relation rel,
   18052             :                      Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved)
   18053             : {
   18054             :     List       *indexList;
   18055             :     ListCell   *l;
   18056             : 
   18057          84 :     indexList = RelationGetIndexList(rel);
   18058             : 
   18059         128 :     foreach(l, indexList)
   18060             :     {
   18061          44 :         Oid         indexOid = lfirst_oid(l);
   18062             :         ObjectAddress thisobj;
   18063             : 
   18064          44 :         thisobj.classId = RelationRelationId;
   18065          44 :         thisobj.objectId = indexOid;
   18066          44 :         thisobj.objectSubId = 0;
   18067             : 
   18068             :         /*
   18069             :          * Note: currently, the index will not have its own dependency on the
   18070             :          * namespace, so we don't need to do changeDependencyFor(). There's no
   18071             :          * row type in pg_type, either.
   18072             :          *
   18073             :          * XXX this objsMoved test may be pointless -- surely we have a single
   18074             :          * dependency link from a relation to each index?
   18075             :          */
   18076          44 :         if (!object_address_present(&thisobj, objsMoved))
   18077             :         {
   18078          44 :             AlterRelationNamespaceInternal(classRel, indexOid,
   18079             :                                            oldNspOid, newNspOid,
   18080             :                                            false, objsMoved);
   18081          44 :             add_exact_object_address(&thisobj, objsMoved);
   18082             :         }
   18083             :     }
   18084             : 
   18085          84 :     list_free(indexList);
   18086          84 : }
   18087             : 
   18088             : /*
   18089             :  * Move all identity and SERIAL-column sequences of the specified relation to another
   18090             :  * namespace.
   18091             :  *
   18092             :  * Note: we assume adequate permission checking was done by the caller,
   18093             :  * and that the caller has a suitable lock on the owning relation.
   18094             :  */
   18095             : static void
   18096          84 : AlterSeqNamespaces(Relation classRel, Relation rel,
   18097             :                    Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved,
   18098             :                    LOCKMODE lockmode)
   18099             : {
   18100             :     Relation    depRel;
   18101             :     SysScanDesc scan;
   18102             :     ScanKeyData key[2];
   18103             :     HeapTuple   tup;
   18104             : 
   18105             :     /*
   18106             :      * SERIAL sequences are those having an auto dependency on one of the
   18107             :      * table's columns (we don't care *which* column, exactly).
   18108             :      */
   18109          84 :     depRel = table_open(DependRelationId, AccessShareLock);
   18110             : 
   18111          84 :     ScanKeyInit(&key[0],
   18112             :                 Anum_pg_depend_refclassid,
   18113             :                 BTEqualStrategyNumber, F_OIDEQ,
   18114             :                 ObjectIdGetDatum(RelationRelationId));
   18115          84 :     ScanKeyInit(&key[1],
   18116             :                 Anum_pg_depend_refobjid,
   18117             :                 BTEqualStrategyNumber, F_OIDEQ,
   18118             :                 ObjectIdGetDatum(RelationGetRelid(rel)));
   18119             :     /* we leave refobjsubid unspecified */
   18120             : 
   18121          84 :     scan = systable_beginscan(depRel, DependReferenceIndexId, true,
   18122             :                               NULL, 2, key);
   18123             : 
   18124         588 :     while (HeapTupleIsValid(tup = systable_getnext(scan)))
   18125             :     {
   18126         504 :         Form_pg_depend depForm = (Form_pg_depend) GETSTRUCT(tup);
   18127             :         Relation    seqRel;
   18128             : 
   18129             :         /* skip dependencies other than auto dependencies on columns */
   18130         504 :         if (depForm->refobjsubid == 0 ||
   18131         360 :             depForm->classid != RelationRelationId ||
   18132          42 :             depForm->objsubid != 0 ||
   18133          42 :             !(depForm->deptype == DEPENDENCY_AUTO || depForm->deptype == DEPENDENCY_INTERNAL))
   18134         462 :             continue;
   18135             : 
   18136             :         /* Use relation_open just in case it's an index */
   18137          42 :         seqRel = relation_open(depForm->objid, lockmode);
   18138             : 
   18139             :         /* skip non-sequence relations */
   18140          42 :         if (RelationGetForm(seqRel)->relkind != RELKIND_SEQUENCE)
   18141             :         {
   18142             :             /* No need to keep the lock */
   18143           0 :             relation_close(seqRel, lockmode);
   18144           0 :             continue;
   18145             :         }
   18146             : 
   18147             :         /* Fix the pg_class and pg_depend entries */
   18148          42 :         AlterRelationNamespaceInternal(classRel, depForm->objid,
   18149             :                                        oldNspOid, newNspOid,
   18150             :                                        true, objsMoved);
   18151             : 
   18152             :         /*
   18153             :          * Sequences used to have entries in pg_type, but no longer do.  If we
   18154             :          * ever re-instate that, we'll need to move the pg_type entry to the
   18155             :          * new namespace, too (using AlterTypeNamespaceInternal).
   18156             :          */
   18157             :         Assert(RelationGetForm(seqRel)->reltype == InvalidOid);
   18158             : 
   18159             :         /* Now we can close it.  Keep the lock till end of transaction. */
   18160          42 :         relation_close(seqRel, NoLock);
   18161             :     }
   18162             : 
   18163          84 :     systable_endscan(scan);
   18164             : 
   18165          84 :     relation_close(depRel, AccessShareLock);
   18166          84 : }
   18167             : 
   18168             : 
   18169             : /*
   18170             :  * This code supports
   18171             :  *  CREATE TEMP TABLE ... ON COMMIT { DROP | PRESERVE ROWS | DELETE ROWS }
   18172             :  *
   18173             :  * Because we only support this for TEMP tables, it's sufficient to remember
   18174             :  * the state in a backend-local data structure.
   18175             :  */
   18176             : 
   18177             : /*
   18178             :  * Register a newly-created relation's ON COMMIT action.
   18179             :  */
   18180             : void
   18181         166 : register_on_commit_action(Oid relid, OnCommitAction action)
   18182             : {
   18183             :     OnCommitItem *oc;
   18184             :     MemoryContext oldcxt;
   18185             : 
   18186             :     /*
   18187             :      * We needn't bother registering the relation unless there is an ON COMMIT
   18188             :      * action we need to take.
   18189             :      */
   18190         166 :     if (action == ONCOMMIT_NOOP || action == ONCOMMIT_PRESERVE_ROWS)
   18191          24 :         return;
   18192             : 
   18193         142 :     oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
   18194             : 
   18195         142 :     oc = (OnCommitItem *) palloc(sizeof(OnCommitItem));
   18196         142 :     oc->relid = relid;
   18197         142 :     oc->oncommit = action;
   18198         142 :     oc->creating_subid = GetCurrentSubTransactionId();
   18199         142 :     oc->deleting_subid = InvalidSubTransactionId;
   18200             : 
   18201             :     /*
   18202             :      * We use lcons() here so that ON COMMIT actions are processed in reverse
   18203             :      * order of registration.  That might not be essential but it seems
   18204             :      * reasonable.
   18205             :      */
   18206         142 :     on_commits = lcons(oc, on_commits);
   18207             : 
   18208         142 :     MemoryContextSwitchTo(oldcxt);
   18209             : }
   18210             : 
   18211             : /*
   18212             :  * Unregister any ON COMMIT action when a relation is deleted.
   18213             :  *
   18214             :  * Actually, we only mark the OnCommitItem entry as to be deleted after commit.
   18215             :  */
   18216             : void
   18217       42324 : remove_on_commit_action(Oid relid)
   18218             : {
   18219             :     ListCell   *l;
   18220             : 
   18221       42458 :     foreach(l, on_commits)
   18222             :     {
   18223         264 :         OnCommitItem *oc = (OnCommitItem *) lfirst(l);
   18224             : 
   18225         264 :         if (oc->relid == relid)
   18226             :         {
   18227         130 :             oc->deleting_subid = GetCurrentSubTransactionId();
   18228         130 :             break;
   18229             :         }
   18230             :     }
   18231       42324 : }
   18232             : 
   18233             : /*
   18234             :  * Perform ON COMMIT actions.
   18235             :  *
   18236             :  * This is invoked just before actually committing, since it's possible
   18237             :  * to encounter errors.
   18238             :  */
   18239             : void
   18240      520040 : PreCommit_on_commit_actions(void)
   18241             : {
   18242             :     ListCell   *l;
   18243      520040 :     List       *oids_to_truncate = NIL;
   18244      520040 :     List       *oids_to_drop = NIL;
   18245             : 
   18246      520756 :     foreach(l, on_commits)
   18247             :     {
   18248         716 :         OnCommitItem *oc = (OnCommitItem *) lfirst(l);
   18249             : 
   18250             :         /* Ignore entry if already dropped in this xact */
   18251         716 :         if (oc->deleting_subid != InvalidSubTransactionId)
   18252          68 :             continue;
   18253             : 
   18254         648 :         switch (oc->oncommit)
   18255             :         {
   18256           0 :             case ONCOMMIT_NOOP:
   18257             :             case ONCOMMIT_PRESERVE_ROWS:
   18258             :                 /* Do nothing (there shouldn't be such entries, actually) */
   18259           0 :                 break;
   18260         598 :             case ONCOMMIT_DELETE_ROWS:
   18261             : 
   18262             :                 /*
   18263             :                  * If this transaction hasn't accessed any temporary
   18264             :                  * relations, we can skip truncating ON COMMIT DELETE ROWS
   18265             :                  * tables, as they must still be empty.
   18266             :                  */
   18267         598 :                 if ((MyXactFlags & XACT_FLAGS_ACCESSEDTEMPNAMESPACE))
   18268         400 :                     oids_to_truncate = lappend_oid(oids_to_truncate, oc->relid);
   18269         598 :                 break;
   18270          50 :             case ONCOMMIT_DROP:
   18271          50 :                 oids_to_drop = lappend_oid(oids_to_drop, oc->relid);
   18272          50 :                 break;
   18273             :         }
   18274         716 :     }
   18275             : 
   18276             :     /*
   18277             :      * Truncate relations before dropping so that all dependencies between
   18278             :      * relations are removed after they are worked on.  Doing it like this
   18279             :      * might be a waste as it is possible that a relation being truncated will
   18280             :      * be dropped anyway due to its parent being dropped, but this makes the
   18281             :      * code more robust because of not having to re-check that the relation
   18282             :      * exists at truncation time.
   18283             :      */
   18284      520040 :     if (oids_to_truncate != NIL)
   18285         334 :         heap_truncate(oids_to_truncate);
   18286             : 
   18287      520034 :     if (oids_to_drop != NIL)
   18288             :     {
   18289          44 :         ObjectAddresses *targetObjects = new_object_addresses();
   18290             : 
   18291          94 :         foreach(l, oids_to_drop)
   18292             :         {
   18293             :             ObjectAddress object;
   18294             : 
   18295          50 :             object.classId = RelationRelationId;
   18296          50 :             object.objectId = lfirst_oid(l);
   18297          50 :             object.objectSubId = 0;
   18298             : 
   18299             :             Assert(!object_address_present(&object, targetObjects));
   18300             : 
   18301          50 :             add_exact_object_address(&object, targetObjects);
   18302             :         }
   18303             : 
   18304             :         /*
   18305             :          * Object deletion might involve toast table access (to clean up
   18306             :          * toasted catalog entries), so ensure we have a valid snapshot.
   18307             :          */
   18308          44 :         PushActiveSnapshot(GetTransactionSnapshot());
   18309             : 
   18310             :         /*
   18311             :          * Since this is an automatic drop, rather than one directly initiated
   18312             :          * by the user, we pass the PERFORM_DELETION_INTERNAL flag.
   18313             :          */
   18314          44 :         performMultipleDeletions(targetObjects, DROP_CASCADE,
   18315             :                                  PERFORM_DELETION_INTERNAL | PERFORM_DELETION_QUIETLY);
   18316             : 
   18317          44 :         PopActiveSnapshot();
   18318             : 
   18319             : #ifdef USE_ASSERT_CHECKING
   18320             : 
   18321             :         /*
   18322             :          * Note that table deletion will call remove_on_commit_action, so the
   18323             :          * entry should get marked as deleted.
   18324             :          */
   18325             :         foreach(l, on_commits)
   18326             :         {
   18327             :             OnCommitItem *oc = (OnCommitItem *) lfirst(l);
   18328             : 
   18329             :             if (oc->oncommit != ONCOMMIT_DROP)
   18330             :                 continue;
   18331             : 
   18332             :             Assert(oc->deleting_subid != InvalidSubTransactionId);
   18333             :         }
   18334             : #endif
   18335             :     }
   18336      520034 : }
   18337             : 
   18338             : /*
   18339             :  * Post-commit or post-abort cleanup for ON COMMIT management.
   18340             :  *
   18341             :  * All we do here is remove no-longer-needed OnCommitItem entries.
   18342             :  *
   18343             :  * During commit, remove entries that were deleted during this transaction;
   18344             :  * during abort, remove those created during this transaction.
   18345             :  */
   18346             : void
   18347      564762 : AtEOXact_on_commit_actions(bool isCommit)
   18348             : {
   18349             :     ListCell   *cur_item;
   18350             : 
   18351      565508 :     foreach(cur_item, on_commits)
   18352             :     {
   18353         746 :         OnCommitItem *oc = (OnCommitItem *) lfirst(cur_item);
   18354             : 
   18355         848 :         if (isCommit ? oc->deleting_subid != InvalidSubTransactionId :
   18356         102 :             oc->creating_subid != InvalidSubTransactionId)
   18357             :         {
   18358             :             /* cur_item must be removed */
   18359         142 :             on_commits = foreach_delete_current(on_commits, cur_item);
   18360         142 :             pfree(oc);
   18361             :         }
   18362             :         else
   18363             :         {
   18364             :             /* cur_item must be preserved */
   18365         604 :             oc->creating_subid = InvalidSubTransactionId;
   18366         604 :             oc->deleting_subid = InvalidSubTransactionId;
   18367             :         }
   18368             :     }
   18369      564762 : }
   18370             : 
   18371             : /*
   18372             :  * Post-subcommit or post-subabort cleanup for ON COMMIT management.
   18373             :  *
   18374             :  * During subabort, we can immediately remove entries created during this
   18375             :  * subtransaction.  During subcommit, just relabel entries marked during
   18376             :  * this subtransaction as being the parent's responsibility.
   18377             :  */
   18378             : void
   18379       17968 : AtEOSubXact_on_commit_actions(bool isCommit, SubTransactionId mySubid,
   18380             :                               SubTransactionId parentSubid)
   18381             : {
   18382             :     ListCell   *cur_item;
   18383             : 
   18384       17968 :     foreach(cur_item, on_commits)
   18385             :     {
   18386           0 :         OnCommitItem *oc = (OnCommitItem *) lfirst(cur_item);
   18387             : 
   18388           0 :         if (!isCommit && oc->creating_subid == mySubid)
   18389             :         {
   18390             :             /* cur_item must be removed */
   18391           0 :             on_commits = foreach_delete_current(on_commits, cur_item);
   18392           0 :             pfree(oc);
   18393             :         }
   18394             :         else
   18395             :         {
   18396             :             /* cur_item must be preserved */
   18397           0 :             if (oc->creating_subid == mySubid)
   18398           0 :                 oc->creating_subid = parentSubid;
   18399           0 :             if (oc->deleting_subid == mySubid)
   18400           0 :                 oc->deleting_subid = isCommit ? parentSubid : InvalidSubTransactionId;
   18401             :         }
   18402             :     }
   18403       17968 : }
   18404             : 
   18405             : /*
   18406             :  * This is intended as a callback for RangeVarGetRelidExtended().  It allows
   18407             :  * the relation to be locked only if (1) it's a plain or partitioned table,
   18408             :  * materialized view, or TOAST table and (2) the current user is the owner (or
   18409             :  * the superuser) or has been granted MAINTAIN.  This meets the
   18410             :  * permission-checking needs of CLUSTER, REINDEX TABLE, and REFRESH
   18411             :  * MATERIALIZED VIEW; we expose it here so that it can be used by all.
   18412             :  */
   18413             : void
   18414         984 : RangeVarCallbackMaintainsTable(const RangeVar *relation,
   18415             :                                Oid relId, Oid oldRelId, void *arg)
   18416             : {
   18417             :     char        relkind;
   18418             :     AclResult   aclresult;
   18419             : 
   18420             :     /* Nothing to do if the relation was not found. */
   18421         984 :     if (!OidIsValid(relId))
   18422           6 :         return;
   18423             : 
   18424             :     /*
   18425             :      * If the relation does exist, check whether it's an index.  But note that
   18426             :      * the relation might have been dropped between the time we did the name
   18427             :      * lookup and now.  In that case, there's nothing to do.
   18428             :      */
   18429         978 :     relkind = get_rel_relkind(relId);
   18430         978 :     if (!relkind)
   18431           0 :         return;
   18432         978 :     if (relkind != RELKIND_RELATION && relkind != RELKIND_TOASTVALUE &&
   18433         136 :         relkind != RELKIND_MATVIEW && relkind != RELKIND_PARTITIONED_TABLE)
   18434          28 :         ereport(ERROR,
   18435             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   18436             :                  errmsg("\"%s\" is not a table or materialized view", relation->relname)));
   18437             : 
   18438             :     /* Check permissions */
   18439         950 :     aclresult = pg_class_aclcheck(relId, GetUserId(), ACL_MAINTAIN);
   18440         950 :     if (aclresult != ACLCHECK_OK)
   18441          30 :         aclcheck_error(aclresult,
   18442          30 :                        get_relkind_objtype(get_rel_relkind(relId)),
   18443          30 :                        relation->relname);
   18444             : }
   18445             : 
   18446             : /*
   18447             :  * Callback to RangeVarGetRelidExtended() for TRUNCATE processing.
   18448             :  */
   18449             : static void
   18450        1794 : RangeVarCallbackForTruncate(const RangeVar *relation,
   18451             :                             Oid relId, Oid oldRelId, void *arg)
   18452             : {
   18453             :     HeapTuple   tuple;
   18454             : 
   18455             :     /* Nothing to do if the relation was not found. */
   18456        1794 :     if (!OidIsValid(relId))
   18457           0 :         return;
   18458             : 
   18459        1794 :     tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relId));
   18460        1794 :     if (!HeapTupleIsValid(tuple))   /* should not happen */
   18461           0 :         elog(ERROR, "cache lookup failed for relation %u", relId);
   18462             : 
   18463        1794 :     truncate_check_rel(relId, (Form_pg_class) GETSTRUCT(tuple));
   18464        1790 :     truncate_check_perms(relId, (Form_pg_class) GETSTRUCT(tuple));
   18465             : 
   18466        1758 :     ReleaseSysCache(tuple);
   18467             : }
   18468             : 
   18469             : /*
   18470             :  * Callback for RangeVarGetRelidExtended().  Checks that the current user is
   18471             :  * the owner of the relation, or superuser.
   18472             :  */
   18473             : void
   18474       14796 : RangeVarCallbackOwnsRelation(const RangeVar *relation,
   18475             :                              Oid relId, Oid oldRelId, void *arg)
   18476             : {
   18477             :     HeapTuple   tuple;
   18478             : 
   18479             :     /* Nothing to do if the relation was not found. */
   18480       14796 :     if (!OidIsValid(relId))
   18481          12 :         return;
   18482             : 
   18483       14784 :     tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relId));
   18484       14784 :     if (!HeapTupleIsValid(tuple))   /* should not happen */
   18485           0 :         elog(ERROR, "cache lookup failed for relation %u", relId);
   18486             : 
   18487       14784 :     if (!object_ownercheck(RelationRelationId, relId, GetUserId()))
   18488           6 :         aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relId)),
   18489           6 :                        relation->relname);
   18490             : 
   18491       29436 :     if (!allowSystemTableMods &&
   18492       14658 :         IsSystemClass(relId, (Form_pg_class) GETSTRUCT(tuple)))
   18493           2 :         ereport(ERROR,
   18494             :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
   18495             :                  errmsg("permission denied: \"%s\" is a system catalog",
   18496             :                         relation->relname)));
   18497             : 
   18498       14776 :     ReleaseSysCache(tuple);
   18499             : }
   18500             : 
   18501             : /*
   18502             :  * Common RangeVarGetRelid callback for rename, set schema, and alter table
   18503             :  * processing.
   18504             :  */
   18505             : static void
   18506       35940 : RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid, Oid oldrelid,
   18507             :                                  void *arg)
   18508             : {
   18509       35940 :     Node       *stmt = (Node *) arg;
   18510             :     ObjectType  reltype;
   18511             :     HeapTuple   tuple;
   18512             :     Form_pg_class classform;
   18513             :     AclResult   aclresult;
   18514             :     char        relkind;
   18515             : 
   18516       35940 :     tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
   18517       35940 :     if (!HeapTupleIsValid(tuple))
   18518         228 :         return;                 /* concurrently dropped */
   18519       35712 :     classform = (Form_pg_class) GETSTRUCT(tuple);
   18520       35712 :     relkind = classform->relkind;
   18521             : 
   18522             :     /* Must own relation. */
   18523       35712 :     if (!object_ownercheck(RelationRelationId, relid, GetUserId()))
   18524          60 :         aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relid)), rv->relname);
   18525             : 
   18526             :     /* No system table modifications unless explicitly allowed. */
   18527       35652 :     if (!allowSystemTableMods && IsSystemClass(relid, classform))
   18528          28 :         ereport(ERROR,
   18529             :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
   18530             :                  errmsg("permission denied: \"%s\" is a system catalog",
   18531             :                         rv->relname)));
   18532             : 
   18533             :     /*
   18534             :      * Extract the specified relation type from the statement parse tree.
   18535             :      *
   18536             :      * Also, for ALTER .. RENAME, check permissions: the user must (still)
   18537             :      * have CREATE rights on the containing namespace.
   18538             :      */
   18539       35624 :     if (IsA(stmt, RenameStmt))
   18540             :     {
   18541         476 :         aclresult = object_aclcheck(NamespaceRelationId, classform->relnamespace,
   18542             :                                     GetUserId(), ACL_CREATE);
   18543         476 :         if (aclresult != ACLCHECK_OK)
   18544           0 :             aclcheck_error(aclresult, OBJECT_SCHEMA,
   18545           0 :                            get_namespace_name(classform->relnamespace));
   18546         476 :         reltype = ((RenameStmt *) stmt)->renameType;
   18547             :     }
   18548       35148 :     else if (IsA(stmt, AlterObjectSchemaStmt))
   18549          90 :         reltype = ((AlterObjectSchemaStmt *) stmt)->objectType;
   18550             : 
   18551       35058 :     else if (IsA(stmt, AlterTableStmt))
   18552       35058 :         reltype = ((AlterTableStmt *) stmt)->objtype;
   18553             :     else
   18554             :     {
   18555           0 :         elog(ERROR, "unrecognized node type: %d", (int) nodeTag(stmt));
   18556             :         reltype = OBJECT_TABLE; /* placate compiler */
   18557             :     }
   18558             : 
   18559             :     /*
   18560             :      * For compatibility with prior releases, we allow ALTER TABLE to be used
   18561             :      * with most other types of relations (but not composite types). We allow
   18562             :      * similar flexibility for ALTER INDEX in the case of RENAME, but not
   18563             :      * otherwise.  Otherwise, the user must select the correct form of the
   18564             :      * command for the relation at issue.
   18565             :      */
   18566       35624 :     if (reltype == OBJECT_SEQUENCE && relkind != RELKIND_SEQUENCE)
   18567           0 :         ereport(ERROR,
   18568             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   18569             :                  errmsg("\"%s\" is not a sequence", rv->relname)));
   18570             : 
   18571       35624 :     if (reltype == OBJECT_VIEW && relkind != RELKIND_VIEW)
   18572           0 :         ereport(ERROR,
   18573             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   18574             :                  errmsg("\"%s\" is not a view", rv->relname)));
   18575             : 
   18576       35624 :     if (reltype == OBJECT_MATVIEW && relkind != RELKIND_MATVIEW)
   18577           0 :         ereport(ERROR,
   18578             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   18579             :                  errmsg("\"%s\" is not a materialized view", rv->relname)));
   18580             : 
   18581       35624 :     if (reltype == OBJECT_FOREIGN_TABLE && relkind != RELKIND_FOREIGN_TABLE)
   18582           0 :         ereport(ERROR,
   18583             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   18584             :                  errmsg("\"%s\" is not a foreign table", rv->relname)));
   18585             : 
   18586       35624 :     if (reltype == OBJECT_TYPE && relkind != RELKIND_COMPOSITE_TYPE)
   18587           0 :         ereport(ERROR,
   18588             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   18589             :                  errmsg("\"%s\" is not a composite type", rv->relname)));
   18590             : 
   18591       35624 :     if (reltype == OBJECT_INDEX && relkind != RELKIND_INDEX &&
   18592             :         relkind != RELKIND_PARTITIONED_INDEX
   18593          34 :         && !IsA(stmt, RenameStmt))
   18594           6 :         ereport(ERROR,
   18595             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   18596             :                  errmsg("\"%s\" is not an index", rv->relname)));
   18597             : 
   18598             :     /*
   18599             :      * Don't allow ALTER TABLE on composite types. We want people to use ALTER
   18600             :      * TYPE for that.
   18601             :      */
   18602       35618 :     if (reltype != OBJECT_TYPE && relkind == RELKIND_COMPOSITE_TYPE)
   18603           0 :         ereport(ERROR,
   18604             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   18605             :                  errmsg("\"%s\" is a composite type", rv->relname),
   18606             :         /* translator: %s is an SQL ALTER command */
   18607             :                  errhint("Use %s instead.",
   18608             :                          "ALTER TYPE")));
   18609             : 
   18610             :     /*
   18611             :      * Don't allow ALTER TABLE .. SET SCHEMA on relations that can't be moved
   18612             :      * to a different schema, such as indexes and TOAST tables.
   18613             :      */
   18614       35618 :     if (IsA(stmt, AlterObjectSchemaStmt))
   18615             :     {
   18616          90 :         if (relkind == RELKIND_INDEX || relkind == RELKIND_PARTITIONED_INDEX)
   18617           0 :             ereport(ERROR,
   18618             :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   18619             :                      errmsg("cannot change schema of index \"%s\"",
   18620             :                             rv->relname),
   18621             :                      errhint("Change the schema of the table instead.")));
   18622          90 :         else if (relkind == RELKIND_COMPOSITE_TYPE)
   18623           0 :             ereport(ERROR,
   18624             :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   18625             :                      errmsg("cannot change schema of composite type \"%s\"",
   18626             :                             rv->relname),
   18627             :             /* translator: %s is an SQL ALTER command */
   18628             :                      errhint("Use %s instead.",
   18629             :                              "ALTER TYPE")));
   18630          90 :         else if (relkind == RELKIND_TOASTVALUE)
   18631           0 :             ereport(ERROR,
   18632             :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   18633             :                      errmsg("cannot change schema of TOAST table \"%s\"",
   18634             :                             rv->relname),
   18635             :                      errhint("Change the schema of the table instead.")));
   18636             :     }
   18637             : 
   18638       35618 :     ReleaseSysCache(tuple);
   18639             : }
   18640             : 
   18641             : /*
   18642             :  * Transform any expressions present in the partition key
   18643             :  *
   18644             :  * Returns a transformed PartitionSpec.
   18645             :  */
   18646             : static PartitionSpec *
   18647        4858 : transformPartitionSpec(Relation rel, PartitionSpec *partspec)
   18648             : {
   18649             :     PartitionSpec *newspec;
   18650             :     ParseState *pstate;
   18651             :     ParseNamespaceItem *nsitem;
   18652             :     ListCell   *l;
   18653             : 
   18654        4858 :     newspec = makeNode(PartitionSpec);
   18655             : 
   18656        4858 :     newspec->strategy = partspec->strategy;
   18657        4858 :     newspec->partParams = NIL;
   18658        4858 :     newspec->location = partspec->location;
   18659             : 
   18660             :     /* Check valid number of columns for strategy */
   18661        7194 :     if (partspec->strategy == PARTITION_STRATEGY_LIST &&
   18662        2336 :         list_length(partspec->partParams) != 1)
   18663           6 :         ereport(ERROR,
   18664             :                 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
   18665             :                  errmsg("cannot use \"list\" partition strategy with more than one column")));
   18666             : 
   18667             :     /*
   18668             :      * Create a dummy ParseState and insert the target relation as its sole
   18669             :      * rangetable entry.  We need a ParseState for transformExpr.
   18670             :      */
   18671        4852 :     pstate = make_parsestate(NULL);
   18672        4852 :     nsitem = addRangeTableEntryForRelation(pstate, rel, AccessShareLock,
   18673             :                                            NULL, false, true);
   18674        4852 :     addNSItemToQuery(pstate, nsitem, true, true, true);
   18675             : 
   18676             :     /* take care of any partition expressions */
   18677       10136 :     foreach(l, partspec->partParams)
   18678             :     {
   18679        5308 :         PartitionElem *pelem = lfirst_node(PartitionElem, l);
   18680             : 
   18681        5308 :         if (pelem->expr)
   18682             :         {
   18683             :             /* Copy, to avoid scribbling on the input */
   18684         298 :             pelem = copyObject(pelem);
   18685             : 
   18686             :             /* Now do parse transformation of the expression */
   18687         298 :             pelem->expr = transformExpr(pstate, pelem->expr,
   18688             :                                         EXPR_KIND_PARTITION_EXPRESSION);
   18689             : 
   18690             :             /* we have to fix its collations too */
   18691         274 :             assign_expr_collations(pstate, pelem->expr);
   18692             :         }
   18693             : 
   18694        5284 :         newspec->partParams = lappend(newspec->partParams, pelem);
   18695             :     }
   18696             : 
   18697        4828 :     return newspec;
   18698             : }
   18699             : 
   18700             : /*
   18701             :  * Compute per-partition-column information from a list of PartitionElems.
   18702             :  * Expressions in the PartitionElems must be parse-analyzed already.
   18703             :  */
   18704             : static void
   18705        4828 : ComputePartitionAttrs(ParseState *pstate, Relation rel, List *partParams, AttrNumber *partattrs,
   18706             :                       List **partexprs, Oid *partopclass, Oid *partcollation,
   18707             :                       PartitionStrategy strategy)
   18708             : {
   18709             :     int         attn;
   18710             :     ListCell   *lc;
   18711             :     Oid         am_oid;
   18712             : 
   18713        4828 :     attn = 0;
   18714       10028 :     foreach(lc, partParams)
   18715             :     {
   18716        5284 :         PartitionElem *pelem = lfirst_node(PartitionElem, lc);
   18717             :         Oid         atttype;
   18718             :         Oid         attcollation;
   18719             : 
   18720        5284 :         if (pelem->name != NULL)
   18721             :         {
   18722             :             /* Simple attribute reference */
   18723             :             HeapTuple   atttuple;
   18724             :             Form_pg_attribute attform;
   18725             : 
   18726        5010 :             atttuple = SearchSysCacheAttName(RelationGetRelid(rel),
   18727        5010 :                                              pelem->name);
   18728        5010 :             if (!HeapTupleIsValid(atttuple))
   18729          12 :                 ereport(ERROR,
   18730             :                         (errcode(ERRCODE_UNDEFINED_COLUMN),
   18731             :                          errmsg("column \"%s\" named in partition key does not exist",
   18732             :                                 pelem->name),
   18733             :                          parser_errposition(pstate, pelem->location)));
   18734        4998 :             attform = (Form_pg_attribute) GETSTRUCT(atttuple);
   18735             : 
   18736        4998 :             if (attform->attnum <= 0)
   18737           6 :                 ereport(ERROR,
   18738             :                         (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
   18739             :                          errmsg("cannot use system column \"%s\" in partition key",
   18740             :                                 pelem->name),
   18741             :                          parser_errposition(pstate, pelem->location)));
   18742             : 
   18743             :             /*
   18744             :              * Generated columns cannot work: They are computed after BEFORE
   18745             :              * triggers, but partition routing is done before all triggers.
   18746             :              */
   18747        4992 :             if (attform->attgenerated)
   18748           6 :                 ereport(ERROR,
   18749             :                         (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
   18750             :                          errmsg("cannot use generated column in partition key"),
   18751             :                          errdetail("Column \"%s\" is a generated column.",
   18752             :                                    pelem->name),
   18753             :                          parser_errposition(pstate, pelem->location)));
   18754             : 
   18755        4986 :             partattrs[attn] = attform->attnum;
   18756        4986 :             atttype = attform->atttypid;
   18757        4986 :             attcollation = attform->attcollation;
   18758        4986 :             ReleaseSysCache(atttuple);
   18759             :         }
   18760             :         else
   18761             :         {
   18762             :             /* Expression */
   18763         274 :             Node       *expr = pelem->expr;
   18764             :             char        partattname[16];
   18765             : 
   18766             :             Assert(expr != NULL);
   18767         274 :             atttype = exprType(expr);
   18768         274 :             attcollation = exprCollation(expr);
   18769             : 
   18770             :             /*
   18771             :              * The expression must be of a storable type (e.g., not RECORD).
   18772             :              * The test is the same as for whether a table column is of a safe
   18773             :              * type (which is why we needn't check for the non-expression
   18774             :              * case).
   18775             :              */
   18776         274 :             snprintf(partattname, sizeof(partattname), "%d", attn + 1);
   18777         274 :             CheckAttributeType(partattname,
   18778             :                                atttype, attcollation,
   18779             :                                NIL, CHKATYPE_IS_PARTKEY);
   18780             : 
   18781             :             /*
   18782             :              * Strip any top-level COLLATE clause.  This ensures that we treat
   18783             :              * "x COLLATE y" and "(x COLLATE y)" alike.
   18784             :              */
   18785         262 :             while (IsA(expr, CollateExpr))
   18786           0 :                 expr = (Node *) ((CollateExpr *) expr)->arg;
   18787             : 
   18788         262 :             if (IsA(expr, Var) &&
   18789          12 :                 ((Var *) expr)->varattno > 0)
   18790             :             {
   18791             :                 /*
   18792             :                  * User wrote "(column)" or "(column COLLATE something)".
   18793             :                  * Treat it like simple attribute anyway.
   18794             :                  */
   18795           6 :                 partattrs[attn] = ((Var *) expr)->varattno;
   18796             :             }
   18797             :             else
   18798             :             {
   18799         256 :                 Bitmapset  *expr_attrs = NULL;
   18800             :                 int         i;
   18801             : 
   18802         256 :                 partattrs[attn] = 0;    /* marks the column as expression */
   18803         256 :                 *partexprs = lappend(*partexprs, expr);
   18804             : 
   18805             :                 /*
   18806             :                  * transformPartitionSpec() should have already rejected
   18807             :                  * subqueries, aggregates, window functions, and SRFs, based
   18808             :                  * on the EXPR_KIND_ for partition expressions.
   18809             :                  */
   18810             : 
   18811             :                 /*
   18812             :                  * Cannot allow system column references, since that would
   18813             :                  * make partition routing impossible: their values won't be
   18814             :                  * known yet when we need to do that.
   18815             :                  */
   18816         256 :                 pull_varattnos(expr, 1, &expr_attrs);
   18817        2048 :                 for (i = FirstLowInvalidHeapAttributeNumber; i < 0; i++)
   18818             :                 {
   18819        1792 :                     if (bms_is_member(i - FirstLowInvalidHeapAttributeNumber,
   18820             :                                       expr_attrs))
   18821           0 :                         ereport(ERROR,
   18822             :                                 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
   18823             :                                  errmsg("partition key expressions cannot contain system column references")));
   18824             :                 }
   18825             : 
   18826             :                 /*
   18827             :                  * Generated columns cannot work: They are computed after
   18828             :                  * BEFORE triggers, but partition routing is done before all
   18829             :                  * triggers.
   18830             :                  */
   18831         256 :                 i = -1;
   18832         564 :                 while ((i = bms_next_member(expr_attrs, i)) >= 0)
   18833             :                 {
   18834         314 :                     AttrNumber  attno = i + FirstLowInvalidHeapAttributeNumber;
   18835             : 
   18836         314 :                     if (attno > 0 &&
   18837         308 :                         TupleDescAttr(RelationGetDescr(rel), attno - 1)->attgenerated)
   18838           6 :                         ereport(ERROR,
   18839             :                                 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
   18840             :                                  errmsg("cannot use generated column in partition key"),
   18841             :                                  errdetail("Column \"%s\" is a generated column.",
   18842             :                                            get_attname(RelationGetRelid(rel), attno, false)),
   18843             :                                  parser_errposition(pstate, pelem->location)));
   18844             :                 }
   18845             : 
   18846             :                 /*
   18847             :                  * Preprocess the expression before checking for mutability.
   18848             :                  * This is essential for the reasons described in
   18849             :                  * contain_mutable_functions_after_planning.  However, we call
   18850             :                  * expression_planner for ourselves rather than using that
   18851             :                  * function, because if constant-folding reduces the
   18852             :                  * expression to a constant, we'd like to know that so we can
   18853             :                  * complain below.
   18854             :                  *
   18855             :                  * Like contain_mutable_functions_after_planning, assume that
   18856             :                  * expression_planner won't scribble on its input, so this
   18857             :                  * won't affect the partexprs entry we saved above.
   18858             :                  */
   18859         250 :                 expr = (Node *) expression_planner((Expr *) expr);
   18860             : 
   18861             :                 /*
   18862             :                  * Partition expressions cannot contain mutable functions,
   18863             :                  * because a given row must always map to the same partition
   18864             :                  * as long as there is no change in the partition boundary
   18865             :                  * structure.
   18866             :                  */
   18867         250 :                 if (contain_mutable_functions(expr))
   18868           6 :                     ereport(ERROR,
   18869             :                             (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
   18870             :                              errmsg("functions in partition key expression must be marked IMMUTABLE")));
   18871             : 
   18872             :                 /*
   18873             :                  * While it is not exactly *wrong* for a partition expression
   18874             :                  * to be a constant, it seems better to reject such keys.
   18875             :                  */
   18876         244 :                 if (IsA(expr, Const))
   18877          12 :                     ereport(ERROR,
   18878             :                             (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
   18879             :                              errmsg("cannot use constant expression as partition key")));
   18880             :             }
   18881             :         }
   18882             : 
   18883             :         /*
   18884             :          * Apply collation override if any
   18885             :          */
   18886        5224 :         if (pelem->collation)
   18887          30 :             attcollation = get_collation_oid(pelem->collation, false);
   18888             : 
   18889             :         /*
   18890             :          * Check we have a collation iff it's a collatable type.  The only
   18891             :          * expected failures here are (1) COLLATE applied to a noncollatable
   18892             :          * type, or (2) partition expression had an unresolved collation. But
   18893             :          * we might as well code this to be a complete consistency check.
   18894             :          */
   18895        5224 :         if (type_is_collatable(atttype))
   18896             :         {
   18897         626 :             if (!OidIsValid(attcollation))
   18898           0 :                 ereport(ERROR,
   18899             :                         (errcode(ERRCODE_INDETERMINATE_COLLATION),
   18900             :                          errmsg("could not determine which collation to use for partition expression"),
   18901             :                          errhint("Use the COLLATE clause to set the collation explicitly.")));
   18902             :         }
   18903             :         else
   18904             :         {
   18905        4598 :             if (OidIsValid(attcollation))
   18906           0 :                 ereport(ERROR,
   18907             :                         (errcode(ERRCODE_DATATYPE_MISMATCH),
   18908             :                          errmsg("collations are not supported by type %s",
   18909             :                                 format_type_be(atttype))));
   18910             :         }
   18911             : 
   18912        5224 :         partcollation[attn] = attcollation;
   18913             : 
   18914             :         /*
   18915             :          * Identify the appropriate operator class.  For list and range
   18916             :          * partitioning, we use a btree operator class; hash partitioning uses
   18917             :          * a hash operator class.
   18918             :          */
   18919        5224 :         if (strategy == PARTITION_STRATEGY_HASH)
   18920         276 :             am_oid = HASH_AM_OID;
   18921             :         else
   18922        4948 :             am_oid = BTREE_AM_OID;
   18923             : 
   18924        5224 :         if (!pelem->opclass)
   18925             :         {
   18926        5092 :             partopclass[attn] = GetDefaultOpClass(atttype, am_oid);
   18927             : 
   18928        5092 :             if (!OidIsValid(partopclass[attn]))
   18929             :             {
   18930          12 :                 if (strategy == PARTITION_STRATEGY_HASH)
   18931           0 :                     ereport(ERROR,
   18932             :                             (errcode(ERRCODE_UNDEFINED_OBJECT),
   18933             :                              errmsg("data type %s has no default operator class for access method \"%s\"",
   18934             :                                     format_type_be(atttype), "hash"),
   18935             :                              errhint("You must specify a hash operator class or define a default hash operator class for the data type.")));
   18936             :                 else
   18937          12 :                     ereport(ERROR,
   18938             :                             (errcode(ERRCODE_UNDEFINED_OBJECT),
   18939             :                              errmsg("data type %s has no default operator class for access method \"%s\"",
   18940             :                                     format_type_be(atttype), "btree"),
   18941             :                              errhint("You must specify a btree operator class or define a default btree operator class for the data type.")));
   18942             :             }
   18943             :         }
   18944             :         else
   18945         132 :             partopclass[attn] = ResolveOpClass(pelem->opclass,
   18946             :                                                atttype,
   18947             :                                                am_oid == HASH_AM_OID ? "hash" : "btree",
   18948             :                                                am_oid);
   18949             : 
   18950        5200 :         attn++;
   18951             :     }
   18952        4744 : }
   18953             : 
   18954             : /*
   18955             :  * PartConstraintImpliedByRelConstraint
   18956             :  *      Do scanrel's existing constraints imply the partition constraint?
   18957             :  *
   18958             :  * "Existing constraints" include its check constraints and column-level
   18959             :  * not-null constraints.  partConstraint describes the partition constraint,
   18960             :  * in implicit-AND form.
   18961             :  */
   18962             : bool
   18963        3046 : PartConstraintImpliedByRelConstraint(Relation scanrel,
   18964             :                                      List *partConstraint)
   18965             : {
   18966        3046 :     List       *existConstraint = NIL;
   18967        3046 :     TupleConstr *constr = RelationGetDescr(scanrel)->constr;
   18968             :     int         i;
   18969             : 
   18970        3046 :     if (constr && constr->has_not_null)
   18971             :     {
   18972         726 :         int         natts = scanrel->rd_att->natts;
   18973             : 
   18974        2334 :         for (i = 1; i <= natts; i++)
   18975             :         {
   18976        1608 :             Form_pg_attribute att = TupleDescAttr(scanrel->rd_att, i - 1);
   18977             : 
   18978        1608 :             if (att->attnotnull && !att->attisdropped)
   18979             :             {
   18980         944 :                 NullTest   *ntest = makeNode(NullTest);
   18981             : 
   18982         944 :                 ntest->arg = (Expr *) makeVar(1,
   18983             :                                               i,
   18984             :                                               att->atttypid,
   18985             :                                               att->atttypmod,
   18986             :                                               att->attcollation,
   18987             :                                               0);
   18988         944 :                 ntest->nulltesttype = IS_NOT_NULL;
   18989             : 
   18990             :                 /*
   18991             :                  * argisrow=false is correct even for a composite column,
   18992             :                  * because attnotnull does not represent a SQL-spec IS NOT
   18993             :                  * NULL test in such a case, just IS DISTINCT FROM NULL.
   18994             :                  */
   18995         944 :                 ntest->argisrow = false;
   18996         944 :                 ntest->location = -1;
   18997         944 :                 existConstraint = lappend(existConstraint, ntest);
   18998             :             }
   18999             :         }
   19000             :     }
   19001             : 
   19002        3046 :     return ConstraintImpliedByRelConstraint(scanrel, partConstraint, existConstraint);
   19003             : }
   19004             : 
   19005             : /*
   19006             :  * ConstraintImpliedByRelConstraint
   19007             :  *      Do scanrel's existing constraints imply the given constraint?
   19008             :  *
   19009             :  * testConstraint is the constraint to validate. provenConstraint is a
   19010             :  * caller-provided list of conditions which this function may assume
   19011             :  * to be true. Both provenConstraint and testConstraint must be in
   19012             :  * implicit-AND form, must only contain immutable clauses, and must
   19013             :  * contain only Vars with varno = 1.
   19014             :  */
   19015             : bool
   19016        4612 : ConstraintImpliedByRelConstraint(Relation scanrel, List *testConstraint, List *provenConstraint)
   19017             : {
   19018        4612 :     List       *existConstraint = list_copy(provenConstraint);
   19019        4612 :     TupleConstr *constr = RelationGetDescr(scanrel)->constr;
   19020             :     int         num_check,
   19021             :                 i;
   19022             : 
   19023        4612 :     num_check = (constr != NULL) ? constr->num_check : 0;
   19024        5198 :     for (i = 0; i < num_check; i++)
   19025             :     {
   19026             :         Node       *cexpr;
   19027             : 
   19028             :         /*
   19029             :          * If this constraint hasn't been fully validated yet, we must ignore
   19030             :          * it here.
   19031             :          */
   19032         586 :         if (!constr->check[i].ccvalid)
   19033           6 :             continue;
   19034             : 
   19035         580 :         cexpr = stringToNode(constr->check[i].ccbin);
   19036             : 
   19037             :         /*
   19038             :          * Run each expression through const-simplification and
   19039             :          * canonicalization.  It is necessary, because we will be comparing it
   19040             :          * to similarly-processed partition constraint expressions, and may
   19041             :          * fail to detect valid matches without this.
   19042             :          */
   19043         580 :         cexpr = eval_const_expressions(NULL, cexpr);
   19044         580 :         cexpr = (Node *) canonicalize_qual((Expr *) cexpr, true);
   19045             : 
   19046         580 :         existConstraint = list_concat(existConstraint,
   19047         580 :                                       make_ands_implicit((Expr *) cexpr));
   19048             :     }
   19049             : 
   19050             :     /*
   19051             :      * Try to make the proof.  Since we are comparing CHECK constraints, we
   19052             :      * need to use weak implication, i.e., we assume existConstraint is
   19053             :      * not-false and try to prove the same for testConstraint.
   19054             :      *
   19055             :      * Note that predicate_implied_by assumes its first argument is known
   19056             :      * immutable.  That should always be true for both NOT NULL and partition
   19057             :      * constraints, so we don't test it here.
   19058             :      */
   19059        4612 :     return predicate_implied_by(testConstraint, existConstraint, true);
   19060             : }
   19061             : 
   19062             : /*
   19063             :  * QueuePartitionConstraintValidation
   19064             :  *
   19065             :  * Add an entry to wqueue to have the given partition constraint validated by
   19066             :  * Phase 3, for the given relation, and all its children.
   19067             :  *
   19068             :  * We first verify whether the given constraint is implied by pre-existing
   19069             :  * relation constraints; if it is, there's no need to scan the table to
   19070             :  * validate, so don't queue in that case.
   19071             :  */
   19072             : static void
   19073        2416 : QueuePartitionConstraintValidation(List **wqueue, Relation scanrel,
   19074             :                                    List *partConstraint,
   19075             :                                    bool validate_default)
   19076             : {
   19077             :     /*
   19078             :      * Based on the table's existing constraints, determine whether or not we
   19079             :      * may skip scanning the table.
   19080             :      */
   19081        2416 :     if (PartConstraintImpliedByRelConstraint(scanrel, partConstraint))
   19082             :     {
   19083          90 :         if (!validate_default)
   19084          68 :             ereport(DEBUG1,
   19085             :                     (errmsg_internal("partition constraint for table \"%s\" is implied by existing constraints",
   19086             :                                      RelationGetRelationName(scanrel))));
   19087             :         else
   19088          22 :             ereport(DEBUG1,
   19089             :                     (errmsg_internal("updated partition constraint for default partition \"%s\" is implied by existing constraints",
   19090             :                                      RelationGetRelationName(scanrel))));
   19091          90 :         return;
   19092             :     }
   19093             : 
   19094             :     /*
   19095             :      * Constraints proved insufficient. For plain relations, queue a
   19096             :      * validation item now; for partitioned tables, recurse to process each
   19097             :      * partition.
   19098             :      */
   19099        2326 :     if (scanrel->rd_rel->relkind == RELKIND_RELATION)
   19100             :     {
   19101             :         AlteredTableInfo *tab;
   19102             : 
   19103             :         /* Grab a work queue entry. */
   19104        1932 :         tab = ATGetQueueEntry(wqueue, scanrel);
   19105             :         Assert(tab->partition_constraint == NULL);
   19106        1932 :         tab->partition_constraint = (Expr *) linitial(partConstraint);
   19107        1932 :         tab->validate_default = validate_default;
   19108             :     }
   19109         394 :     else if (scanrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
   19110             :     {
   19111         346 :         PartitionDesc partdesc = RelationGetPartitionDesc(scanrel, true);
   19112             :         int         i;
   19113             : 
   19114         740 :         for (i = 0; i < partdesc->nparts; i++)
   19115             :         {
   19116             :             Relation    part_rel;
   19117             :             List       *thisPartConstraint;
   19118             : 
   19119             :             /*
   19120             :              * This is the minimum lock we need to prevent deadlocks.
   19121             :              */
   19122         394 :             part_rel = table_open(partdesc->oids[i], AccessExclusiveLock);
   19123             : 
   19124             :             /*
   19125             :              * Adjust the constraint for scanrel so that it matches this
   19126             :              * partition's attribute numbers.
   19127             :              */
   19128             :             thisPartConstraint =
   19129         394 :                 map_partition_varattnos(partConstraint, 1,
   19130             :                                         part_rel, scanrel);
   19131             : 
   19132         394 :             QueuePartitionConstraintValidation(wqueue, part_rel,
   19133             :                                                thisPartConstraint,
   19134             :                                                validate_default);
   19135         394 :             table_close(part_rel, NoLock);  /* keep lock till commit */
   19136             :         }
   19137             :     }
   19138             : }
   19139             : 
   19140             : /*
   19141             :  * attachPartitionTable: attach a new partition to the partitioned table
   19142             :  *
   19143             :  * wqueue: the ALTER TABLE work queue; can be NULL when not running as part
   19144             :  *   of an ALTER TABLE sequence.
   19145             :  * rel: partitioned relation;
   19146             :  * attachrel: relation of attached partition;
   19147             :  * bound: bounds of attached relation.
   19148             :  */
   19149             : static void
   19150        2452 : attachPartitionTable(List **wqueue, Relation rel, Relation attachrel, PartitionBoundSpec *bound)
   19151             : {
   19152             :     /* OK to create inheritance.  Rest of the checks performed there */
   19153        2452 :     CreateInheritance(attachrel, rel, true);
   19154             : 
   19155             :     /* Update the pg_class entry. */
   19156        2380 :     StorePartitionBound(attachrel, rel, bound);
   19157             : 
   19158             :     /* Ensure there exists a correct set of indexes in the partition. */
   19159        2380 :     AttachPartitionEnsureIndexes(wqueue, rel, attachrel);
   19160             : 
   19161             :     /* and triggers */
   19162        2350 :     CloneRowTriggersToPartition(rel, attachrel);
   19163             : 
   19164             :     /*
   19165             :      * Clone foreign key constraints.  Callee is responsible for setting up
   19166             :      * for phase 3 constraint verification.
   19167             :      */
   19168        2344 :     CloneForeignKeyConstraints(wqueue, rel, attachrel);
   19169        2344 : }
   19170             : 
   19171             : /*
   19172             :  * ALTER TABLE <name> ATTACH PARTITION <partition-name> FOR VALUES
   19173             :  *
   19174             :  * Return the address of the newly attached partition.
   19175             :  */
   19176             : static ObjectAddress
   19177        2182 : ATExecAttachPartition(List **wqueue, Relation rel, PartitionCmd *cmd,
   19178             :                       AlterTableUtilityContext *context)
   19179             : {
   19180             :     Relation    attachrel,
   19181             :                 catalog;
   19182             :     List       *attachrel_children;
   19183             :     List       *partConstraint;
   19184             :     SysScanDesc scan;
   19185             :     ScanKeyData skey;
   19186             :     AttrNumber  attno;
   19187             :     int         natts;
   19188             :     TupleDesc   tupleDesc;
   19189             :     ObjectAddress address;
   19190             :     const char *trigger_name;
   19191             :     Oid         defaultPartOid;
   19192             :     List       *partBoundConstraint;
   19193        2182 :     ParseState *pstate = make_parsestate(NULL);
   19194             : 
   19195        2182 :     pstate->p_sourcetext = context->queryString;
   19196             : 
   19197             :     /*
   19198             :      * We must lock the default partition if one exists, because attaching a
   19199             :      * new partition will change its partition constraint.
   19200             :      */
   19201             :     defaultPartOid =
   19202        2182 :         get_default_oid_from_partdesc(RelationGetPartitionDesc(rel, true));
   19203        2182 :     if (OidIsValid(defaultPartOid))
   19204         182 :         LockRelationOid(defaultPartOid, AccessExclusiveLock);
   19205             : 
   19206        2182 :     attachrel = table_openrv(cmd->name, AccessExclusiveLock);
   19207             : 
   19208             :     /*
   19209             :      * XXX I think it'd be a good idea to grab locks on all tables referenced
   19210             :      * by FKs at this point also.
   19211             :      */
   19212             : 
   19213             :     /*
   19214             :      * Must be owner of both parent and source table -- parent was checked by
   19215             :      * ATSimplePermissions call in ATPrepCmd
   19216             :      */
   19217        2176 :     ATSimplePermissions(AT_AttachPartition, attachrel, ATT_TABLE | ATT_FOREIGN_TABLE);
   19218             : 
   19219             :     /* A partition can only have one parent */
   19220        2170 :     if (attachrel->rd_rel->relispartition)
   19221           6 :         ereport(ERROR,
   19222             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   19223             :                  errmsg("\"%s\" is already a partition",
   19224             :                         RelationGetRelationName(attachrel))));
   19225             : 
   19226        2164 :     if (OidIsValid(attachrel->rd_rel->reloftype))
   19227           6 :         ereport(ERROR,
   19228             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   19229             :                  errmsg("cannot attach a typed table as partition")));
   19230             : 
   19231             :     /*
   19232             :      * Table being attached should not already be part of inheritance; either
   19233             :      * as a child table...
   19234             :      */
   19235        2158 :     catalog = table_open(InheritsRelationId, AccessShareLock);
   19236        2158 :     ScanKeyInit(&skey,
   19237             :                 Anum_pg_inherits_inhrelid,
   19238             :                 BTEqualStrategyNumber, F_OIDEQ,
   19239             :                 ObjectIdGetDatum(RelationGetRelid(attachrel)));
   19240        2158 :     scan = systable_beginscan(catalog, InheritsRelidSeqnoIndexId, true,
   19241             :                               NULL, 1, &skey);
   19242        2158 :     if (HeapTupleIsValid(systable_getnext(scan)))
   19243           6 :         ereport(ERROR,
   19244             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   19245             :                  errmsg("cannot attach inheritance child as partition")));
   19246        2152 :     systable_endscan(scan);
   19247             : 
   19248             :     /* ...or as a parent table (except the case when it is partitioned) */
   19249        2152 :     ScanKeyInit(&skey,
   19250             :                 Anum_pg_inherits_inhparent,
   19251             :                 BTEqualStrategyNumber, F_OIDEQ,
   19252             :                 ObjectIdGetDatum(RelationGetRelid(attachrel)));
   19253        2152 :     scan = systable_beginscan(catalog, InheritsParentIndexId, true, NULL,
   19254             :                               1, &skey);
   19255        2152 :     if (HeapTupleIsValid(systable_getnext(scan)) &&
   19256         248 :         attachrel->rd_rel->relkind == RELKIND_RELATION)
   19257           6 :         ereport(ERROR,
   19258             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   19259             :                  errmsg("cannot attach inheritance parent as partition")));
   19260        2146 :     systable_endscan(scan);
   19261        2146 :     table_close(catalog, AccessShareLock);
   19262             : 
   19263             :     /*
   19264             :      * Prevent circularity by seeing if rel is a partition of attachrel. (In
   19265             :      * particular, this disallows making a rel a partition of itself.)
   19266             :      *
   19267             :      * We do that by checking if rel is a member of the list of attachrel's
   19268             :      * partitions provided the latter is partitioned at all.  We want to avoid
   19269             :      * having to construct this list again, so we request the strongest lock
   19270             :      * on all partitions.  We need the strongest lock, because we may decide
   19271             :      * to scan them if we find out that the table being attached (or its leaf
   19272             :      * partitions) may contain rows that violate the partition constraint. If
   19273             :      * the table has a constraint that would prevent such rows, which by
   19274             :      * definition is present in all the partitions, we need not scan the
   19275             :      * table, nor its partitions.  But we cannot risk a deadlock by taking a
   19276             :      * weaker lock now and the stronger one only when needed.
   19277             :      */
   19278        2146 :     attachrel_children = find_all_inheritors(RelationGetRelid(attachrel),
   19279             :                                              AccessExclusiveLock, NULL);
   19280        2146 :     if (list_member_oid(attachrel_children, RelationGetRelid(rel)))
   19281          12 :         ereport(ERROR,
   19282             :                 (errcode(ERRCODE_DUPLICATE_TABLE),
   19283             :                  errmsg("circular inheritance not allowed"),
   19284             :                  errdetail("\"%s\" is already a child of \"%s\".",
   19285             :                            RelationGetRelationName(rel),
   19286             :                            RelationGetRelationName(attachrel))));
   19287             : 
   19288             :     /* If the parent is permanent, so must be all of its partitions. */
   19289        2134 :     if (rel->rd_rel->relpersistence != RELPERSISTENCE_TEMP &&
   19290        2110 :         attachrel->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
   19291           6 :         ereport(ERROR,
   19292             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   19293             :                  errmsg("cannot attach a temporary relation as partition of permanent relation \"%s\"",
   19294             :                         RelationGetRelationName(rel))));
   19295             : 
   19296             :     /* Temp parent cannot have a partition that is itself not a temp */
   19297        2128 :     if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
   19298          24 :         attachrel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
   19299          18 :         ereport(ERROR,
   19300             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   19301             :                  errmsg("cannot attach a permanent relation as partition of temporary relation \"%s\"",
   19302             :                         RelationGetRelationName(rel))));
   19303             : 
   19304             :     /* If the parent is temp, it must belong to this session */
   19305        2110 :     if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
   19306           6 :         !rel->rd_islocaltemp)
   19307           0 :         ereport(ERROR,
   19308             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   19309             :                  errmsg("cannot attach as partition of temporary relation of another session")));
   19310             : 
   19311             :     /* Ditto for the partition */
   19312        2110 :     if (attachrel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
   19313           6 :         !attachrel->rd_islocaltemp)
   19314           0 :         ereport(ERROR,
   19315             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   19316             :                  errmsg("cannot attach temporary relation of another session as partition")));
   19317             : 
   19318             :     /*
   19319             :      * Check if attachrel has any identity columns or any columns that aren't
   19320             :      * in the parent.
   19321             :      */
   19322        2110 :     tupleDesc = RelationGetDescr(attachrel);
   19323        2110 :     natts = tupleDesc->natts;
   19324        7256 :     for (attno = 1; attno <= natts; attno++)
   19325             :     {
   19326        5182 :         Form_pg_attribute attribute = TupleDescAttr(tupleDesc, attno - 1);
   19327        5182 :         char       *attributeName = NameStr(attribute->attname);
   19328             : 
   19329             :         /* Ignore dropped */
   19330        5182 :         if (attribute->attisdropped)
   19331         592 :             continue;
   19332             : 
   19333        4590 :         if (attribute->attidentity)
   19334          18 :             ereport(ERROR,
   19335             :                     errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
   19336             :                     errmsg("table \"%s\" being attached contains an identity column \"%s\"",
   19337             :                            RelationGetRelationName(attachrel), attributeName),
   19338             :                     errdetail("The new partition may not contain an identity column."));
   19339             : 
   19340             :         /* Try to find the column in parent (matching on column name) */
   19341        4572 :         if (!SearchSysCacheExists2(ATTNAME,
   19342             :                                    ObjectIdGetDatum(RelationGetRelid(rel)),
   19343             :                                    CStringGetDatum(attributeName)))
   19344          18 :             ereport(ERROR,
   19345             :                     (errcode(ERRCODE_DATATYPE_MISMATCH),
   19346             :                      errmsg("table \"%s\" contains column \"%s\" not found in parent \"%s\"",
   19347             :                             RelationGetRelationName(attachrel), attributeName,
   19348             :                             RelationGetRelationName(rel)),
   19349             :                      errdetail("The new partition may contain only the columns present in parent.")));
   19350             :     }
   19351             : 
   19352             :     /*
   19353             :      * If child_rel has row-level triggers with transition tables, we
   19354             :      * currently don't allow it to become a partition.  See also prohibitions
   19355             :      * in ATExecAddInherit() and CreateTrigger().
   19356             :      */
   19357        2074 :     trigger_name = FindTriggerIncompatibleWithInheritance(attachrel->trigdesc);
   19358        2074 :     if (trigger_name != NULL)
   19359           6 :         ereport(ERROR,
   19360             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   19361             :                  errmsg("trigger \"%s\" prevents table \"%s\" from becoming a partition",
   19362             :                         trigger_name, RelationGetRelationName(attachrel)),
   19363             :                  errdetail("ROW triggers with transition tables are not supported on partitions.")));
   19364             : 
   19365             :     /*
   19366             :      * Check that the new partition's bound is valid and does not overlap any
   19367             :      * of existing partitions of the parent - note that it does not return on
   19368             :      * error.
   19369             :      */
   19370        2068 :     check_new_partition_bound(RelationGetRelationName(attachrel), rel,
   19371             :                               cmd->bound, pstate);
   19372             : 
   19373             :     /* Attach a new partition to the partitioned table. */
   19374        2032 :     attachPartitionTable(wqueue, rel, attachrel, cmd->bound);
   19375             : 
   19376             :     /*
   19377             :      * Generate partition constraint from the partition bound specification.
   19378             :      * If the parent itself is a partition, make sure to include its
   19379             :      * constraint as well.
   19380             :      */
   19381        1924 :     partBoundConstraint = get_qual_from_partbound(rel, cmd->bound);
   19382        1924 :     partConstraint = list_concat(partBoundConstraint,
   19383        1924 :                                  RelationGetPartitionQual(rel));
   19384             : 
   19385             :     /* Skip validation if there are no constraints to validate. */
   19386        1924 :     if (partConstraint)
   19387             :     {
   19388             :         /*
   19389             :          * Run the partition quals through const-simplification similar to
   19390             :          * check constraints.  We skip canonicalize_qual, though, because
   19391             :          * partition quals should be in canonical form already.
   19392             :          */
   19393             :         partConstraint =
   19394        1876 :             (List *) eval_const_expressions(NULL,
   19395             :                                             (Node *) partConstraint);
   19396             : 
   19397             :         /* XXX this sure looks wrong */
   19398        1876 :         partConstraint = list_make1(make_ands_explicit(partConstraint));
   19399             : 
   19400             :         /*
   19401             :          * Adjust the generated constraint to match this partition's attribute
   19402             :          * numbers.
   19403             :          */
   19404        1876 :         partConstraint = map_partition_varattnos(partConstraint, 1, attachrel,
   19405             :                                                  rel);
   19406             : 
   19407             :         /* Validate partition constraints against the table being attached. */
   19408        1876 :         QueuePartitionConstraintValidation(wqueue, attachrel, partConstraint,
   19409             :                                            false);
   19410             :     }
   19411             : 
   19412             :     /*
   19413             :      * If we're attaching a partition other than the default partition and a
   19414             :      * default one exists, then that partition's partition constraint changes,
   19415             :      * so add an entry to the work queue to validate it, too.  (We must not do
   19416             :      * this when the partition being attached is the default one; we already
   19417             :      * did it above!)
   19418             :      */
   19419        1924 :     if (OidIsValid(defaultPartOid))
   19420             :     {
   19421             :         Relation    defaultrel;
   19422             :         List       *defPartConstraint;
   19423             : 
   19424             :         Assert(!cmd->bound->is_default);
   19425             : 
   19426             :         /* we already hold a lock on the default partition */
   19427         146 :         defaultrel = table_open(defaultPartOid, NoLock);
   19428             :         defPartConstraint =
   19429         146 :             get_proposed_default_constraint(partBoundConstraint);
   19430             : 
   19431             :         /*
   19432             :          * Map the Vars in the constraint expression from rel's attnos to
   19433             :          * defaultrel's.
   19434             :          */
   19435             :         defPartConstraint =
   19436         146 :             map_partition_varattnos(defPartConstraint,
   19437             :                                     1, defaultrel, rel);
   19438         146 :         QueuePartitionConstraintValidation(wqueue, defaultrel,
   19439             :                                            defPartConstraint, true);
   19440             : 
   19441             :         /* keep our lock until commit. */
   19442         146 :         table_close(defaultrel, NoLock);
   19443             :     }
   19444             : 
   19445        1924 :     ObjectAddressSet(address, RelationRelationId, RelationGetRelid(attachrel));
   19446             : 
   19447             :     /*
   19448             :      * If the partition we just attached is partitioned itself, invalidate
   19449             :      * relcache for all descendent partitions too to ensure that their
   19450             :      * rd_partcheck expression trees are rebuilt; partitions already locked at
   19451             :      * the beginning of this function.
   19452             :      */
   19453        1924 :     if (attachrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
   19454             :     {
   19455             :         ListCell   *l;
   19456             : 
   19457         996 :         foreach(l, attachrel_children)
   19458             :         {
   19459         674 :             CacheInvalidateRelcacheByRelid(lfirst_oid(l));
   19460             :         }
   19461             :     }
   19462             : 
   19463             :     /* keep our lock until commit */
   19464        1924 :     table_close(attachrel, NoLock);
   19465             : 
   19466        1924 :     return address;
   19467             : }
   19468             : 
   19469             : /*
   19470             :  * AttachPartitionEnsureIndexes
   19471             :  *      subroutine for ATExecAttachPartition to create/match indexes
   19472             :  *
   19473             :  * Enforce the indexing rule for partitioned tables during ALTER TABLE / ATTACH
   19474             :  * PARTITION: every partition must have an index attached to each index on the
   19475             :  * partitioned table.
   19476             :  */
   19477             : static void
   19478        2380 : AttachPartitionEnsureIndexes(List **wqueue, Relation rel, Relation attachrel)
   19479             : {
   19480             :     List       *idxes;
   19481             :     List       *attachRelIdxs;
   19482             :     Relation   *attachrelIdxRels;
   19483             :     IndexInfo **attachInfos;
   19484             :     ListCell   *cell;
   19485             :     MemoryContext cxt;
   19486             :     MemoryContext oldcxt;
   19487             : 
   19488        2380 :     cxt = AllocSetContextCreate(CurrentMemoryContext,
   19489             :                                 "AttachPartitionEnsureIndexes",
   19490             :                                 ALLOCSET_DEFAULT_SIZES);
   19491        2380 :     oldcxt = MemoryContextSwitchTo(cxt);
   19492             : 
   19493        2380 :     idxes = RelationGetIndexList(rel);
   19494        2380 :     attachRelIdxs = RelationGetIndexList(attachrel);
   19495        2380 :     attachrelIdxRels = palloc(sizeof(Relation) * list_length(attachRelIdxs));
   19496        2380 :     attachInfos = palloc(sizeof(IndexInfo *) * list_length(attachRelIdxs));
   19497             : 
   19498             :     /* Build arrays of all existing indexes and their IndexInfos */
   19499        2714 :     foreach(cell, attachRelIdxs)
   19500             :     {
   19501         334 :         Oid         cldIdxId = lfirst_oid(cell);
   19502         334 :         int         i = foreach_current_index(cell);
   19503             : 
   19504         334 :         attachrelIdxRels[i] = index_open(cldIdxId, AccessShareLock);
   19505         334 :         attachInfos[i] = BuildIndexInfo(attachrelIdxRels[i]);
   19506             :     }
   19507             : 
   19508             :     /*
   19509             :      * If we're attaching a foreign table, we must fail if any of the indexes
   19510             :      * is a constraint index; otherwise, there's nothing to do here.  Do this
   19511             :      * before starting work, to avoid wasting the effort of building a few
   19512             :      * non-unique indexes before coming across a unique one.
   19513             :      */
   19514        2380 :     if (attachrel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
   19515             :     {
   19516          86 :         foreach(cell, idxes)
   19517             :         {
   19518          36 :             Oid         idx = lfirst_oid(cell);
   19519          36 :             Relation    idxRel = index_open(idx, AccessShareLock);
   19520             : 
   19521          36 :             if (idxRel->rd_index->indisunique ||
   19522          24 :                 idxRel->rd_index->indisprimary)
   19523          12 :                 ereport(ERROR,
   19524             :                         (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   19525             :                          errmsg("cannot attach foreign table \"%s\" as partition of partitioned table \"%s\"",
   19526             :                                 RelationGetRelationName(attachrel),
   19527             :                                 RelationGetRelationName(rel)),
   19528             :                          errdetail("Partitioned table \"%s\" contains unique indexes.",
   19529             :                                    RelationGetRelationName(rel))));
   19530          24 :             index_close(idxRel, AccessShareLock);
   19531             :         }
   19532             : 
   19533          50 :         goto out;
   19534             :     }
   19535             : 
   19536             :     /*
   19537             :      * For each index on the partitioned table, find a matching one in the
   19538             :      * partition-to-be; if one is not found, create one.
   19539             :      */
   19540        2926 :     foreach(cell, idxes)
   19541             :     {
   19542         626 :         Oid         idx = lfirst_oid(cell);
   19543         626 :         Relation    idxRel = index_open(idx, AccessShareLock);
   19544             :         IndexInfo  *info;
   19545             :         AttrMap    *attmap;
   19546         626 :         bool        found = false;
   19547             :         Oid         constraintOid;
   19548             : 
   19549             :         /*
   19550             :          * Ignore indexes in the partitioned table other than partitioned
   19551             :          * indexes.
   19552             :          */
   19553         626 :         if (idxRel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
   19554             :         {
   19555           0 :             index_close(idxRel, AccessShareLock);
   19556           0 :             continue;
   19557             :         }
   19558             : 
   19559             :         /* construct an indexinfo to compare existing indexes against */
   19560         626 :         info = BuildIndexInfo(idxRel);
   19561         626 :         attmap = build_attrmap_by_name(RelationGetDescr(attachrel),
   19562             :                                        RelationGetDescr(rel),
   19563             :                                        false);
   19564         626 :         constraintOid = get_relation_idx_constraint_oid(RelationGetRelid(rel), idx);
   19565             : 
   19566             :         /*
   19567             :          * Scan the list of existing indexes in the partition-to-be, and mark
   19568             :          * the first matching, valid, unattached one we find, if any, as
   19569             :          * partition of the parent index.  If we find one, we're done.
   19570             :          */
   19571         692 :         for (int i = 0; i < list_length(attachRelIdxs); i++)
   19572             :         {
   19573         256 :             Oid         cldIdxId = RelationGetRelid(attachrelIdxRels[i]);
   19574         256 :             Oid         cldConstrOid = InvalidOid;
   19575             : 
   19576             :             /* does this index have a parent?  if so, can't use it */
   19577         256 :             if (attachrelIdxRels[i]->rd_rel->relispartition)
   19578          12 :                 continue;
   19579             : 
   19580             :             /* If this index is invalid, can't use it */
   19581         244 :             if (!attachrelIdxRels[i]->rd_index->indisvalid)
   19582           6 :                 continue;
   19583             : 
   19584         238 :             if (CompareIndexInfo(attachInfos[i], info,
   19585         238 :                                  attachrelIdxRels[i]->rd_indcollation,
   19586         238 :                                  idxRel->rd_indcollation,
   19587         238 :                                  attachrelIdxRels[i]->rd_opfamily,
   19588         238 :                                  idxRel->rd_opfamily,
   19589             :                                  attmap))
   19590             :             {
   19591             :                 /*
   19592             :                  * If this index is being created in the parent because of a
   19593             :                  * constraint, then the child needs to have a constraint also,
   19594             :                  * so look for one.  If there is no such constraint, this
   19595             :                  * index is no good, so keep looking.
   19596             :                  */
   19597         202 :                 if (OidIsValid(constraintOid))
   19598             :                 {
   19599             :                     cldConstrOid =
   19600         116 :                         get_relation_idx_constraint_oid(RelationGetRelid(attachrel),
   19601             :                                                         cldIdxId);
   19602             :                     /* no dice */
   19603         116 :                     if (!OidIsValid(cldConstrOid))
   19604           6 :                         continue;
   19605             : 
   19606             :                     /* Ensure they're both the same type of constraint */
   19607         220 :                     if (get_constraint_type(constraintOid) !=
   19608         110 :                         get_constraint_type(cldConstrOid))
   19609           6 :                         continue;
   19610             :                 }
   19611             : 
   19612             :                 /* bingo. */
   19613         190 :                 IndexSetParentIndex(attachrelIdxRels[i], idx);
   19614         190 :                 if (OidIsValid(constraintOid))
   19615         104 :                     ConstraintSetParentConstraint(cldConstrOid, constraintOid,
   19616             :                                                   RelationGetRelid(attachrel));
   19617         190 :                 found = true;
   19618             : 
   19619         190 :                 CommandCounterIncrement();
   19620         190 :                 break;
   19621             :             }
   19622             :         }
   19623             : 
   19624             :         /*
   19625             :          * If no suitable index was found in the partition-to-be, create one
   19626             :          * now.
   19627             :          */
   19628         626 :         if (!found)
   19629             :         {
   19630             :             IndexStmt  *stmt;
   19631             :             Oid         conOid;
   19632             : 
   19633         436 :             stmt = generateClonedIndexStmt(NULL,
   19634             :                                            idxRel, attmap,
   19635             :                                            &conOid);
   19636             : 
   19637             :             /*
   19638             :              * If the index is a primary key, mark all columns as NOT NULL if
   19639             :              * they aren't already.
   19640             :              */
   19641         436 :             if (stmt->primary)
   19642             :             {
   19643         220 :                 MemoryContextSwitchTo(oldcxt);
   19644         452 :                 for (int j = 0; j < info->ii_NumIndexKeyAttrs; j++)
   19645             :                 {
   19646             :                     AttrNumber  childattno;
   19647             : 
   19648         232 :                     childattno = get_attnum(RelationGetRelid(attachrel),
   19649         232 :                                             get_attname(RelationGetRelid(rel),
   19650         232 :                                                         info->ii_IndexAttrNumbers[j],
   19651             :                                                         false));
   19652         232 :                     set_attnotnull(wqueue, attachrel, childattno,
   19653             :                                    true, AccessExclusiveLock);
   19654             :                 }
   19655         220 :                 MemoryContextSwitchTo(cxt);
   19656             :             }
   19657             : 
   19658         436 :             DefineIndex(RelationGetRelid(attachrel), stmt, InvalidOid,
   19659             :                         RelationGetRelid(idxRel),
   19660             :                         conOid,
   19661             :                         -1,
   19662             :                         true, false, false, false, false);
   19663             :         }
   19664             : 
   19665         608 :         index_close(idxRel, AccessShareLock);
   19666             :     }
   19667             : 
   19668        2350 : out:
   19669             :     /* Clean up. */
   19670        2672 :     for (int i = 0; i < list_length(attachRelIdxs); i++)
   19671         322 :         index_close(attachrelIdxRels[i], AccessShareLock);
   19672        2350 :     MemoryContextSwitchTo(oldcxt);
   19673        2350 :     MemoryContextDelete(cxt);
   19674        2350 : }
   19675             : 
   19676             : /*
   19677             :  * CloneRowTriggersToPartition
   19678             :  *      subroutine for ATExecAttachPartition/DefineRelation to create row
   19679             :  *      triggers on partitions
   19680             :  */
   19681             : static void
   19682        2776 : CloneRowTriggersToPartition(Relation parent, Relation partition)
   19683             : {
   19684             :     Relation    pg_trigger;
   19685             :     ScanKeyData key;
   19686             :     SysScanDesc scan;
   19687             :     HeapTuple   tuple;
   19688             :     MemoryContext perTupCxt;
   19689             : 
   19690        2776 :     ScanKeyInit(&key, Anum_pg_trigger_tgrelid, BTEqualStrategyNumber,
   19691             :                 F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(parent)));
   19692        2776 :     pg_trigger = table_open(TriggerRelationId, RowExclusiveLock);
   19693        2776 :     scan = systable_beginscan(pg_trigger, TriggerRelidNameIndexId,
   19694             :                               true, NULL, 1, &key);
   19695             : 
   19696        2776 :     perTupCxt = AllocSetContextCreate(CurrentMemoryContext,
   19697             :                                       "clone trig", ALLOCSET_SMALL_SIZES);
   19698             : 
   19699        4356 :     while (HeapTupleIsValid(tuple = systable_getnext(scan)))
   19700             :     {
   19701        1586 :         Form_pg_trigger trigForm = (Form_pg_trigger) GETSTRUCT(tuple);
   19702             :         CreateTrigStmt *trigStmt;
   19703        1586 :         Node       *qual = NULL;
   19704             :         Datum       value;
   19705             :         bool        isnull;
   19706        1586 :         List       *cols = NIL;
   19707        1586 :         List       *trigargs = NIL;
   19708             :         MemoryContext oldcxt;
   19709             : 
   19710             :         /*
   19711             :          * Ignore statement-level triggers; those are not cloned.
   19712             :          */
   19713        1586 :         if (!TRIGGER_FOR_ROW(trigForm->tgtype))
   19714        1406 :             continue;
   19715             : 
   19716             :         /*
   19717             :          * Don't clone internal triggers, because the constraint cloning code
   19718             :          * will.
   19719             :          */
   19720        1562 :         if (trigForm->tgisinternal)
   19721        1382 :             continue;
   19722             : 
   19723             :         /*
   19724             :          * Complain if we find an unexpected trigger type.
   19725             :          */
   19726         180 :         if (!TRIGGER_FOR_BEFORE(trigForm->tgtype) &&
   19727         162 :             !TRIGGER_FOR_AFTER(trigForm->tgtype))
   19728           0 :             elog(ERROR, "unexpected trigger \"%s\" found",
   19729             :                  NameStr(trigForm->tgname));
   19730             : 
   19731             :         /* Use short-lived context for CREATE TRIGGER */
   19732         180 :         oldcxt = MemoryContextSwitchTo(perTupCxt);
   19733             : 
   19734             :         /*
   19735             :          * If there is a WHEN clause, generate a 'cooked' version of it that's
   19736             :          * appropriate for the partition.
   19737             :          */
   19738         180 :         value = heap_getattr(tuple, Anum_pg_trigger_tgqual,
   19739             :                              RelationGetDescr(pg_trigger), &isnull);
   19740         180 :         if (!isnull)
   19741             :         {
   19742           6 :             qual = stringToNode(TextDatumGetCString(value));
   19743           6 :             qual = (Node *) map_partition_varattnos((List *) qual, PRS2_OLD_VARNO,
   19744             :                                                     partition, parent);
   19745           6 :             qual = (Node *) map_partition_varattnos((List *) qual, PRS2_NEW_VARNO,
   19746             :                                                     partition, parent);
   19747             :         }
   19748             : 
   19749             :         /*
   19750             :          * If there is a column list, transform it to a list of column names.
   19751             :          * Note we don't need to map this list in any way ...
   19752             :          */
   19753         180 :         if (trigForm->tgattr.dim1 > 0)
   19754             :         {
   19755             :             int         i;
   19756             : 
   19757          12 :             for (i = 0; i < trigForm->tgattr.dim1; i++)
   19758             :             {
   19759             :                 Form_pg_attribute col;
   19760             : 
   19761           6 :                 col = TupleDescAttr(parent->rd_att,
   19762             :                                     trigForm->tgattr.values[i] - 1);
   19763           6 :                 cols = lappend(cols,
   19764           6 :                                makeString(pstrdup(NameStr(col->attname))));
   19765             :             }
   19766             :         }
   19767             : 
   19768             :         /* Reconstruct trigger arguments list. */
   19769         180 :         if (trigForm->tgnargs > 0)
   19770             :         {
   19771             :             char       *p;
   19772             : 
   19773          36 :             value = heap_getattr(tuple, Anum_pg_trigger_tgargs,
   19774             :                                  RelationGetDescr(pg_trigger), &isnull);
   19775          36 :             if (isnull)
   19776           0 :                 elog(ERROR, "tgargs is null for trigger \"%s\" in partition \"%s\"",
   19777             :                      NameStr(trigForm->tgname), RelationGetRelationName(partition));
   19778             : 
   19779          36 :             p = (char *) VARDATA_ANY(DatumGetByteaPP(value));
   19780             : 
   19781          84 :             for (int i = 0; i < trigForm->tgnargs; i++)
   19782             :             {
   19783          48 :                 trigargs = lappend(trigargs, makeString(pstrdup(p)));
   19784          48 :                 p += strlen(p) + 1;
   19785             :             }
   19786             :         }
   19787             : 
   19788         180 :         trigStmt = makeNode(CreateTrigStmt);
   19789         180 :         trigStmt->replace = false;
   19790         180 :         trigStmt->isconstraint = OidIsValid(trigForm->tgconstraint);
   19791         180 :         trigStmt->trigname = NameStr(trigForm->tgname);
   19792         180 :         trigStmt->relation = NULL;
   19793         180 :         trigStmt->funcname = NULL;   /* passed separately */
   19794         180 :         trigStmt->args = trigargs;
   19795         180 :         trigStmt->row = true;
   19796         180 :         trigStmt->timing = trigForm->tgtype & TRIGGER_TYPE_TIMING_MASK;
   19797         180 :         trigStmt->events = trigForm->tgtype & TRIGGER_TYPE_EVENT_MASK;
   19798         180 :         trigStmt->columns = cols;
   19799         180 :         trigStmt->whenClause = NULL; /* passed separately */
   19800         180 :         trigStmt->transitionRels = NIL; /* not supported at present */
   19801         180 :         trigStmt->deferrable = trigForm->tgdeferrable;
   19802         180 :         trigStmt->initdeferred = trigForm->tginitdeferred;
   19803         180 :         trigStmt->constrrel = NULL; /* passed separately */
   19804             : 
   19805         180 :         CreateTriggerFiringOn(trigStmt, NULL, RelationGetRelid(partition),
   19806             :                               trigForm->tgconstrrelid, InvalidOid, InvalidOid,
   19807             :                               trigForm->tgfoid, trigForm->oid, qual,
   19808         180 :                               false, true, trigForm->tgenabled);
   19809             : 
   19810         174 :         MemoryContextSwitchTo(oldcxt);
   19811         174 :         MemoryContextReset(perTupCxt);
   19812             :     }
   19813             : 
   19814        2770 :     MemoryContextDelete(perTupCxt);
   19815             : 
   19816        2770 :     systable_endscan(scan);
   19817        2770 :     table_close(pg_trigger, RowExclusiveLock);
   19818        2770 : }
   19819             : 
   19820             : /*
   19821             :  * ALTER TABLE DETACH PARTITION
   19822             :  *
   19823             :  * Return the address of the relation that is no longer a partition of rel.
   19824             :  *
   19825             :  * If concurrent mode is requested, we run in two transactions.  A side-
   19826             :  * effect is that this command cannot run in a multi-part ALTER TABLE.
   19827             :  * Currently, that's enforced by the grammar.
   19828             :  *
   19829             :  * The strategy for concurrency is to first modify the partition's
   19830             :  * pg_inherit catalog row to make it visible to everyone that the
   19831             :  * partition is detached, lock the partition against writes, and commit
   19832             :  * the transaction; anyone who requests the partition descriptor from
   19833             :  * that point onwards has to ignore such a partition.  In a second
   19834             :  * transaction, we wait until all transactions that could have seen the
   19835             :  * partition as attached are gone, then we remove the rest of partition
   19836             :  * metadata (pg_inherits and pg_class.relpartbounds).
   19837             :  */
   19838             : static ObjectAddress
   19839         516 : ATExecDetachPartition(List **wqueue, AlteredTableInfo *tab, Relation rel,
   19840             :                       RangeVar *name, bool concurrent)
   19841             : {
   19842             :     Relation    partRel;
   19843             :     ObjectAddress address;
   19844             :     Oid         defaultPartOid;
   19845             : 
   19846             :     /*
   19847             :      * We must lock the default partition, because detaching this partition
   19848             :      * will change its partition constraint.
   19849             :      */
   19850             :     defaultPartOid =
   19851         516 :         get_default_oid_from_partdesc(RelationGetPartitionDesc(rel, true));
   19852         516 :     if (OidIsValid(defaultPartOid))
   19853             :     {
   19854             :         /*
   19855             :          * Concurrent detaching when a default partition exists is not
   19856             :          * supported. The main problem is that the default partition
   19857             :          * constraint would change.  And there's a definitional problem: what
   19858             :          * should happen to the tuples that are being inserted that belong to
   19859             :          * the partition being detached?  Putting them on the partition being
   19860             :          * detached would be wrong, since they'd become "lost" after the
   19861             :          * detaching completes but we cannot put them in the default partition
   19862             :          * either until we alter its partition constraint.
   19863             :          *
   19864             :          * I think we could solve this problem if we effected the constraint
   19865             :          * change before committing the first transaction.  But the lock would
   19866             :          * have to remain AEL and it would cause concurrent query planning to
   19867             :          * be blocked, so changing it that way would be even worse.
   19868             :          */
   19869         112 :         if (concurrent)
   19870          12 :             ereport(ERROR,
   19871             :                     (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
   19872             :                      errmsg("cannot detach partitions concurrently when a default partition exists")));
   19873         100 :         LockRelationOid(defaultPartOid, AccessExclusiveLock);
   19874             :     }
   19875             : 
   19876             :     /*
   19877             :      * In concurrent mode, the partition is locked with share-update-exclusive
   19878             :      * in the first transaction.  This allows concurrent transactions to be
   19879             :      * doing DML to the partition.
   19880             :      */
   19881         504 :     partRel = table_openrv(name, concurrent ? ShareUpdateExclusiveLock :
   19882             :                            AccessExclusiveLock);
   19883             : 
   19884             :     /*
   19885             :      * Check inheritance conditions and either delete the pg_inherits row (in
   19886             :      * non-concurrent mode) or just set the inhdetachpending flag.
   19887             :      */
   19888         492 :     if (!concurrent)
   19889         346 :         RemoveInheritance(partRel, rel, false);
   19890             :     else
   19891         146 :         MarkInheritDetached(partRel, rel);
   19892             : 
   19893             :     /*
   19894             :      * Ensure that foreign keys still hold after this detach.  This keeps
   19895             :      * locks on the referencing tables, which prevents concurrent transactions
   19896             :      * from adding rows that we wouldn't see.  For this to work in concurrent
   19897             :      * mode, it is critical that the partition appears as no longer attached
   19898             :      * for the RI queries as soon as the first transaction commits.
   19899             :      */
   19900         472 :     ATDetachCheckNoForeignKeyRefs(partRel);
   19901             : 
   19902             :     /*
   19903             :      * Concurrent mode has to work harder; first we add a new constraint to
   19904             :      * the partition that matches the partition constraint.  Then we close our
   19905             :      * existing transaction, and in a new one wait for all processes to catch
   19906             :      * up on the catalog updates we've done so far; at that point we can
   19907             :      * complete the operation.
   19908             :      */
   19909         438 :     if (concurrent)
   19910             :     {
   19911             :         Oid         partrelid,
   19912             :                     parentrelid;
   19913             :         LOCKTAG     tag;
   19914             :         char       *parentrelname;
   19915             :         char       *partrelname;
   19916             : 
   19917             :         /*
   19918             :          * Add a new constraint to the partition being detached, which
   19919             :          * supplants the partition constraint (unless there is one already).
   19920             :          */
   19921         140 :         DetachAddConstraintIfNeeded(wqueue, partRel);
   19922             : 
   19923             :         /*
   19924             :          * We're almost done now; the only traces that remain are the
   19925             :          * pg_inherits tuple and the partition's relpartbounds.  Before we can
   19926             :          * remove those, we need to wait until all transactions that know that
   19927             :          * this is a partition are gone.
   19928             :          */
   19929             : 
   19930             :         /*
   19931             :          * Remember relation OIDs to re-acquire them later; and relation names
   19932             :          * too, for error messages if something is dropped in between.
   19933             :          */
   19934         140 :         partrelid = RelationGetRelid(partRel);
   19935         140 :         parentrelid = RelationGetRelid(rel);
   19936         140 :         parentrelname = MemoryContextStrdup(PortalContext,
   19937         140 :                                             RelationGetRelationName(rel));
   19938         140 :         partrelname = MemoryContextStrdup(PortalContext,
   19939         140 :                                           RelationGetRelationName(partRel));
   19940             : 
   19941             :         /* Invalidate relcache entries for the parent -- must be before close */
   19942         140 :         CacheInvalidateRelcache(rel);
   19943             : 
   19944         140 :         table_close(partRel, NoLock);
   19945         140 :         table_close(rel, NoLock);
   19946         140 :         tab->rel = NULL;
   19947             : 
   19948             :         /* Make updated catalog entry visible */
   19949         140 :         PopActiveSnapshot();
   19950         140 :         CommitTransactionCommand();
   19951             : 
   19952         140 :         StartTransactionCommand();
   19953             : 
   19954             :         /*
   19955             :          * Now wait.  This ensures that all queries that were planned
   19956             :          * including the partition are finished before we remove the rest of
   19957             :          * catalog entries.  We don't need or indeed want to acquire this
   19958             :          * lock, though -- that would block later queries.
   19959             :          *
   19960             :          * We don't need to concern ourselves with waiting for a lock on the
   19961             :          * partition itself, since we will acquire AccessExclusiveLock below.
   19962             :          */
   19963         140 :         SET_LOCKTAG_RELATION(tag, MyDatabaseId, parentrelid);
   19964         140 :         WaitForLockersMultiple(list_make1(&tag), AccessExclusiveLock, false);
   19965             : 
   19966             :         /*
   19967             :          * Now acquire locks in both relations again.  Note they may have been
   19968             :          * removed in the meantime, so care is required.
   19969             :          */
   19970          90 :         rel = try_relation_open(parentrelid, ShareUpdateExclusiveLock);
   19971          90 :         partRel = try_relation_open(partrelid, AccessExclusiveLock);
   19972             : 
   19973             :         /* If the relations aren't there, something bad happened; bail out */
   19974          90 :         if (rel == NULL)
   19975             :         {
   19976           0 :             if (partRel != NULL)    /* shouldn't happen */
   19977           0 :                 elog(WARNING, "dangling partition \"%s\" remains, can't fix",
   19978             :                      partrelname);
   19979           0 :             ereport(ERROR,
   19980             :                     (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
   19981             :                      errmsg("partitioned table \"%s\" was removed concurrently",
   19982             :                             parentrelname)));
   19983             :         }
   19984          90 :         if (partRel == NULL)
   19985           0 :             ereport(ERROR,
   19986             :                     (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
   19987             :                      errmsg("partition \"%s\" was removed concurrently", partrelname)));
   19988             : 
   19989          90 :         tab->rel = rel;
   19990             :     }
   19991             : 
   19992             :     /* Do the final part of detaching */
   19993         388 :     DetachPartitionFinalize(rel, partRel, concurrent, defaultPartOid);
   19994             : 
   19995         386 :     ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partRel));
   19996             : 
   19997             :     /* keep our lock until commit */
   19998         386 :     table_close(partRel, NoLock);
   19999             : 
   20000         386 :     return address;
   20001             : }
   20002             : 
   20003             : /*
   20004             :  * Second part of ALTER TABLE .. DETACH.
   20005             :  *
   20006             :  * This is separate so that it can be run independently when the second
   20007             :  * transaction of the concurrent algorithm fails (crash or abort).
   20008             :  */
   20009             : static void
   20010         690 : DetachPartitionFinalize(Relation rel, Relation partRel, bool concurrent,
   20011             :                         Oid defaultPartOid)
   20012             : {
   20013             :     Relation    classRel;
   20014             :     List       *fks;
   20015             :     ListCell   *cell;
   20016             :     List       *indexes;
   20017             :     Datum       new_val[Natts_pg_class];
   20018             :     bool        new_null[Natts_pg_class],
   20019             :                 new_repl[Natts_pg_class];
   20020             :     HeapTuple   tuple,
   20021             :                 newtuple;
   20022         690 :     Relation    trigrel = NULL;
   20023             : 
   20024         690 :     if (concurrent)
   20025             :     {
   20026             :         /*
   20027             :          * We can remove the pg_inherits row now. (In the non-concurrent case,
   20028             :          * this was already done).
   20029             :          */
   20030         104 :         RemoveInheritance(partRel, rel, true);
   20031             :     }
   20032             : 
   20033             :     /* Drop any triggers that were cloned on creation/attach. */
   20034         690 :     DropClonedTriggersFromPartition(RelationGetRelid(partRel));
   20035             : 
   20036             :     /*
   20037             :      * Detach any foreign keys that are inherited.  This includes creating
   20038             :      * additional action triggers.
   20039             :      */
   20040         690 :     fks = copyObject(RelationGetFKeyList(partRel));
   20041         690 :     if (fks != NIL)
   20042          54 :         trigrel = table_open(TriggerRelationId, RowExclusiveLock);
   20043         774 :     foreach(cell, fks)
   20044             :     {
   20045          84 :         ForeignKeyCacheInfo *fk = lfirst(cell);
   20046             :         HeapTuple   contup;
   20047             :         Form_pg_constraint conform;
   20048             :         Constraint *fkconstraint;
   20049             :         Oid         insertTriggerOid,
   20050             :                     updateTriggerOid;
   20051             : 
   20052          84 :         contup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(fk->conoid));
   20053          84 :         if (!HeapTupleIsValid(contup))
   20054           0 :             elog(ERROR, "cache lookup failed for constraint %u", fk->conoid);
   20055          84 :         conform = (Form_pg_constraint) GETSTRUCT(contup);
   20056             : 
   20057             :         /* consider only the inherited foreign keys */
   20058          84 :         if (conform->contype != CONSTRAINT_FOREIGN ||
   20059          84 :             !OidIsValid(conform->conparentid))
   20060             :         {
   20061          18 :             ReleaseSysCache(contup);
   20062          18 :             continue;
   20063             :         }
   20064             : 
   20065             :         /* unset conparentid and adjust conislocal, coninhcount, etc. */
   20066          66 :         ConstraintSetParentConstraint(fk->conoid, InvalidOid, InvalidOid);
   20067             : 
   20068             :         /*
   20069             :          * Also, look up the partition's "check" triggers corresponding to the
   20070             :          * constraint being detached and detach them from the parent triggers.
   20071             :          */
   20072          66 :         GetForeignKeyCheckTriggers(trigrel,
   20073             :                                    fk->conoid, fk->confrelid, fk->conrelid,
   20074             :                                    &insertTriggerOid, &updateTriggerOid);
   20075             :         Assert(OidIsValid(insertTriggerOid));
   20076          66 :         TriggerSetParentTrigger(trigrel, insertTriggerOid, InvalidOid,
   20077             :                                 RelationGetRelid(partRel));
   20078             :         Assert(OidIsValid(updateTriggerOid));
   20079          66 :         TriggerSetParentTrigger(trigrel, updateTriggerOid, InvalidOid,
   20080             :                                 RelationGetRelid(partRel));
   20081             : 
   20082             :         /*
   20083             :          * Make the action triggers on the referenced relation.  When this was
   20084             :          * a partition the action triggers pointed to the parent rel (they
   20085             :          * still do), but now we need separate ones of our own.
   20086             :          */
   20087          66 :         fkconstraint = makeNode(Constraint);
   20088          66 :         fkconstraint->contype = CONSTRAINT_FOREIGN;
   20089          66 :         fkconstraint->conname = pstrdup(NameStr(conform->conname));
   20090          66 :         fkconstraint->deferrable = conform->condeferrable;
   20091          66 :         fkconstraint->initdeferred = conform->condeferred;
   20092          66 :         fkconstraint->location = -1;
   20093          66 :         fkconstraint->pktable = NULL;
   20094          66 :         fkconstraint->fk_attrs = NIL;
   20095          66 :         fkconstraint->pk_attrs = NIL;
   20096          66 :         fkconstraint->fk_matchtype = conform->confmatchtype;
   20097          66 :         fkconstraint->fk_upd_action = conform->confupdtype;
   20098          66 :         fkconstraint->fk_del_action = conform->confdeltype;
   20099          66 :         fkconstraint->fk_del_set_cols = NIL;
   20100          66 :         fkconstraint->old_conpfeqop = NIL;
   20101          66 :         fkconstraint->old_pktable_oid = InvalidOid;
   20102          66 :         fkconstraint->skip_validation = false;
   20103          66 :         fkconstraint->initially_valid = true;
   20104             : 
   20105          66 :         createForeignKeyActionTriggers(partRel, conform->confrelid,
   20106             :                                        fkconstraint, fk->conoid,
   20107             :                                        conform->conindid,
   20108             :                                        InvalidOid, InvalidOid,
   20109             :                                        NULL, NULL);
   20110             : 
   20111          66 :         ReleaseSysCache(contup);
   20112             :     }
   20113         690 :     list_free_deep(fks);
   20114         690 :     if (trigrel)
   20115          54 :         table_close(trigrel, RowExclusiveLock);
   20116             : 
   20117             :     /*
   20118             :      * Any sub-constraints that are in the referenced-side of a larger
   20119             :      * constraint have to be removed.  This partition is no longer part of the
   20120             :      * key space of the constraint.
   20121             :      */
   20122         732 :     foreach(cell, GetParentedForeignKeyRefs(partRel))
   20123             :     {
   20124          44 :         Oid         constrOid = lfirst_oid(cell);
   20125             :         ObjectAddress constraint;
   20126             : 
   20127          44 :         ConstraintSetParentConstraint(constrOid, InvalidOid, InvalidOid);
   20128          44 :         deleteDependencyRecordsForClass(ConstraintRelationId,
   20129             :                                         constrOid,
   20130             :                                         ConstraintRelationId,
   20131             :                                         DEPENDENCY_INTERNAL);
   20132          44 :         CommandCounterIncrement();
   20133             : 
   20134          44 :         ObjectAddressSet(constraint, ConstraintRelationId, constrOid);
   20135          44 :         performDeletion(&constraint, DROP_RESTRICT, 0);
   20136             :     }
   20137             : 
   20138             :     /* Now we can detach indexes */
   20139         688 :     indexes = RelationGetIndexList(partRel);
   20140        1002 :     foreach(cell, indexes)
   20141             :     {
   20142         314 :         Oid         idxid = lfirst_oid(cell);
   20143             :         Relation    idx;
   20144             :         Oid         constrOid;
   20145             : 
   20146         314 :         if (!has_superclass(idxid))
   20147          12 :             continue;
   20148             : 
   20149             :         Assert((IndexGetRelation(get_partition_parent(idxid, false), false) ==
   20150             :                 RelationGetRelid(rel)));
   20151             : 
   20152         302 :         idx = index_open(idxid, AccessExclusiveLock);
   20153         302 :         IndexSetParentIndex(idx, InvalidOid);
   20154             : 
   20155             :         /* If there's a constraint associated with the index, detach it too */
   20156         302 :         constrOid = get_relation_idx_constraint_oid(RelationGetRelid(partRel),
   20157             :                                                     idxid);
   20158         302 :         if (OidIsValid(constrOid))
   20159         120 :             ConstraintSetParentConstraint(constrOid, InvalidOid, InvalidOid);
   20160             : 
   20161         302 :         index_close(idx, NoLock);
   20162             :     }
   20163             : 
   20164             :     /* Update pg_class tuple */
   20165         688 :     classRel = table_open(RelationRelationId, RowExclusiveLock);
   20166         688 :     tuple = SearchSysCacheCopy1(RELOID,
   20167             :                                 ObjectIdGetDatum(RelationGetRelid(partRel)));
   20168         688 :     if (!HeapTupleIsValid(tuple))
   20169           0 :         elog(ERROR, "cache lookup failed for relation %u",
   20170             :              RelationGetRelid(partRel));
   20171             :     Assert(((Form_pg_class) GETSTRUCT(tuple))->relispartition);
   20172             : 
   20173             :     /* Clear relpartbound and reset relispartition */
   20174         688 :     memset(new_val, 0, sizeof(new_val));
   20175         688 :     memset(new_null, false, sizeof(new_null));
   20176         688 :     memset(new_repl, false, sizeof(new_repl));
   20177         688 :     new_val[Anum_pg_class_relpartbound - 1] = (Datum) 0;
   20178         688 :     new_null[Anum_pg_class_relpartbound - 1] = true;
   20179         688 :     new_repl[Anum_pg_class_relpartbound - 1] = true;
   20180         688 :     newtuple = heap_modify_tuple(tuple, RelationGetDescr(classRel),
   20181             :                                  new_val, new_null, new_repl);
   20182             : 
   20183         688 :     ((Form_pg_class) GETSTRUCT(newtuple))->relispartition = false;
   20184         688 :     CatalogTupleUpdate(classRel, &newtuple->t_self, newtuple);
   20185         688 :     heap_freetuple(newtuple);
   20186         688 :     table_close(classRel, RowExclusiveLock);
   20187             : 
   20188             :     /*
   20189             :      * Drop identity property from all identity columns of partition.
   20190             :      */
   20191        2452 :     for (int attno = 0; attno < RelationGetNumberOfAttributes(partRel); attno++)
   20192             :     {
   20193        1764 :         Form_pg_attribute attr = TupleDescAttr(partRel->rd_att, attno);
   20194             : 
   20195        1764 :         if (!attr->attisdropped && attr->attidentity)
   20196          30 :             ATExecDropIdentity(partRel, NameStr(attr->attname), false,
   20197             :                                AccessExclusiveLock, true, true);
   20198             :     }
   20199             : 
   20200         688 :     if (OidIsValid(defaultPartOid))
   20201             :     {
   20202             :         /*
   20203             :          * If the relation being detached is the default partition itself,
   20204             :          * remove it from the parent's pg_partitioned_table entry.
   20205             :          *
   20206             :          * If not, we must invalidate default partition's relcache entry, as
   20207             :          * in StorePartitionBound: its partition constraint depends on every
   20208             :          * other partition's partition constraint.
   20209             :          */
   20210         232 :         if (RelationGetRelid(partRel) == defaultPartOid)
   20211          38 :             update_default_partition_oid(RelationGetRelid(rel), InvalidOid);
   20212             :         else
   20213         194 :             CacheInvalidateRelcacheByRelid(defaultPartOid);
   20214             :     }
   20215             : 
   20216             :     /*
   20217             :      * Invalidate the parent's relcache so that the partition is no longer
   20218             :      * included in its partition descriptor.
   20219             :      */
   20220         688 :     CacheInvalidateRelcache(rel);
   20221             : 
   20222             :     /*
   20223             :      * If the partition we just detached is partitioned itself, invalidate
   20224             :      * relcache for all descendent partitions too to ensure that their
   20225             :      * rd_partcheck expression trees are rebuilt; must lock partitions before
   20226             :      * doing so, using the same lockmode as what partRel has been locked with
   20227             :      * by the caller.
   20228             :      */
   20229         688 :     if (partRel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
   20230             :     {
   20231             :         List       *children;
   20232             : 
   20233          50 :         children = find_all_inheritors(RelationGetRelid(partRel),
   20234             :                                        AccessExclusiveLock, NULL);
   20235         162 :         foreach(cell, children)
   20236             :         {
   20237         112 :             CacheInvalidateRelcacheByRelid(lfirst_oid(cell));
   20238             :         }
   20239             :     }
   20240         688 : }
   20241             : 
   20242             : /*
   20243             :  * ALTER TABLE ... DETACH PARTITION ... FINALIZE
   20244             :  *
   20245             :  * To use when a DETACH PARTITION command previously did not run to
   20246             :  * completion; this completes the detaching process.
   20247             :  */
   20248             : static ObjectAddress
   20249          14 : ATExecDetachPartitionFinalize(Relation rel, RangeVar *name)
   20250             : {
   20251             :     Relation    partRel;
   20252             :     ObjectAddress address;
   20253          14 :     Snapshot    snap = GetActiveSnapshot();
   20254             : 
   20255          14 :     partRel = table_openrv(name, AccessExclusiveLock);
   20256             : 
   20257             :     /*
   20258             :      * Wait until existing snapshots are gone.  This is important if the
   20259             :      * second transaction of DETACH PARTITION CONCURRENTLY is canceled: the
   20260             :      * user could immediately run DETACH FINALIZE without actually waiting for
   20261             :      * existing transactions.  We must not complete the detach action until
   20262             :      * all such queries are complete (otherwise we would present them with an
   20263             :      * inconsistent view of catalogs).
   20264             :      */
   20265          14 :     WaitForOlderSnapshots(snap->xmin, false);
   20266             : 
   20267          14 :     DetachPartitionFinalize(rel, partRel, true, InvalidOid);
   20268             : 
   20269          14 :     ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partRel));
   20270             : 
   20271          14 :     table_close(partRel, NoLock);
   20272             : 
   20273          14 :     return address;
   20274             : }
   20275             : 
   20276             : /*
   20277             :  * DetachAddConstraintIfNeeded
   20278             :  *      Subroutine for ATExecDetachPartition.  Create a constraint that
   20279             :  *      takes the place of the partition constraint, but avoid creating
   20280             :  *      a dupe if a constraint already exists which implies the needed
   20281             :  *      constraint.
   20282             :  */
   20283             : static void
   20284         140 : DetachAddConstraintIfNeeded(List **wqueue, Relation partRel)
   20285             : {
   20286             :     List       *constraintExpr;
   20287             : 
   20288         140 :     constraintExpr = RelationGetPartitionQual(partRel);
   20289         140 :     constraintExpr = (List *) eval_const_expressions(NULL, (Node *) constraintExpr);
   20290             : 
   20291             :     /*
   20292             :      * Avoid adding a new constraint if the needed constraint is implied by an
   20293             :      * existing constraint
   20294             :      */
   20295         140 :     if (!PartConstraintImpliedByRelConstraint(partRel, constraintExpr))
   20296             :     {
   20297             :         AlteredTableInfo *tab;
   20298             :         Constraint *n;
   20299             : 
   20300         134 :         tab = ATGetQueueEntry(wqueue, partRel);
   20301             : 
   20302             :         /* Add constraint on partition, equivalent to the partition constraint */
   20303         134 :         n = makeNode(Constraint);
   20304         134 :         n->contype = CONSTR_CHECK;
   20305         134 :         n->conname = NULL;
   20306         134 :         n->location = -1;
   20307         134 :         n->is_no_inherit = false;
   20308         134 :         n->raw_expr = NULL;
   20309         134 :         n->cooked_expr = nodeToString(make_ands_explicit(constraintExpr));
   20310         134 :         n->initially_valid = true;
   20311         134 :         n->skip_validation = true;
   20312             :         /* It's a re-add, since it nominally already exists */
   20313         134 :         ATAddCheckNNConstraint(wqueue, tab, partRel, n,
   20314             :                                true, false, true, ShareUpdateExclusiveLock);
   20315             :     }
   20316         140 : }
   20317             : 
   20318             : /*
   20319             :  * DropClonedTriggersFromPartition
   20320             :  *      subroutine for ATExecDetachPartition to remove any triggers that were
   20321             :  *      cloned to the partition when it was created-as-partition or attached.
   20322             :  *      This undoes what CloneRowTriggersToPartition did.
   20323             :  */
   20324             : static void
   20325         690 : DropClonedTriggersFromPartition(Oid partitionId)
   20326             : {
   20327             :     ScanKeyData skey;
   20328             :     SysScanDesc scan;
   20329             :     HeapTuple   trigtup;
   20330             :     Relation    tgrel;
   20331             :     ObjectAddresses *objects;
   20332             : 
   20333         690 :     objects = new_object_addresses();
   20334             : 
   20335             :     /*
   20336             :      * Scan pg_trigger to search for all triggers on this rel.
   20337             :      */
   20338         690 :     ScanKeyInit(&skey, Anum_pg_trigger_tgrelid, BTEqualStrategyNumber,
   20339             :                 F_OIDEQ, ObjectIdGetDatum(partitionId));
   20340         690 :     tgrel = table_open(TriggerRelationId, RowExclusiveLock);
   20341         690 :     scan = systable_beginscan(tgrel, TriggerRelidNameIndexId,
   20342             :                               true, NULL, 1, &skey);
   20343         988 :     while (HeapTupleIsValid(trigtup = systable_getnext(scan)))
   20344             :     {
   20345         298 :         Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(trigtup);
   20346             :         ObjectAddress trig;
   20347             : 
   20348             :         /* Ignore triggers that weren't cloned */
   20349         298 :         if (!OidIsValid(pg_trigger->tgparentid))
   20350         256 :             continue;
   20351             : 
   20352             :         /*
   20353             :          * Ignore internal triggers that are implementation objects of foreign
   20354             :          * keys, because these will be detached when the foreign keys
   20355             :          * themselves are.
   20356             :          */
   20357         262 :         if (OidIsValid(pg_trigger->tgconstrrelid))
   20358         220 :             continue;
   20359             : 
   20360             :         /*
   20361             :          * This is ugly, but necessary: remove the dependency markings on the
   20362             :          * trigger so that it can be removed.
   20363             :          */
   20364          42 :         deleteDependencyRecordsForClass(TriggerRelationId, pg_trigger->oid,
   20365             :                                         TriggerRelationId,
   20366             :                                         DEPENDENCY_PARTITION_PRI);
   20367          42 :         deleteDependencyRecordsForClass(TriggerRelationId, pg_trigger->oid,
   20368             :                                         RelationRelationId,
   20369             :                                         DEPENDENCY_PARTITION_SEC);
   20370             : 
   20371             :         /* remember this trigger to remove it below */
   20372          42 :         ObjectAddressSet(trig, TriggerRelationId, pg_trigger->oid);
   20373          42 :         add_exact_object_address(&trig, objects);
   20374             :     }
   20375             : 
   20376             :     /* make the dependency removal visible to the deletion below */
   20377         690 :     CommandCounterIncrement();
   20378         690 :     performMultipleDeletions(objects, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
   20379             : 
   20380             :     /* done */
   20381         690 :     free_object_addresses(objects);
   20382         690 :     systable_endscan(scan);
   20383         690 :     table_close(tgrel, RowExclusiveLock);
   20384         690 : }
   20385             : 
   20386             : /*
   20387             :  * Before acquiring lock on an index, acquire the same lock on the owning
   20388             :  * table.
   20389             :  */
   20390             : struct AttachIndexCallbackState
   20391             : {
   20392             :     Oid         partitionOid;
   20393             :     Oid         parentTblOid;
   20394             :     bool        lockedParentTbl;
   20395             : };
   20396             : 
   20397             : static void
   20398         392 : RangeVarCallbackForAttachIndex(const RangeVar *rv, Oid relOid, Oid oldRelOid,
   20399             :                                void *arg)
   20400             : {
   20401             :     struct AttachIndexCallbackState *state;
   20402             :     Form_pg_class classform;
   20403             :     HeapTuple   tuple;
   20404             : 
   20405         392 :     state = (struct AttachIndexCallbackState *) arg;
   20406             : 
   20407         392 :     if (!state->lockedParentTbl)
   20408             :     {
   20409         384 :         LockRelationOid(state->parentTblOid, AccessShareLock);
   20410         384 :         state->lockedParentTbl = true;
   20411             :     }
   20412             : 
   20413             :     /*
   20414             :      * If we previously locked some other heap, and the name we're looking up
   20415             :      * no longer refers to an index on that relation, release the now-useless
   20416             :      * lock.  XXX maybe we should do *after* we verify whether the index does
   20417             :      * not actually belong to the same relation ...
   20418             :      */
   20419         392 :     if (relOid != oldRelOid && OidIsValid(state->partitionOid))
   20420             :     {
   20421           0 :         UnlockRelationOid(state->partitionOid, AccessShareLock);
   20422           0 :         state->partitionOid = InvalidOid;
   20423             :     }
   20424             : 
   20425             :     /* Didn't find a relation, so no need for locking or permission checks. */
   20426         392 :     if (!OidIsValid(relOid))
   20427           6 :         return;
   20428             : 
   20429         386 :     tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid));
   20430         386 :     if (!HeapTupleIsValid(tuple))
   20431           0 :         return;                 /* concurrently dropped, so nothing to do */
   20432         386 :     classform = (Form_pg_class) GETSTRUCT(tuple);
   20433         386 :     if (classform->relkind != RELKIND_PARTITIONED_INDEX &&
   20434         296 :         classform->relkind != RELKIND_INDEX)
   20435           6 :         ereport(ERROR,
   20436             :                 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
   20437             :                  errmsg("\"%s\" is not an index", rv->relname)));
   20438         380 :     ReleaseSysCache(tuple);
   20439             : 
   20440             :     /*
   20441             :      * Since we need only examine the heap's tupledesc, an access share lock
   20442             :      * on it (preventing any DDL) is sufficient.
   20443             :      */
   20444         380 :     state->partitionOid = IndexGetRelation(relOid, false);
   20445         380 :     LockRelationOid(state->partitionOid, AccessShareLock);
   20446             : }
   20447             : 
   20448             : /*
   20449             :  * ALTER INDEX i1 ATTACH PARTITION i2
   20450             :  */
   20451             : static ObjectAddress
   20452         384 : ATExecAttachPartitionIdx(List **wqueue, Relation parentIdx, RangeVar *name)
   20453             : {
   20454             :     Relation    partIdx;
   20455             :     Relation    partTbl;
   20456             :     Relation    parentTbl;
   20457             :     ObjectAddress address;
   20458             :     Oid         partIdxId;
   20459             :     Oid         currParent;
   20460             :     struct AttachIndexCallbackState state;
   20461             : 
   20462             :     /*
   20463             :      * We need to obtain lock on the index 'name' to modify it, but we also
   20464             :      * need to read its owning table's tuple descriptor -- so we need to lock
   20465             :      * both.  To avoid deadlocks, obtain lock on the table before doing so on
   20466             :      * the index.  Furthermore, we need to examine the parent table of the
   20467             :      * partition, so lock that one too.
   20468             :      */
   20469         384 :     state.partitionOid = InvalidOid;
   20470         384 :     state.parentTblOid = parentIdx->rd_index->indrelid;
   20471         384 :     state.lockedParentTbl = false;
   20472             :     partIdxId =
   20473         384 :         RangeVarGetRelidExtended(name, AccessExclusiveLock, 0,
   20474             :                                  RangeVarCallbackForAttachIndex,
   20475             :                                  (void *) &state);
   20476             :     /* Not there? */
   20477         372 :     if (!OidIsValid(partIdxId))
   20478           0 :         ereport(ERROR,
   20479             :                 (errcode(ERRCODE_UNDEFINED_OBJECT),
   20480             :                  errmsg("index \"%s\" does not exist", name->relname)));
   20481             : 
   20482             :     /* no deadlock risk: RangeVarGetRelidExtended already acquired the lock */
   20483         372 :     partIdx = relation_open(partIdxId, AccessExclusiveLock);
   20484             : 
   20485             :     /* we already hold locks on both tables, so this is safe: */
   20486         372 :     parentTbl = relation_open(parentIdx->rd_index->indrelid, AccessShareLock);
   20487         372 :     partTbl = relation_open(partIdx->rd_index->indrelid, NoLock);
   20488             : 
   20489         372 :     ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partIdx));
   20490             : 
   20491             :     /* Silently do nothing if already in the right state */
   20492         744 :     currParent = partIdx->rd_rel->relispartition ?
   20493         372 :         get_partition_parent(partIdxId, false) : InvalidOid;
   20494         372 :     if (currParent != RelationGetRelid(parentIdx))
   20495             :     {
   20496             :         IndexInfo  *childInfo;
   20497             :         IndexInfo  *parentInfo;
   20498             :         AttrMap    *attmap;
   20499             :         bool        found;
   20500             :         int         i;
   20501             :         PartitionDesc partDesc;
   20502             :         Oid         constraintOid,
   20503         360 :                     cldConstrId = InvalidOid;
   20504             : 
   20505             :         /*
   20506             :          * If this partition already has an index attached, refuse the
   20507             :          * operation.
   20508             :          */
   20509         360 :         refuseDupeIndexAttach(parentIdx, partIdx, partTbl);
   20510             : 
   20511         354 :         if (OidIsValid(currParent))
   20512           0 :             ereport(ERROR,
   20513             :                     (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
   20514             :                      errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
   20515             :                             RelationGetRelationName(partIdx),
   20516             :                             RelationGetRelationName(parentIdx)),
   20517             :                      errdetail("Index \"%s\" is already attached to another index.",
   20518             :                                RelationGetRelationName(partIdx))));
   20519             : 
   20520             :         /* Make sure it indexes a partition of the other index's table */
   20521         354 :         partDesc = RelationGetPartitionDesc(parentTbl, true);
   20522         354 :         found = false;
   20523         556 :         for (i = 0; i < partDesc->nparts; i++)
   20524             :         {
   20525         550 :             if (partDesc->oids[i] == state.partitionOid)
   20526             :             {
   20527         348 :                 found = true;
   20528         348 :                 break;
   20529             :             }
   20530             :         }
   20531         354 :         if (!found)
   20532           6 :             ereport(ERROR,
   20533             :                     (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
   20534             :                      errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
   20535             :                             RelationGetRelationName(partIdx),
   20536             :                             RelationGetRelationName(parentIdx)),
   20537             :                      errdetail("Index \"%s\" is not an index on any partition of table \"%s\".",
   20538             :                                RelationGetRelationName(partIdx),
   20539             :                                RelationGetRelationName(parentTbl))));
   20540             : 
   20541             :         /* Ensure the indexes are compatible */
   20542         348 :         childInfo = BuildIndexInfo(partIdx);
   20543         348 :         parentInfo = BuildIndexInfo(parentIdx);
   20544         348 :         attmap = build_attrmap_by_name(RelationGetDescr(partTbl),
   20545             :                                        RelationGetDescr(parentTbl),
   20546             :                                        false);
   20547         348 :         if (!CompareIndexInfo(childInfo, parentInfo,
   20548         348 :                               partIdx->rd_indcollation,
   20549         348 :                               parentIdx->rd_indcollation,
   20550         348 :                               partIdx->rd_opfamily,
   20551         348 :                               parentIdx->rd_opfamily,
   20552             :                               attmap))
   20553          42 :             ereport(ERROR,
   20554             :                     (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
   20555             :                      errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
   20556             :                             RelationGetRelationName(partIdx),
   20557             :                             RelationGetRelationName(parentIdx)),
   20558             :                      errdetail("The index definitions do not match.")));
   20559             : 
   20560             :         /*
   20561             :          * If there is a constraint in the parent, make sure there is one in
   20562             :          * the child too.
   20563             :          */
   20564         306 :         constraintOid = get_relation_idx_constraint_oid(RelationGetRelid(parentTbl),
   20565             :                                                         RelationGetRelid(parentIdx));
   20566             : 
   20567         306 :         if (OidIsValid(constraintOid))
   20568             :         {
   20569         128 :             cldConstrId = get_relation_idx_constraint_oid(RelationGetRelid(partTbl),
   20570             :                                                           partIdxId);
   20571         128 :             if (!OidIsValid(cldConstrId))
   20572           6 :                 ereport(ERROR,
   20573             :                         (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
   20574             :                          errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
   20575             :                                 RelationGetRelationName(partIdx),
   20576             :                                 RelationGetRelationName(parentIdx)),
   20577             :                          errdetail("The index \"%s\" belongs to a constraint in table \"%s\" but no constraint exists for index \"%s\".",
   20578             :                                    RelationGetRelationName(parentIdx),
   20579             :                                    RelationGetRelationName(parentTbl),
   20580             :                                    RelationGetRelationName(partIdx))));
   20581             :         }
   20582             : 
   20583             :         /*
   20584             :          * If it's a primary key, make sure the columns in the partition are
   20585             :          * NOT NULL.
   20586             :          */
   20587         300 :         if (parentIdx->rd_index->indisprimary)
   20588          98 :             verifyPartitionIndexNotNull(childInfo, partTbl);
   20589             : 
   20590             :         /* All good -- do it */
   20591         294 :         IndexSetParentIndex(partIdx, RelationGetRelid(parentIdx));
   20592         294 :         if (OidIsValid(constraintOid))
   20593         116 :             ConstraintSetParentConstraint(cldConstrId, constraintOid,
   20594             :                                           RelationGetRelid(partTbl));
   20595             : 
   20596         294 :         free_attrmap(attmap);
   20597             : 
   20598         294 :         validatePartitionedIndex(parentIdx, parentTbl);
   20599             :     }
   20600             : 
   20601         306 :     relation_close(parentTbl, AccessShareLock);
   20602             :     /* keep these locks till commit */
   20603         306 :     relation_close(partTbl, NoLock);
   20604         306 :     relation_close(partIdx, NoLock);
   20605             : 
   20606         306 :     return address;
   20607             : }
   20608             : 
   20609             : /*
   20610             :  * Verify whether the given partition already contains an index attached
   20611             :  * to the given partitioned index.  If so, raise an error.
   20612             :  */
   20613             : static void
   20614         360 : refuseDupeIndexAttach(Relation parentIdx, Relation partIdx, Relation partitionTbl)
   20615             : {
   20616             :     Oid         existingIdx;
   20617             : 
   20618         360 :     existingIdx = index_get_partition(partitionTbl,
   20619             :                                       RelationGetRelid(parentIdx));
   20620         360 :     if (OidIsValid(existingIdx))
   20621           6 :         ereport(ERROR,
   20622             :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
   20623             :                  errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
   20624             :                         RelationGetRelationName(partIdx),
   20625             :                         RelationGetRelationName(parentIdx)),
   20626             :                  errdetail("Another index is already attached for partition \"%s\".",
   20627             :                            RelationGetRelationName(partitionTbl))));
   20628         354 : }
   20629             : 
   20630             : /*
   20631             :  * Verify whether the set of attached partition indexes to a parent index on
   20632             :  * a partitioned table is complete.  If it is, mark the parent index valid.
   20633             :  *
   20634             :  * This should be called each time a partition index is attached.
   20635             :  */
   20636             : static void
   20637         336 : validatePartitionedIndex(Relation partedIdx, Relation partedTbl)
   20638             : {
   20639             :     Relation    inheritsRel;
   20640             :     SysScanDesc scan;
   20641             :     ScanKeyData key;
   20642         336 :     int         tuples = 0;
   20643             :     HeapTuple   inhTup;
   20644         336 :     bool        updated = false;
   20645             : 
   20646             :     Assert(partedIdx->rd_rel->relkind == RELKIND_PARTITIONED_INDEX);
   20647             : 
   20648             :     /*
   20649             :      * Scan pg_inherits for this parent index.  Count each valid index we find
   20650             :      * (verifying the pg_index entry for each), and if we reach the total
   20651             :      * amount we expect, we can mark this parent index as valid.
   20652             :      */
   20653         336 :     inheritsRel = table_open(InheritsRelationId, AccessShareLock);
   20654         336 :     ScanKeyInit(&key, Anum_pg_inherits_inhparent,
   20655             :                 BTEqualStrategyNumber, F_OIDEQ,
   20656             :                 ObjectIdGetDatum(RelationGetRelid(partedIdx)));
   20657         336 :     scan = systable_beginscan(inheritsRel, InheritsParentIndexId, true,
   20658             :                               NULL, 1, &key);
   20659         876 :     while ((inhTup = systable_getnext(scan)) != NULL)
   20660             :     {
   20661         540 :         Form_pg_inherits inhForm = (Form_pg_inherits) GETSTRUCT(inhTup);
   20662             :         HeapTuple   indTup;
   20663             :         Form_pg_index indexForm;
   20664             : 
   20665         540 :         indTup = SearchSysCache1(INDEXRELID,
   20666             :                                  ObjectIdGetDatum(inhForm->inhrelid));
   20667         540 :         if (!HeapTupleIsValid(indTup))
   20668           0 :             elog(ERROR, "cache lookup failed for index %u", inhForm->inhrelid);
   20669         540 :         indexForm = (Form_pg_index) GETSTRUCT(indTup);
   20670         540 :         if (indexForm->indisvalid)
   20671         482 :             tuples += 1;
   20672         540 :         ReleaseSysCache(indTup);
   20673             :     }
   20674             : 
   20675             :     /* Done with pg_inherits */
   20676         336 :     systable_endscan(scan);
   20677         336 :     table_close(inheritsRel, AccessShareLock);
   20678             : 
   20679             :     /*
   20680             :      * If we found as many inherited indexes as the partitioned table has
   20681             :      * partitions, we're good; update pg_index to set indisvalid.
   20682             :      */
   20683         336 :     if (tuples == RelationGetPartitionDesc(partedTbl, true)->nparts)
   20684             :     {
   20685             :         Relation    idxRel;
   20686             :         HeapTuple   indTup;
   20687             :         Form_pg_index indexForm;
   20688             : 
   20689         166 :         idxRel = table_open(IndexRelationId, RowExclusiveLock);
   20690         166 :         indTup = SearchSysCacheCopy1(INDEXRELID,
   20691             :                                      ObjectIdGetDatum(RelationGetRelid(partedIdx)));
   20692         166 :         if (!HeapTupleIsValid(indTup))
   20693           0 :             elog(ERROR, "cache lookup failed for index %u",
   20694             :                  RelationGetRelid(partedIdx));
   20695         166 :         indexForm = (Form_pg_index) GETSTRUCT(indTup);
   20696             : 
   20697         166 :         indexForm->indisvalid = true;
   20698         166 :         updated = true;
   20699             : 
   20700         166 :         CatalogTupleUpdate(idxRel, &indTup->t_self, indTup);
   20701             : 
   20702         166 :         table_close(idxRel, RowExclusiveLock);
   20703         166 :         heap_freetuple(indTup);
   20704             :     }
   20705             : 
   20706             :     /*
   20707             :      * If this index is in turn a partition of a larger index, validating it
   20708             :      * might cause the parent to become valid also.  Try that.
   20709             :      */
   20710         336 :     if (updated && partedIdx->rd_rel->relispartition)
   20711             :     {
   20712             :         Oid         parentIdxId,
   20713             :                     parentTblId;
   20714             :         Relation    parentIdx,
   20715             :                     parentTbl;
   20716             : 
   20717             :         /* make sure we see the validation we just did */
   20718          42 :         CommandCounterIncrement();
   20719             : 
   20720          42 :         parentIdxId = get_partition_parent(RelationGetRelid(partedIdx), false);
   20721          42 :         parentTblId = get_partition_parent(RelationGetRelid(partedTbl), false);
   20722          42 :         parentIdx = relation_open(parentIdxId, AccessExclusiveLock);
   20723          42 :         parentTbl = relation_open(parentTblId, AccessExclusiveLock);
   20724             :         Assert(!parentIdx->rd_index->indisvalid);
   20725             : 
   20726          42 :         validatePartitionedIndex(parentIdx, parentTbl);
   20727             : 
   20728          42 :         relation_close(parentIdx, AccessExclusiveLock);
   20729          42 :         relation_close(parentTbl, AccessExclusiveLock);
   20730             :     }
   20731         336 : }
   20732             : 
   20733             : /*
   20734             :  * When attaching an index as a partition of a partitioned index which is a
   20735             :  * primary key, verify that all the columns in the partition are marked NOT
   20736             :  * NULL.
   20737             :  */
   20738             : static void
   20739          98 : verifyPartitionIndexNotNull(IndexInfo *iinfo, Relation partition)
   20740             : {
   20741         192 :     for (int i = 0; i < iinfo->ii_NumIndexKeyAttrs; i++)
   20742             :     {
   20743         100 :         Form_pg_attribute att = TupleDescAttr(RelationGetDescr(partition),
   20744             :                                               iinfo->ii_IndexAttrNumbers[i] - 1);
   20745             : 
   20746         100 :         if (!att->attnotnull)
   20747           6 :             ereport(ERROR,
   20748             :                     errcode(ERRCODE_INVALID_TABLE_DEFINITION),
   20749             :                     errmsg("invalid primary key definition"),
   20750             :                     errdetail("Column \"%s\" of relation \"%s\" is not marked NOT NULL.",
   20751             :                               NameStr(att->attname),
   20752             :                               RelationGetRelationName(partition)));
   20753             :     }
   20754          92 : }
   20755             : 
   20756             : /*
   20757             :  * Return an OID list of constraints that reference the given relation
   20758             :  * that are marked as having a parent constraints.
   20759             :  */
   20760             : static List *
   20761        1162 : GetParentedForeignKeyRefs(Relation partition)
   20762             : {
   20763             :     Relation    pg_constraint;
   20764             :     HeapTuple   tuple;
   20765             :     SysScanDesc scan;
   20766             :     ScanKeyData key[2];
   20767        1162 :     List       *constraints = NIL;
   20768             : 
   20769             :     /*
   20770             :      * If no indexes, or no columns are referenceable by FKs, we can avoid the
   20771             :      * scan.
   20772             :      */
   20773        1644 :     if (RelationGetIndexList(partition) == NIL ||
   20774         482 :         bms_is_empty(RelationGetIndexAttrBitmap(partition,
   20775             :                                                 INDEX_ATTR_BITMAP_KEY)))
   20776         926 :         return NIL;
   20777             : 
   20778             :     /* Search for constraints referencing this table */
   20779         236 :     pg_constraint = table_open(ConstraintRelationId, AccessShareLock);
   20780         236 :     ScanKeyInit(&key[0],
   20781             :                 Anum_pg_constraint_confrelid, BTEqualStrategyNumber,
   20782             :                 F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(partition)));
   20783         236 :     ScanKeyInit(&key[1],
   20784             :                 Anum_pg_constraint_contype, BTEqualStrategyNumber,
   20785             :                 F_CHAREQ, CharGetDatum(CONSTRAINT_FOREIGN));
   20786             : 
   20787             :     /* XXX This is a seqscan, as we don't have a usable index */
   20788         236 :     scan = systable_beginscan(pg_constraint, InvalidOid, true, NULL, 2, key);
   20789         366 :     while ((tuple = systable_getnext(scan)) != NULL)
   20790             :     {
   20791         130 :         Form_pg_constraint constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
   20792             : 
   20793             :         /*
   20794             :          * We only need to process constraints that are part of larger ones.
   20795             :          */
   20796         130 :         if (!OidIsValid(constrForm->conparentid))
   20797           0 :             continue;
   20798             : 
   20799         130 :         constraints = lappend_oid(constraints, constrForm->oid);
   20800             :     }
   20801             : 
   20802         236 :     systable_endscan(scan);
   20803         236 :     table_close(pg_constraint, AccessShareLock);
   20804             : 
   20805         236 :     return constraints;
   20806             : }
   20807             : 
   20808             : /*
   20809             :  * During DETACH PARTITION, verify that any foreign keys pointing to the
   20810             :  * partitioned table would not become invalid.  An error is raised if any
   20811             :  * referenced values exist.
   20812             :  */
   20813             : static void
   20814         472 : ATDetachCheckNoForeignKeyRefs(Relation partition)
   20815             : {
   20816             :     List       *constraints;
   20817             :     ListCell   *cell;
   20818             : 
   20819         472 :     constraints = GetParentedForeignKeyRefs(partition);
   20820             : 
   20821         524 :     foreach(cell, constraints)
   20822             :     {
   20823          86 :         Oid         constrOid = lfirst_oid(cell);
   20824             :         HeapTuple   tuple;
   20825             :         Form_pg_constraint constrForm;
   20826             :         Relation    rel;
   20827          86 :         Trigger     trig = {0};
   20828             : 
   20829          86 :         tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constrOid));
   20830          86 :         if (!HeapTupleIsValid(tuple))
   20831           0 :             elog(ERROR, "cache lookup failed for constraint %u", constrOid);
   20832          86 :         constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
   20833             : 
   20834             :         Assert(OidIsValid(constrForm->conparentid));
   20835             :         Assert(constrForm->confrelid == RelationGetRelid(partition));
   20836             : 
   20837             :         /* prevent data changes into the referencing table until commit */
   20838          86 :         rel = table_open(constrForm->conrelid, ShareLock);
   20839             : 
   20840          86 :         trig.tgoid = InvalidOid;
   20841          86 :         trig.tgname = NameStr(constrForm->conname);
   20842          86 :         trig.tgenabled = TRIGGER_FIRES_ON_ORIGIN;
   20843          86 :         trig.tgisinternal = true;
   20844          86 :         trig.tgconstrrelid = RelationGetRelid(partition);
   20845          86 :         trig.tgconstrindid = constrForm->conindid;
   20846          86 :         trig.tgconstraint = constrForm->oid;
   20847          86 :         trig.tgdeferrable = false;
   20848          86 :         trig.tginitdeferred = false;
   20849             :         /* we needn't fill in remaining fields */
   20850             : 
   20851          86 :         RI_PartitionRemove_Check(&trig, rel, partition);
   20852             : 
   20853          52 :         ReleaseSysCache(tuple);
   20854             : 
   20855          52 :         table_close(rel, NoLock);
   20856             :     }
   20857         438 : }
   20858             : 
   20859             : /*
   20860             :  * resolve column compression specification to compression method.
   20861             :  */
   20862             : static char
   20863      217540 : GetAttributeCompression(Oid atttypid, const char *compression)
   20864             : {
   20865             :     char        cmethod;
   20866             : 
   20867      217540 :     if (compression == NULL || strcmp(compression, "default") == 0)
   20868      217398 :         return InvalidCompressionMethod;
   20869             : 
   20870             :     /*
   20871             :      * To specify a nondefault method, the column data type must be toastable.
   20872             :      * Note this says nothing about whether the column's attstorage setting
   20873             :      * permits compression; we intentionally allow attstorage and
   20874             :      * attcompression to be independent.  But with a non-toastable type,
   20875             :      * attstorage could not be set to a value that would permit compression.
   20876             :      *
   20877             :      * We don't actually need to enforce this, since nothing bad would happen
   20878             :      * if attcompression were non-default; it would never be consulted.  But
   20879             :      * it seems more user-friendly to complain about a certainly-useless
   20880             :      * attempt to set the property.
   20881             :      */
   20882         142 :     if (!TypeIsToastable(atttypid))
   20883           6 :         ereport(ERROR,
   20884             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   20885             :                  errmsg("column data type %s does not support compression",
   20886             :                         format_type_be(atttypid))));
   20887             : 
   20888         136 :     cmethod = CompressionNameToMethod(compression);
   20889         136 :     if (!CompressionMethodIsValid(cmethod))
   20890          12 :         ereport(ERROR,
   20891             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
   20892             :                  errmsg("invalid compression method \"%s\"", compression)));
   20893             : 
   20894         124 :     return cmethod;
   20895             : }
   20896             : 
   20897             : /*
   20898             :  * resolve column storage specification
   20899             :  */
   20900             : static char
   20901         242 : GetAttributeStorage(Oid atttypid, const char *storagemode)
   20902             : {
   20903         242 :     char        cstorage = 0;
   20904             : 
   20905         242 :     if (pg_strcasecmp(storagemode, "plain") == 0)
   20906          50 :         cstorage = TYPSTORAGE_PLAIN;
   20907         192 :     else if (pg_strcasecmp(storagemode, "external") == 0)
   20908         156 :         cstorage = TYPSTORAGE_EXTERNAL;
   20909          36 :     else if (pg_strcasecmp(storagemode, "extended") == 0)
   20910          16 :         cstorage = TYPSTORAGE_EXTENDED;
   20911          20 :     else if (pg_strcasecmp(storagemode, "main") == 0)
   20912          14 :         cstorage = TYPSTORAGE_MAIN;
   20913           6 :     else if (pg_strcasecmp(storagemode, "default") == 0)
   20914           6 :         cstorage = get_typstorage(atttypid);
   20915             :     else
   20916           0 :         ereport(ERROR,
   20917             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
   20918             :                  errmsg("invalid storage type \"%s\"",
   20919             :                         storagemode)));
   20920             : 
   20921             :     /*
   20922             :      * safety check: do not allow toasted storage modes unless column datatype
   20923             :      * is TOAST-aware.
   20924             :      */
   20925         242 :     if (!(cstorage == TYPSTORAGE_PLAIN || TypeIsToastable(atttypid)))
   20926           6 :         ereport(ERROR,
   20927             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   20928             :                  errmsg("column data type %s can only have storage PLAIN",
   20929             :                         format_type_be(atttypid))));
   20930             : 
   20931         236 :     return cstorage;
   20932             : }
   20933             : 
   20934             : /*
   20935             :  * Struct with context of new partition for inserting rows from split partition
   20936             :  */
   20937             : typedef struct SplitPartitionContext
   20938             : {
   20939             :     ExprState  *partqualstate;  /* expression for checking slot for partition
   20940             :                                  * (NULL for DEFAULT partition) */
   20941             :     BulkInsertState bistate;    /* state of bulk inserts for partition */
   20942             :     TupleTableSlot *dstslot;    /* slot for inserting row into partition */
   20943             :     Relation    partRel;        /* relation for partition */
   20944             : } SplitPartitionContext;
   20945             : 
   20946             : 
   20947             : /*
   20948             :  * createSplitPartitionContext: create context for partition and fill it
   20949             :  */
   20950             : static SplitPartitionContext *
   20951         402 : createSplitPartitionContext(Relation partRel)
   20952             : {
   20953             :     SplitPartitionContext *pc;
   20954             : 
   20955         402 :     pc = (SplitPartitionContext *) palloc0(sizeof(SplitPartitionContext));
   20956         402 :     pc->partRel = partRel;
   20957             : 
   20958             :     /*
   20959             :      * Prepare a BulkInsertState for table_tuple_insert. The FSM is empty, so
   20960             :      * don't bother using it.
   20961             :      */
   20962         402 :     pc->bistate = GetBulkInsertState();
   20963             : 
   20964             :     /* Create tuple slot for new partition. */
   20965         402 :     pc->dstslot = MakeSingleTupleTableSlot(RelationGetDescr(pc->partRel),
   20966             :                                            table_slot_callbacks(pc->partRel));
   20967         402 :     ExecStoreAllNullTuple(pc->dstslot);
   20968             : 
   20969         402 :     return pc;
   20970             : }
   20971             : 
   20972             : /*
   20973             :  * deleteSplitPartitionContext: delete context for partition
   20974             :  */
   20975             : static void
   20976         402 : deleteSplitPartitionContext(SplitPartitionContext *pc, int ti_options)
   20977             : {
   20978         402 :     ExecDropSingleTupleTableSlot(pc->dstslot);
   20979         402 :     FreeBulkInsertState(pc->bistate);
   20980             : 
   20981         402 :     table_finish_bulk_insert(pc->partRel, ti_options);
   20982             : 
   20983         402 :     pfree(pc);
   20984         402 : }
   20985             : 
   20986             : /*
   20987             :  * moveSplitTableRows: scan split partition (splitRel) of partitioned table
   20988             :  * (rel) and move rows into new partitions.
   20989             :  *
   20990             :  * New partitions description:
   20991             :  * partlist: list of pointers to SinglePartitionSpec structures.
   20992             :  * newPartRels: list of Relations.
   20993             :  * defaultPartOid: oid of DEFAULT partition, for table rel.
   20994             :  */
   20995             : static void
   20996         114 : moveSplitTableRows(Relation rel, Relation splitRel, List *partlist, List *newPartRels, Oid defaultPartOid)
   20997             : {
   20998             :     /* The FSM is empty, so don't bother using it. */
   20999         114 :     int         ti_options = TABLE_INSERT_SKIP_FSM;
   21000             :     CommandId   mycid;
   21001             :     EState     *estate;
   21002             :     ListCell   *listptr,
   21003             :                *listptr2;
   21004             :     TupleTableSlot *srcslot;
   21005             :     ExprContext *econtext;
   21006             :     TableScanDesc scan;
   21007             :     Snapshot    snapshot;
   21008             :     MemoryContext oldCxt;
   21009         114 :     List       *partContexts = NIL;
   21010             :     TupleConversionMap *tuple_map;
   21011         114 :     SplitPartitionContext *defaultPartCtx = NULL,
   21012             :                *pc;
   21013         114 :     bool        isOldDefaultPart = false;
   21014             : 
   21015         114 :     mycid = GetCurrentCommandId(true);
   21016             : 
   21017         114 :     estate = CreateExecutorState();
   21018             : 
   21019         468 :     forboth(listptr, partlist, listptr2, newPartRels)
   21020             :     {
   21021         354 :         SinglePartitionSpec *sps = (SinglePartitionSpec *) lfirst(listptr);
   21022             : 
   21023         354 :         pc = createSplitPartitionContext((Relation) lfirst(listptr2));
   21024             : 
   21025         354 :         if (sps->bound->is_default)
   21026             :         {
   21027             :             /* We should not create constraint for detached DEFAULT partition. */
   21028          30 :             defaultPartCtx = pc;
   21029             :         }
   21030             :         else
   21031             :         {
   21032             :             List       *partConstraint;
   21033             : 
   21034             :             /* Build expression execution states for partition check quals. */
   21035         324 :             partConstraint = get_qual_from_partbound(rel, sps->bound);
   21036             :             partConstraint =
   21037         324 :                 (List *) eval_const_expressions(NULL,
   21038             :                                                 (Node *) partConstraint);
   21039             :             /* Make boolean expression for ExecCheck(). */
   21040         324 :             partConstraint = list_make1(make_ands_explicit(partConstraint));
   21041             : 
   21042             :             /*
   21043             :              * Map the vars in the constraint expression from rel's attnos to
   21044             :              * splitRel's.
   21045             :              */
   21046         324 :             partConstraint = map_partition_varattnos(partConstraint,
   21047             :                                                      1, splitRel, rel);
   21048             : 
   21049         324 :             pc->partqualstate =
   21050         324 :                 ExecPrepareExpr((Expr *) linitial(partConstraint), estate);
   21051             :             Assert(pc->partqualstate != NULL);
   21052             :         }
   21053             : 
   21054             :         /* Store partition context into list. */
   21055         354 :         partContexts = lappend(partContexts, pc);
   21056             :     }
   21057             : 
   21058             :     /*
   21059             :      * Create partition context for DEFAULT partition. We can insert values
   21060             :      * into this partition in case spaces with values between new partitions.
   21061             :      */
   21062         114 :     if (!defaultPartCtx && OidIsValid(defaultPartOid))
   21063             :     {
   21064             :         /* Indicate that we allocate context for old DEFAULT partition */
   21065          48 :         isOldDefaultPart = true;
   21066          48 :         defaultPartCtx = createSplitPartitionContext(table_open(defaultPartOid, AccessExclusiveLock));
   21067             :     }
   21068             : 
   21069         114 :     econtext = GetPerTupleExprContext(estate);
   21070             : 
   21071             :     /* Create necessary tuple slot. */
   21072         114 :     srcslot = MakeSingleTupleTableSlot(RelationGetDescr(splitRel),
   21073             :                                        table_slot_callbacks(splitRel));
   21074             : 
   21075             :     /*
   21076             :      * Map computing for moving attributes of split partition to new partition
   21077             :      * (for first new partition, but other new partitions can use the same
   21078             :      * map).
   21079             :      */
   21080         114 :     pc = (SplitPartitionContext *) lfirst(list_head(partContexts));
   21081         114 :     tuple_map = convert_tuples_by_name(RelationGetDescr(splitRel),
   21082         114 :                                        RelationGetDescr(pc->partRel));
   21083             : 
   21084             :     /* Scan through the rows. */
   21085         114 :     snapshot = RegisterSnapshot(GetLatestSnapshot());
   21086         114 :     scan = table_beginscan(splitRel, snapshot, 0, NULL);
   21087             : 
   21088             :     /*
   21089             :      * Switch to per-tuple memory context and reset it for each tuple
   21090             :      * produced, so we don't leak memory.
   21091             :      */
   21092         114 :     oldCxt = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
   21093             : 
   21094         654 :     while (table_scan_getnextslot(scan, ForwardScanDirection, srcslot))
   21095             :     {
   21096         540 :         bool        found = false;
   21097             :         TupleTableSlot *insertslot;
   21098             : 
   21099             :         /* Extract data from old tuple. */
   21100         540 :         slot_getallattrs(srcslot);
   21101             : 
   21102         540 :         econtext->ecxt_scantuple = srcslot;
   21103             : 
   21104             :         /* Search partition for current slot srcslot. */
   21105        1488 :         foreach(listptr, partContexts)
   21106             :         {
   21107        1374 :             pc = (SplitPartitionContext *) lfirst(listptr);
   21108             : 
   21109        2640 :             if (pc->partqualstate /* skip DEFAULT partition */ &&
   21110        1266 :                 ExecCheck(pc->partqualstate, econtext))
   21111             :             {
   21112         426 :                 found = true;
   21113         426 :                 break;
   21114             :             }
   21115         948 :             ResetExprContext(econtext);
   21116             :         }
   21117         540 :         if (!found)
   21118             :         {
   21119             :             /* Use DEFAULT partition if it exists. */
   21120         114 :             if (defaultPartCtx)
   21121         114 :                 pc = defaultPartCtx;
   21122             :             else
   21123           0 :                 ereport(ERROR,
   21124             :                         (errcode(ERRCODE_CHECK_VIOLATION),
   21125             :                          errmsg("can not find partition for split partition row"),
   21126             :                          errtable(splitRel)));
   21127             :         }
   21128             : 
   21129         540 :         if (tuple_map)
   21130             :         {
   21131             :             /* Need to use map to copy attributes. */
   21132          24 :             insertslot = execute_attr_map_slot(tuple_map->attrMap, srcslot, pc->dstslot);
   21133             :         }
   21134             :         else
   21135             :         {
   21136             :             /* Copy attributes directly. */
   21137         516 :             insertslot = pc->dstslot;
   21138             : 
   21139         516 :             ExecClearTuple(insertslot);
   21140             : 
   21141         516 :             memcpy(insertslot->tts_values, srcslot->tts_values,
   21142         516 :                    sizeof(Datum) * srcslot->tts_nvalid);
   21143         516 :             memcpy(insertslot->tts_isnull, srcslot->tts_isnull,
   21144         516 :                    sizeof(bool) * srcslot->tts_nvalid);
   21145             : 
   21146         516 :             ExecStoreVirtualTuple(insertslot);
   21147             :         }
   21148             : 
   21149             :         /* Write the tuple out to the new relation. */
   21150         540 :         table_tuple_insert(pc->partRel, insertslot, mycid,
   21151             :                            ti_options, pc->bistate);
   21152             : 
   21153         540 :         ResetExprContext(econtext);
   21154             : 
   21155         540 :         CHECK_FOR_INTERRUPTS();
   21156             :     }
   21157             : 
   21158         114 :     MemoryContextSwitchTo(oldCxt);
   21159             : 
   21160         114 :     table_endscan(scan);
   21161         114 :     UnregisterSnapshot(snapshot);
   21162             : 
   21163         114 :     if (tuple_map)
   21164           6 :         free_conversion_map(tuple_map);
   21165             : 
   21166         114 :     ExecDropSingleTupleTableSlot(srcslot);
   21167             : 
   21168         114 :     FreeExecutorState(estate);
   21169             : 
   21170         468 :     foreach(listptr, partContexts)
   21171         354 :         deleteSplitPartitionContext((SplitPartitionContext *) lfirst(listptr), ti_options);
   21172             : 
   21173             :     /* Need to close table and free buffers for DEFAULT partition. */
   21174         114 :     if (isOldDefaultPart)
   21175             :     {
   21176          48 :         Relation    defaultPartRel = defaultPartCtx->partRel;
   21177             : 
   21178          48 :         deleteSplitPartitionContext(defaultPartCtx, ti_options);
   21179             :         /* Keep the lock until commit. */
   21180          48 :         table_close(defaultPartRel, NoLock);
   21181             :     }
   21182         114 : }
   21183             : 
   21184             : /*
   21185             :  * createPartitionTable: create table for a new partition with given name
   21186             :  * (newPartName) like table (modelRelName)
   21187             :  *
   21188             :  * Emulates command: CREATE TABLE <newPartName> (LIKE <modelRelName>
   21189             :  * INCLUDING ALL EXCLUDING INDEXES EXCLUDING IDENTITY)
   21190             :  */
   21191             : static void
   21192         420 : createPartitionTable(RangeVar *newPartName, RangeVar *modelRelName,
   21193             :                      AlterTableUtilityContext *context)
   21194             : {
   21195             :     CreateStmt *createStmt;
   21196             :     TableLikeClause *tlc;
   21197             :     PlannedStmt *wrapper;
   21198             : 
   21199         420 :     createStmt = makeNode(CreateStmt);
   21200         420 :     createStmt->relation = newPartName;
   21201         420 :     createStmt->tableElts = NIL;
   21202         420 :     createStmt->inhRelations = NIL;
   21203         420 :     createStmt->constraints = NIL;
   21204         420 :     createStmt->options = NIL;
   21205         420 :     createStmt->oncommit = ONCOMMIT_NOOP;
   21206         420 :     createStmt->tablespacename = NULL;
   21207         420 :     createStmt->if_not_exists = false;
   21208             : 
   21209         420 :     tlc = makeNode(TableLikeClause);
   21210         420 :     tlc->relation = modelRelName;
   21211             : 
   21212             :     /*
   21213             :      * Indexes will be inherited on "attach new partitions" stage, after data
   21214             :      * moving.
   21215             :      */
   21216         420 :     tlc->options = CREATE_TABLE_LIKE_ALL & ~(CREATE_TABLE_LIKE_INDEXES | CREATE_TABLE_LIKE_IDENTITY);
   21217         420 :     tlc->relationOid = InvalidOid;
   21218         420 :     createStmt->tableElts = lappend(createStmt->tableElts, tlc);
   21219             : 
   21220             :     /* Need to make a wrapper PlannedStmt. */
   21221         420 :     wrapper = makeNode(PlannedStmt);
   21222         420 :     wrapper->commandType = CMD_UTILITY;
   21223         420 :     wrapper->canSetTag = false;
   21224         420 :     wrapper->utilityStmt = (Node *) createStmt;
   21225         420 :     wrapper->stmt_location = context->pstmt->stmt_location;
   21226         420 :     wrapper->stmt_len = context->pstmt->stmt_len;
   21227             : 
   21228         420 :     ProcessUtility(wrapper,
   21229             :                    context->queryString,
   21230             :                    false,
   21231             :                    PROCESS_UTILITY_SUBCOMMAND,
   21232             :                    NULL,
   21233             :                    NULL,
   21234             :                    None_Receiver,
   21235             :                    NULL);
   21236         420 : }
   21237             : 
   21238             : /*
   21239             :  * ALTER TABLE <name> SPLIT PARTITION <partition-name> INTO <partition-list>
   21240             :  */
   21241             : static void
   21242         120 : ATExecSplitPartition(List **wqueue, AlteredTableInfo *tab, Relation rel,
   21243             :                      PartitionCmd *cmd, AlterTableUtilityContext *context)
   21244             : {
   21245             :     Relation    splitRel;
   21246             :     Oid         splitRelOid;
   21247             :     char        relname[NAMEDATALEN];
   21248             :     Oid         namespaceId;
   21249             :     ListCell   *listptr,
   21250             :                *listptr2;
   21251         120 :     bool        isSameName = false;
   21252             :     char        tmpRelName[NAMEDATALEN];
   21253         120 :     List       *newPartRels = NIL;
   21254             :     ObjectAddress object;
   21255             :     RangeVar   *parentName;
   21256             :     Oid         defaultPartOid;
   21257             : 
   21258         120 :     defaultPartOid = get_default_oid_from_partdesc(RelationGetPartitionDesc(rel, true));
   21259             : 
   21260             :     /*
   21261             :      * We are going to detach and remove this partition: need to use exclusive
   21262             :      * lock for preventing DML-queries to the partition.
   21263             :      */
   21264         120 :     splitRel = table_openrv(cmd->name, AccessExclusiveLock);
   21265             : 
   21266         120 :     splitRelOid = RelationGetRelid(splitRel);
   21267             : 
   21268             :     /* Check descriptions of new partitions. */
   21269         474 :     foreach(listptr, cmd->partlist)
   21270             :     {
   21271             :         Oid         existing_relid;
   21272         360 :         SinglePartitionSpec *sps = (SinglePartitionSpec *) lfirst(listptr);
   21273             : 
   21274         360 :         strlcpy(relname, sps->name->relname, NAMEDATALEN);
   21275             : 
   21276             :         /*
   21277             :          * Look up the namespace in which we are supposed to create the
   21278             :          * partition, check we have permission to create there, lock it
   21279             :          * against concurrent drop, and mark stmt->relation as
   21280             :          * RELPERSISTENCE_TEMP if a temporary namespace is selected.
   21281             :          */
   21282             :         namespaceId =
   21283         360 :             RangeVarGetAndCheckCreationNamespace(sps->name, NoLock, NULL);
   21284             : 
   21285             :         /*
   21286             :          * This would fail later on anyway if the relation already exists. But
   21287             :          * by catching it here we can emit a nicer error message.
   21288             :          */
   21289         360 :         existing_relid = get_relname_relid(relname, namespaceId);
   21290         360 :         if (existing_relid == splitRelOid && !isSameName)
   21291             :             /* One new partition can have the same name as split partition. */
   21292          24 :             isSameName = true;
   21293         336 :         else if (existing_relid != InvalidOid)
   21294           6 :             ereport(ERROR,
   21295             :                     (errcode(ERRCODE_DUPLICATE_TABLE),
   21296             :                      errmsg("relation \"%s\" already exists", relname)));
   21297             :     }
   21298             : 
   21299             :     /* Detach split partition. */
   21300         114 :     RemoveInheritance(splitRel, rel, false);
   21301             :     /* Do the final part of detaching. */
   21302         114 :     DetachPartitionFinalize(rel, splitRel, false, defaultPartOid);
   21303             : 
   21304             :     /*
   21305             :      * If new partition has the same name as split partition then we should
   21306             :      * rename split partition for reusing name.
   21307             :      */
   21308         114 :     if (isSameName)
   21309             :     {
   21310             :         /*
   21311             :          * We must bump the command counter to make the split partition tuple
   21312             :          * visible for renaming.
   21313             :          */
   21314          24 :         CommandCounterIncrement();
   21315             :         /* Rename partition. */
   21316          24 :         sprintf(tmpRelName, "split-%u-%X-tmp", RelationGetRelid(rel), MyProcPid);
   21317          24 :         RenameRelationInternal(splitRelOid, tmpRelName, false, false);
   21318             : 
   21319             :         /*
   21320             :          * We must bump the command counter to make the split partition tuple
   21321             :          * visible after renaming.
   21322             :          */
   21323          24 :         CommandCounterIncrement();
   21324             :     }
   21325             : 
   21326             :     /* Create new partitions (like split partition), without indexes. */
   21327         114 :     parentName = makeRangeVar(get_namespace_name(RelationGetNamespace(rel)),
   21328         114 :                               RelationGetRelationName(rel), -1);
   21329         468 :     foreach(listptr, cmd->partlist)
   21330             :     {
   21331         354 :         SinglePartitionSpec *sps = (SinglePartitionSpec *) lfirst(listptr);
   21332             :         Relation    newPartRel;
   21333             : 
   21334         354 :         createPartitionTable(sps->name, parentName, context);
   21335             : 
   21336             :         /* Open the new partition and acquire exclusive lock on it. */
   21337         354 :         newPartRel = table_openrv(sps->name, AccessExclusiveLock);
   21338             : 
   21339         354 :         newPartRels = lappend(newPartRels, newPartRel);
   21340             :     }
   21341             : 
   21342             :     /* Copy data from split partition to new partitions. */
   21343         114 :     moveSplitTableRows(rel, splitRel, cmd->partlist, newPartRels, defaultPartOid);
   21344             :     /* Keep the lock until commit. */
   21345         114 :     table_close(splitRel, NoLock);
   21346             : 
   21347             :     /* Attach new partitions to partitioned table. */
   21348         468 :     forboth(listptr, cmd->partlist, listptr2, newPartRels)
   21349             :     {
   21350         354 :         SinglePartitionSpec *sps = (SinglePartitionSpec *) lfirst(listptr);
   21351         354 :         Relation    newPartRel = (Relation) lfirst(listptr2);
   21352             : 
   21353             :         /*
   21354             :          * wqueue = NULL: verification for each cloned constraint is not
   21355             :          * needed.
   21356             :          */
   21357         354 :         attachPartitionTable(NULL, rel, newPartRel, sps->bound);
   21358             :         /* Keep the lock until commit. */
   21359         354 :         table_close(newPartRel, NoLock);
   21360             :     }
   21361             : 
   21362             :     /* Drop split partition. */
   21363         114 :     object.classId = RelationRelationId;
   21364         114 :     object.objectId = splitRelOid;
   21365         114 :     object.objectSubId = 0;
   21366             :     /* Probably DROP_CASCADE is not needed. */
   21367         114 :     performDeletion(&object, DROP_RESTRICT, 0);
   21368         114 : }
   21369             : 
   21370             : /*
   21371             :  * moveMergedTablesRows: scan partitions to be merged (mergingPartitionsList)
   21372             :  * of the partitioned table (rel) and move rows into the new partition
   21373             :  * (newPartRel).
   21374             :  */
   21375             : static void
   21376          66 : moveMergedTablesRows(Relation rel, List *mergingPartitionsList,
   21377             :                      Relation newPartRel)
   21378             : {
   21379             :     CommandId   mycid;
   21380             : 
   21381             :     /* The FSM is empty, so don't bother using it. */
   21382          66 :     int         ti_options = TABLE_INSERT_SKIP_FSM;
   21383             :     ListCell   *listptr;
   21384             :     BulkInsertState bistate;    /* state of bulk inserts for partition */
   21385             :     TupleTableSlot *dstslot;
   21386             : 
   21387          66 :     mycid = GetCurrentCommandId(true);
   21388             : 
   21389             :     /* Prepare a BulkInsertState for table_tuple_insert. */
   21390          66 :     bistate = GetBulkInsertState();
   21391             : 
   21392             :     /* Create necessary tuple slot. */
   21393          66 :     dstslot = MakeSingleTupleTableSlot(RelationGetDescr(newPartRel),
   21394             :                                        table_slot_callbacks(newPartRel));
   21395          66 :     ExecStoreAllNullTuple(dstslot);
   21396             : 
   21397         240 :     foreach(listptr, mergingPartitionsList)
   21398             :     {
   21399         174 :         Relation    mergingPartition = (Relation) lfirst(listptr);
   21400             :         TupleTableSlot *srcslot;
   21401             :         TupleConversionMap *tuple_map;
   21402             :         TableScanDesc scan;
   21403             :         Snapshot    snapshot;
   21404             : 
   21405             :         /* Create tuple slot for new partition. */
   21406         174 :         srcslot = MakeSingleTupleTableSlot(RelationGetDescr(mergingPartition),
   21407             :                                            table_slot_callbacks(mergingPartition));
   21408             : 
   21409             :         /*
   21410             :          * Map computing for moving attributes of merged partition to new
   21411             :          * partition.
   21412             :          */
   21413         174 :         tuple_map = convert_tuples_by_name(RelationGetDescr(mergingPartition),
   21414             :                                            RelationGetDescr(newPartRel));
   21415             : 
   21416             :         /* Scan through the rows. */
   21417         174 :         snapshot = RegisterSnapshot(GetLatestSnapshot());
   21418         174 :         scan = table_beginscan(mergingPartition, snapshot, 0, NULL);
   21419             : 
   21420         480 :         while (table_scan_getnextslot(scan, ForwardScanDirection, srcslot))
   21421             :         {
   21422             :             TupleTableSlot *insertslot;
   21423             : 
   21424             :             /* Extract data from old tuple. */
   21425         306 :             slot_getallattrs(srcslot);
   21426             : 
   21427         306 :             if (tuple_map)
   21428             :             {
   21429             :                 /* Need to use map to copy attributes. */
   21430          30 :                 insertslot = execute_attr_map_slot(tuple_map->attrMap, srcslot, dstslot);
   21431             :             }
   21432             :             else
   21433             :             {
   21434             :                 /* Copy attributes directly. */
   21435         276 :                 insertslot = dstslot;
   21436             : 
   21437         276 :                 ExecClearTuple(insertslot);
   21438             : 
   21439         276 :                 memcpy(insertslot->tts_values, srcslot->tts_values,
   21440         276 :                        sizeof(Datum) * srcslot->tts_nvalid);
   21441         276 :                 memcpy(insertslot->tts_isnull, srcslot->tts_isnull,
   21442         276 :                        sizeof(bool) * srcslot->tts_nvalid);
   21443             : 
   21444         276 :                 ExecStoreVirtualTuple(insertslot);
   21445             :             }
   21446             : 
   21447             :             /* Write the tuple out to the new relation. */
   21448         306 :             table_tuple_insert(newPartRel, insertslot, mycid,
   21449             :                                ti_options, bistate);
   21450             : 
   21451         306 :             CHECK_FOR_INTERRUPTS();
   21452             :         }
   21453             : 
   21454         174 :         table_endscan(scan);
   21455         174 :         UnregisterSnapshot(snapshot);
   21456             : 
   21457         174 :         if (tuple_map)
   21458          18 :             free_conversion_map(tuple_map);
   21459             : 
   21460         174 :         ExecDropSingleTupleTableSlot(srcslot);
   21461             :     }
   21462             : 
   21463          66 :     ExecDropSingleTupleTableSlot(dstslot);
   21464          66 :     FreeBulkInsertState(bistate);
   21465             : 
   21466          66 :     table_finish_bulk_insert(newPartRel, ti_options);
   21467          66 : }
   21468             : 
   21469             : /*
   21470             :  * ALTER TABLE <name> MERGE PARTITIONS <partition-list> INTO <partition-name>
   21471             :  */
   21472             : static void
   21473          66 : ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel,
   21474             :                       PartitionCmd *cmd, AlterTableUtilityContext *context)
   21475             : {
   21476             :     Relation    newPartRel;
   21477             :     ListCell   *listptr;
   21478          66 :     List       *mergingPartitionsList = NIL;
   21479             :     Oid         defaultPartOid;
   21480             :     char        tmpRelName[NAMEDATALEN];
   21481          66 :     RangeVar   *mergePartName = cmd->name;
   21482          66 :     bool        isSameName = false;
   21483             : 
   21484             :     /*
   21485             :      * Lock all merged partitions, check them and create list with partitions
   21486             :      * contexts.
   21487             :      */
   21488         240 :     foreach(listptr, cmd->partlist)
   21489             :     {
   21490         174 :         RangeVar   *name = (RangeVar *) lfirst(listptr);
   21491             :         Relation    mergingPartition;
   21492             : 
   21493             :         /*
   21494             :          * We are going to detach and remove this partition: need to use
   21495             :          * exclusive lock for preventing DML-queries to the partition.
   21496             :          */
   21497         174 :         mergingPartition = table_openrv(name, AccessExclusiveLock);
   21498             : 
   21499             :         /*
   21500             :          * Checking that two partitions have the same name was before, in
   21501             :          * function transformPartitionCmdForMerge().
   21502             :          */
   21503         174 :         if (equal(name, cmd->name))
   21504             :             /* One new partition can have the same name as merged partition. */
   21505           6 :             isSameName = true;
   21506             : 
   21507             :         /* Store a next merging partition into the list. */
   21508         174 :         mergingPartitionsList = lappend(mergingPartitionsList,
   21509             :                                         mergingPartition);
   21510             :     }
   21511             : 
   21512             :     /* Detach all merged partitions. */
   21513             :     defaultPartOid =
   21514          66 :         get_default_oid_from_partdesc(RelationGetPartitionDesc(rel, true));
   21515         240 :     foreach(listptr, mergingPartitionsList)
   21516             :     {
   21517         174 :         Relation    mergingPartition = (Relation) lfirst(listptr);
   21518             : 
   21519             :         /* Remove the pg_inherits row first. */
   21520         174 :         RemoveInheritance(mergingPartition, rel, false);
   21521             :         /* Do the final part of detaching. */
   21522         174 :         DetachPartitionFinalize(rel, mergingPartition, false, defaultPartOid);
   21523             :     }
   21524             : 
   21525             :     /* Create table for new partition, use partitioned table as model. */
   21526          66 :     if (isSameName)
   21527             :     {
   21528             :         /* Create partition table with generated temporary name. */
   21529           6 :         sprintf(tmpRelName, "merge-%u-%X-tmp", RelationGetRelid(rel), MyProcPid);
   21530           6 :         mergePartName = makeRangeVar(get_namespace_name(RelationGetNamespace(rel)),
   21531             :                                      tmpRelName, -1);
   21532             :     }
   21533          66 :     createPartitionTable(mergePartName,
   21534          66 :                          makeRangeVar(get_namespace_name(RelationGetNamespace(rel)),
   21535          66 :                                       RelationGetRelationName(rel), -1),
   21536             :                          context);
   21537             : 
   21538             :     /*
   21539             :      * Open the new partition and acquire exclusive lock on it.  This will
   21540             :      * stop all the operations with partitioned table.  This might seem
   21541             :      * excessive, but this is the way we make sure nobody is planning queries
   21542             :      * involving merging partitions.
   21543             :      */
   21544          66 :     newPartRel = table_openrv(mergePartName, AccessExclusiveLock);
   21545             : 
   21546             :     /* Copy data from merged partitions to new partition. */
   21547          66 :     moveMergedTablesRows(rel, mergingPartitionsList, newPartRel);
   21548             : 
   21549             :     /*
   21550             :      * Attach a new partition to the partitioned table. wqueue = NULL:
   21551             :      * verification for each cloned constraint is not need.
   21552             :      */
   21553          66 :     attachPartitionTable(NULL, rel, newPartRel, cmd->bound);
   21554             : 
   21555             :     /* Unlock and drop merged partitions. */
   21556         240 :     foreach(listptr, mergingPartitionsList)
   21557             :     {
   21558             :         ObjectAddress object;
   21559         174 :         Relation    mergingPartition = (Relation) lfirst(listptr);
   21560             : 
   21561             :         /* Get relation id before table_close() call. */
   21562         174 :         object.objectId = RelationGetRelid(mergingPartition);
   21563         174 :         object.classId = RelationRelationId;
   21564         174 :         object.objectSubId = 0;
   21565             : 
   21566             :         /* Keep the lock until commit. */
   21567         174 :         table_close(mergingPartition, NoLock);
   21568             : 
   21569         174 :         performDeletion(&object, DROP_RESTRICT, 0);
   21570             :     }
   21571          66 :     list_free(mergingPartitionsList);
   21572             : 
   21573             :     /* Rename new partition if it is needed. */
   21574          66 :     if (isSameName)
   21575             :     {
   21576             :         /*
   21577             :          * We must bump the command counter to make the new partition tuple
   21578             :          * visible for rename.
   21579             :          */
   21580           6 :         CommandCounterIncrement();
   21581             :         /* Rename partition. */
   21582           6 :         RenameRelationInternal(RelationGetRelid(newPartRel),
   21583           6 :                                cmd->name->relname, false, false);
   21584             :     }
   21585             :     /* Keep the lock until commit. */
   21586          66 :     table_close(newPartRel, NoLock);
   21587          66 : }

Generated by: LCOV version 1.14