LCOV - code coverage report
Current view: top level - src/backend/commands - tablecmds.c (source / functions) Hit Total Coverage
Test: PostgreSQL 19devel Lines: 6616 7177 92.2 %
Date: 2026-01-05 05:17:26 Functions: 219 220 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-2026, 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_largeobject_metadata.h"
      46             : #include "catalog/pg_namespace.h"
      47             : #include "catalog/pg_opclass.h"
      48             : #include "catalog/pg_policy.h"
      49             : #include "catalog/pg_proc.h"
      50             : #include "catalog/pg_publication_rel.h"
      51             : #include "catalog/pg_rewrite.h"
      52             : #include "catalog/pg_statistic_ext.h"
      53             : #include "catalog/pg_tablespace.h"
      54             : #include "catalog/pg_trigger.h"
      55             : #include "catalog/pg_type.h"
      56             : #include "catalog/storage.h"
      57             : #include "catalog/storage_xlog.h"
      58             : #include "catalog/toasting.h"
      59             : #include "commands/cluster.h"
      60             : #include "commands/comment.h"
      61             : #include "commands/defrem.h"
      62             : #include "commands/event_trigger.h"
      63             : #include "commands/sequence.h"
      64             : #include "commands/tablecmds.h"
      65             : #include "commands/tablespace.h"
      66             : #include "commands/trigger.h"
      67             : #include "commands/typecmds.h"
      68             : #include "commands/user.h"
      69             : #include "commands/vacuum.h"
      70             : #include "common/int.h"
      71             : #include "executor/executor.h"
      72             : #include "foreign/fdwapi.h"
      73             : #include "foreign/foreign.h"
      74             : #include "miscadmin.h"
      75             : #include "nodes/makefuncs.h"
      76             : #include "nodes/nodeFuncs.h"
      77             : #include "nodes/parsenodes.h"
      78             : #include "optimizer/optimizer.h"
      79             : #include "parser/parse_coerce.h"
      80             : #include "parser/parse_collate.h"
      81             : #include "parser/parse_expr.h"
      82             : #include "parser/parse_relation.h"
      83             : #include "parser/parse_type.h"
      84             : #include "parser/parse_utilcmd.h"
      85             : #include "parser/parser.h"
      86             : #include "partitioning/partbounds.h"
      87             : #include "partitioning/partdesc.h"
      88             : #include "pgstat.h"
      89             : #include "rewrite/rewriteDefine.h"
      90             : #include "rewrite/rewriteHandler.h"
      91             : #include "rewrite/rewriteManip.h"
      92             : #include "storage/bufmgr.h"
      93             : #include "storage/lmgr.h"
      94             : #include "storage/lock.h"
      95             : #include "storage/predicate.h"
      96             : #include "storage/smgr.h"
      97             : #include "tcop/utility.h"
      98             : #include "utils/acl.h"
      99             : #include "utils/builtins.h"
     100             : #include "utils/fmgroids.h"
     101             : #include "utils/inval.h"
     102             : #include "utils/lsyscache.h"
     103             : #include "utils/memutils.h"
     104             : #include "utils/partcache.h"
     105             : #include "utils/relcache.h"
     106             : #include "utils/ruleutils.h"
     107             : #include "utils/snapmgr.h"
     108             : #include "utils/syscache.h"
     109             : #include "utils/timestamp.h"
     110             : #include "utils/typcache.h"
     111             : #include "utils/usercontext.h"
     112             : 
     113             : /*
     114             :  * ON COMMIT action list
     115             :  */
     116             : typedef struct OnCommitItem
     117             : {
     118             :     Oid         relid;          /* relid of relation */
     119             :     OnCommitAction oncommit;    /* what to do at end of xact */
     120             : 
     121             :     /*
     122             :      * If this entry was created during the current transaction,
     123             :      * creating_subid is the ID of the creating subxact; if created in a prior
     124             :      * transaction, creating_subid is zero.  If deleted during the current
     125             :      * transaction, deleting_subid is the ID of the deleting subxact; if no
     126             :      * deletion request is pending, deleting_subid is zero.
     127             :      */
     128             :     SubTransactionId creating_subid;
     129             :     SubTransactionId deleting_subid;
     130             : } OnCommitItem;
     131             : 
     132             : static List *on_commits = NIL;
     133             : 
     134             : 
     135             : /*
     136             :  * State information for ALTER TABLE
     137             :  *
     138             :  * The pending-work queue for an ALTER TABLE is a List of AlteredTableInfo
     139             :  * structs, one for each table modified by the operation (the named table
     140             :  * plus any child tables that are affected).  We save lists of subcommands
     141             :  * to apply to this table (possibly modified by parse transformation steps);
     142             :  * these lists will be executed in Phase 2.  If a Phase 3 step is needed,
     143             :  * necessary information is stored in the constraints and newvals lists.
     144             :  *
     145             :  * Phase 2 is divided into multiple passes; subcommands are executed in
     146             :  * a pass determined by subcommand type.
     147             :  */
     148             : 
     149             : typedef enum AlterTablePass
     150             : {
     151             :     AT_PASS_UNSET = -1,         /* UNSET will cause ERROR */
     152             :     AT_PASS_DROP,               /* DROP (all flavors) */
     153             :     AT_PASS_ALTER_TYPE,         /* ALTER COLUMN TYPE */
     154             :     AT_PASS_ADD_COL,            /* ADD COLUMN */
     155             :     AT_PASS_SET_EXPRESSION,     /* ALTER SET EXPRESSION */
     156             :     AT_PASS_OLD_INDEX,          /* re-add existing indexes */
     157             :     AT_PASS_OLD_CONSTR,         /* re-add existing constraints */
     158             :     /* We could support a RENAME COLUMN pass here, but not currently used */
     159             :     AT_PASS_ADD_CONSTR,         /* ADD constraints (initial examination) */
     160             :     AT_PASS_COL_ATTRS,          /* set column attributes, eg NOT NULL */
     161             :     AT_PASS_ADD_INDEXCONSTR,    /* ADD index-based constraints */
     162             :     AT_PASS_ADD_INDEX,          /* ADD indexes */
     163             :     AT_PASS_ADD_OTHERCONSTR,    /* ADD other constraints, defaults */
     164             :     AT_PASS_MISC,               /* other stuff */
     165             : } AlterTablePass;
     166             : 
     167             : #define AT_NUM_PASSES           (AT_PASS_MISC + 1)
     168             : 
     169             : typedef struct AlteredTableInfo
     170             : {
     171             :     /* Information saved before any work commences: */
     172             :     Oid         relid;          /* Relation to work on */
     173             :     char        relkind;        /* Its relkind */
     174             :     TupleDesc   oldDesc;        /* Pre-modification tuple descriptor */
     175             : 
     176             :     /*
     177             :      * Transiently set during Phase 2, normally set to NULL.
     178             :      *
     179             :      * ATRewriteCatalogs sets this when it starts, and closes when ATExecCmd
     180             :      * returns control.  This can be exploited by ATExecCmd subroutines to
     181             :      * close/reopen across transaction boundaries.
     182             :      */
     183             :     Relation    rel;
     184             : 
     185             :     /* Information saved by Phase 1 for Phase 2: */
     186             :     List       *subcmds[AT_NUM_PASSES]; /* Lists of AlterTableCmd */
     187             :     /* Information saved by Phases 1/2 for Phase 3: */
     188             :     List       *constraints;    /* List of NewConstraint */
     189             :     List       *newvals;        /* List of NewColumnValue */
     190             :     List       *afterStmts;     /* List of utility command parsetrees */
     191             :     bool        verify_new_notnull; /* T if we should recheck NOT NULL */
     192             :     int         rewrite;        /* Reason for forced rewrite, if any */
     193             :     bool        chgAccessMethod;    /* T if SET ACCESS METHOD is used */
     194             :     Oid         newAccessMethod;    /* new access method; 0 means no change,
     195             :                                      * if above is true */
     196             :     Oid         newTableSpace;  /* new tablespace; 0 means no change */
     197             :     bool        chgPersistence; /* T if SET LOGGED/UNLOGGED is used */
     198             :     char        newrelpersistence;  /* if above is true */
     199             :     Expr       *partition_constraint;   /* for attach partition validation */
     200             :     /* true, if validating default due to some other attach/detach */
     201             :     bool        validate_default;
     202             :     /* Objects to rebuild after completing ALTER TYPE operations */
     203             :     List       *changedConstraintOids;  /* OIDs of constraints to rebuild */
     204             :     List       *changedConstraintDefs;  /* string definitions of same */
     205             :     List       *changedIndexOids;   /* OIDs of indexes to rebuild */
     206             :     List       *changedIndexDefs;   /* string definitions of same */
     207             :     char       *replicaIdentityIndex;   /* index to reset as REPLICA IDENTITY */
     208             :     char       *clusterOnIndex; /* index to use for CLUSTER */
     209             :     List       *changedStatisticsOids;  /* OIDs of statistics to rebuild */
     210             :     List       *changedStatisticsDefs;  /* string definitions of same */
     211             : } AlteredTableInfo;
     212             : 
     213             : /* Struct describing one new constraint to check in Phase 3 scan */
     214             : /* Note: new not-null constraints are handled elsewhere */
     215             : typedef struct NewConstraint
     216             : {
     217             :     char       *name;           /* Constraint name, or NULL if none */
     218             :     ConstrType  contype;        /* CHECK or FOREIGN */
     219             :     Oid         refrelid;       /* PK rel, if FOREIGN */
     220             :     Oid         refindid;       /* OID of PK's index, if FOREIGN */
     221             :     bool        conwithperiod;  /* Whether the new FOREIGN KEY uses PERIOD */
     222             :     Oid         conid;          /* OID of pg_constraint entry, if FOREIGN */
     223             :     Node       *qual;           /* Check expr or CONSTR_FOREIGN Constraint */
     224             :     ExprState  *qualstate;      /* Execution state for CHECK expr */
     225             : } NewConstraint;
     226             : 
     227             : /*
     228             :  * Struct describing one new column value that needs to be computed during
     229             :  * Phase 3 copy (this could be either a new column with a non-null default, or
     230             :  * a column that we're changing the type of).  Columns without such an entry
     231             :  * are just copied from the old table during ATRewriteTable.  Note that the
     232             :  * expr is an expression over *old* table values, except when is_generated
     233             :  * is true; then it is an expression over columns of the *new* tuple.
     234             :  */
     235             : typedef struct NewColumnValue
     236             : {
     237             :     AttrNumber  attnum;         /* which column */
     238             :     Expr       *expr;           /* expression to compute */
     239             :     ExprState  *exprstate;      /* execution state */
     240             :     bool        is_generated;   /* is it a GENERATED expression? */
     241             : } NewColumnValue;
     242             : 
     243             : /*
     244             :  * Error-reporting support for RemoveRelations
     245             :  */
     246             : struct dropmsgstrings
     247             : {
     248             :     char        kind;
     249             :     int         nonexistent_code;
     250             :     const char *nonexistent_msg;
     251             :     const char *skipping_msg;
     252             :     const char *nota_msg;
     253             :     const char *drophint_msg;
     254             : };
     255             : 
     256             : static const struct dropmsgstrings dropmsgstringarray[] = {
     257             :     {RELKIND_RELATION,
     258             :         ERRCODE_UNDEFINED_TABLE,
     259             :         gettext_noop("table \"%s\" does not exist"),
     260             :         gettext_noop("table \"%s\" does not exist, skipping"),
     261             :         gettext_noop("\"%s\" is not a table"),
     262             :     gettext_noop("Use DROP TABLE to remove a table.")},
     263             :     {RELKIND_SEQUENCE,
     264             :         ERRCODE_UNDEFINED_TABLE,
     265             :         gettext_noop("sequence \"%s\" does not exist"),
     266             :         gettext_noop("sequence \"%s\" does not exist, skipping"),
     267             :         gettext_noop("\"%s\" is not a sequence"),
     268             :     gettext_noop("Use DROP SEQUENCE to remove a sequence.")},
     269             :     {RELKIND_VIEW,
     270             :         ERRCODE_UNDEFINED_TABLE,
     271             :         gettext_noop("view \"%s\" does not exist"),
     272             :         gettext_noop("view \"%s\" does not exist, skipping"),
     273             :         gettext_noop("\"%s\" is not a view"),
     274             :     gettext_noop("Use DROP VIEW to remove a view.")},
     275             :     {RELKIND_MATVIEW,
     276             :         ERRCODE_UNDEFINED_TABLE,
     277             :         gettext_noop("materialized view \"%s\" does not exist"),
     278             :         gettext_noop("materialized view \"%s\" does not exist, skipping"),
     279             :         gettext_noop("\"%s\" is not a materialized view"),
     280             :     gettext_noop("Use DROP MATERIALIZED VIEW to remove a materialized view.")},
     281             :     {RELKIND_INDEX,
     282             :         ERRCODE_UNDEFINED_OBJECT,
     283             :         gettext_noop("index \"%s\" does not exist"),
     284             :         gettext_noop("index \"%s\" does not exist, skipping"),
     285             :         gettext_noop("\"%s\" is not an index"),
     286             :     gettext_noop("Use DROP INDEX to remove an index.")},
     287             :     {RELKIND_COMPOSITE_TYPE,
     288             :         ERRCODE_UNDEFINED_OBJECT,
     289             :         gettext_noop("type \"%s\" does not exist"),
     290             :         gettext_noop("type \"%s\" does not exist, skipping"),
     291             :         gettext_noop("\"%s\" is not a type"),
     292             :     gettext_noop("Use DROP TYPE to remove a type.")},
     293             :     {RELKIND_FOREIGN_TABLE,
     294             :         ERRCODE_UNDEFINED_OBJECT,
     295             :         gettext_noop("foreign table \"%s\" does not exist"),
     296             :         gettext_noop("foreign table \"%s\" does not exist, skipping"),
     297             :         gettext_noop("\"%s\" is not a foreign table"),
     298             :     gettext_noop("Use DROP FOREIGN TABLE to remove a foreign table.")},
     299             :     {RELKIND_PARTITIONED_TABLE,
     300             :         ERRCODE_UNDEFINED_TABLE,
     301             :         gettext_noop("table \"%s\" does not exist"),
     302             :         gettext_noop("table \"%s\" does not exist, skipping"),
     303             :         gettext_noop("\"%s\" is not a table"),
     304             :     gettext_noop("Use DROP TABLE to remove a table.")},
     305             :     {RELKIND_PARTITIONED_INDEX,
     306             :         ERRCODE_UNDEFINED_OBJECT,
     307             :         gettext_noop("index \"%s\" does not exist"),
     308             :         gettext_noop("index \"%s\" does not exist, skipping"),
     309             :         gettext_noop("\"%s\" is not an index"),
     310             :     gettext_noop("Use DROP INDEX to remove an index.")},
     311             :     {'\0', 0, NULL, NULL, NULL, NULL}
     312             : };
     313             : 
     314             : /* communication between RemoveRelations and RangeVarCallbackForDropRelation */
     315             : struct DropRelationCallbackState
     316             : {
     317             :     /* These fields are set by RemoveRelations: */
     318             :     char        expected_relkind;
     319             :     LOCKMODE    heap_lockmode;
     320             :     /* These fields are state to track which subsidiary locks are held: */
     321             :     Oid         heapOid;
     322             :     Oid         partParentOid;
     323             :     /* These fields are passed back by RangeVarCallbackForDropRelation: */
     324             :     char        actual_relkind;
     325             :     char        actual_relpersistence;
     326             : };
     327             : 
     328             : /* Alter table target-type flags for ATSimplePermissions */
     329             : #define     ATT_TABLE               0x0001
     330             : #define     ATT_VIEW                0x0002
     331             : #define     ATT_MATVIEW             0x0004
     332             : #define     ATT_INDEX               0x0008
     333             : #define     ATT_COMPOSITE_TYPE      0x0010
     334             : #define     ATT_FOREIGN_TABLE       0x0020
     335             : #define     ATT_PARTITIONED_INDEX   0x0040
     336             : #define     ATT_SEQUENCE            0x0080
     337             : #define     ATT_PARTITIONED_TABLE   0x0100
     338             : 
     339             : /*
     340             :  * ForeignTruncateInfo
     341             :  *
     342             :  * Information related to truncation of foreign tables.  This is used for
     343             :  * the elements in a hash table. It uses the server OID as lookup key,
     344             :  * and includes a per-server list of all foreign tables involved in the
     345             :  * truncation.
     346             :  */
     347             : typedef struct ForeignTruncateInfo
     348             : {
     349             :     Oid         serverid;
     350             :     List       *rels;
     351             : } ForeignTruncateInfo;
     352             : 
     353             : /* Partial or complete FK creation in addFkConstraint() */
     354             : typedef enum addFkConstraintSides
     355             : {
     356             :     addFkReferencedSide,
     357             :     addFkReferencingSide,
     358             :     addFkBothSides,
     359             : } addFkConstraintSides;
     360             : 
     361             : /*
     362             :  * Partition tables are expected to be dropped when the parent partitioned
     363             :  * table gets dropped. Hence for partitioning we use AUTO dependency.
     364             :  * Otherwise, for regular inheritance use NORMAL dependency.
     365             :  */
     366             : #define child_dependency_type(child_is_partition)   \
     367             :     ((child_is_partition) ? DEPENDENCY_AUTO : DEPENDENCY_NORMAL)
     368             : 
     369             : static void truncate_check_rel(Oid relid, Form_pg_class reltuple);
     370             : static void truncate_check_perms(Oid relid, Form_pg_class reltuple);
     371             : static void truncate_check_activity(Relation rel);
     372             : static void RangeVarCallbackForTruncate(const RangeVar *relation,
     373             :                                         Oid relId, Oid oldRelId, void *arg);
     374             : static List *MergeAttributes(List *columns, const List *supers, char relpersistence,
     375             :                              bool is_partition, List **supconstr,
     376             :                              List **supnotnulls);
     377             : static List *MergeCheckConstraint(List *constraints, const char *name, Node *expr, bool is_enforced);
     378             : static void MergeChildAttribute(List *inh_columns, int exist_attno, int newcol_attno, const ColumnDef *newdef);
     379             : static ColumnDef *MergeInheritedAttribute(List *inh_columns, int exist_attno, const ColumnDef *newdef);
     380             : static void MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel, bool ispartition);
     381             : static void MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel);
     382             : static void StoreCatalogInheritance(Oid relationId, List *supers,
     383             :                                     bool child_is_partition);
     384             : static void StoreCatalogInheritance1(Oid relationId, Oid parentOid,
     385             :                                      int32 seqNumber, Relation inhRelation,
     386             :                                      bool child_is_partition);
     387             : static int  findAttrByName(const char *attributeName, const List *columns);
     388             : static void AlterIndexNamespaces(Relation classRel, Relation rel,
     389             :                                  Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved);
     390             : static void AlterSeqNamespaces(Relation classRel, Relation rel,
     391             :                                Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved,
     392             :                                LOCKMODE lockmode);
     393             : static ObjectAddress ATExecAlterConstraint(List **wqueue, Relation rel,
     394             :                                            ATAlterConstraint *cmdcon,
     395             :                                            bool recurse, LOCKMODE lockmode);
     396             : static bool ATExecAlterConstraintInternal(List **wqueue, ATAlterConstraint *cmdcon, Relation conrel,
     397             :                                           Relation tgrel, Relation rel, HeapTuple contuple,
     398             :                                           bool recurse, LOCKMODE lockmode);
     399             : static bool ATExecAlterConstrEnforceability(List **wqueue, ATAlterConstraint *cmdcon,
     400             :                                             Relation conrel, Relation tgrel,
     401             :                                             Oid fkrelid, Oid pkrelid,
     402             :                                             HeapTuple contuple, LOCKMODE lockmode,
     403             :                                             Oid ReferencedParentDelTrigger,
     404             :                                             Oid ReferencedParentUpdTrigger,
     405             :                                             Oid ReferencingParentInsTrigger,
     406             :                                             Oid ReferencingParentUpdTrigger);
     407             : static bool ATExecAlterConstrDeferrability(List **wqueue, ATAlterConstraint *cmdcon,
     408             :                                            Relation conrel, Relation tgrel, Relation rel,
     409             :                                            HeapTuple contuple, bool recurse,
     410             :                                            List **otherrelids, LOCKMODE lockmode);
     411             : static bool ATExecAlterConstrInheritability(List **wqueue, ATAlterConstraint *cmdcon,
     412             :                                             Relation conrel, Relation rel,
     413             :                                             HeapTuple contuple, LOCKMODE lockmode);
     414             : static void AlterConstrTriggerDeferrability(Oid conoid, Relation tgrel, Relation rel,
     415             :                                             bool deferrable, bool initdeferred,
     416             :                                             List **otherrelids);
     417             : static void AlterConstrEnforceabilityRecurse(List **wqueue, ATAlterConstraint *cmdcon,
     418             :                                              Relation conrel, Relation tgrel,
     419             :                                              Oid fkrelid, Oid pkrelid,
     420             :                                              HeapTuple contuple, LOCKMODE lockmode,
     421             :                                              Oid ReferencedParentDelTrigger,
     422             :                                              Oid ReferencedParentUpdTrigger,
     423             :                                              Oid ReferencingParentInsTrigger,
     424             :                                              Oid ReferencingParentUpdTrigger);
     425             : static void AlterConstrDeferrabilityRecurse(List **wqueue, ATAlterConstraint *cmdcon,
     426             :                                             Relation conrel, Relation tgrel, Relation rel,
     427             :                                             HeapTuple contuple, bool recurse,
     428             :                                             List **otherrelids, LOCKMODE lockmode);
     429             : static void AlterConstrUpdateConstraintEntry(ATAlterConstraint *cmdcon, Relation conrel,
     430             :                                              HeapTuple contuple);
     431             : static ObjectAddress ATExecValidateConstraint(List **wqueue,
     432             :                                               Relation rel, char *constrName,
     433             :                                               bool recurse, bool recursing, LOCKMODE lockmode);
     434             : static void QueueFKConstraintValidation(List **wqueue, Relation conrel, Relation fkrel,
     435             :                                         Oid pkrelid, HeapTuple contuple, LOCKMODE lockmode);
     436             : static void QueueCheckConstraintValidation(List **wqueue, Relation conrel, Relation rel,
     437             :                                            char *constrName, HeapTuple contuple,
     438             :                                            bool recurse, bool recursing, LOCKMODE lockmode);
     439             : static void QueueNNConstraintValidation(List **wqueue, Relation conrel, Relation rel,
     440             :                                         HeapTuple contuple, bool recurse, bool recursing,
     441             :                                         LOCKMODE lockmode);
     442             : static int  transformColumnNameList(Oid relId, List *colList,
     443             :                                     int16 *attnums, Oid *atttypids, Oid *attcollids);
     444             : static int  transformFkeyGetPrimaryKey(Relation pkrel, Oid *indexOid,
     445             :                                        List **attnamelist,
     446             :                                        int16 *attnums, Oid *atttypids, Oid *attcollids,
     447             :                                        Oid *opclasses, bool *pk_has_without_overlaps);
     448             : static Oid  transformFkeyCheckAttrs(Relation pkrel,
     449             :                                     int numattrs, int16 *attnums,
     450             :                                     bool with_period, Oid *opclasses,
     451             :                                     bool *pk_has_without_overlaps);
     452             : static void checkFkeyPermissions(Relation rel, int16 *attnums, int natts);
     453             : static CoercionPathType findFkeyCast(Oid targetTypeId, Oid sourceTypeId,
     454             :                                      Oid *funcid);
     455             : static void validateForeignKeyConstraint(char *conname,
     456             :                                          Relation rel, Relation pkrel,
     457             :                                          Oid pkindOid, Oid constraintOid, bool hasperiod);
     458             : static void CheckAlterTableIsSafe(Relation rel);
     459             : static void ATController(AlterTableStmt *parsetree,
     460             :                          Relation rel, List *cmds, bool recurse, LOCKMODE lockmode,
     461             :                          AlterTableUtilityContext *context);
     462             : static void ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
     463             :                       bool recurse, bool recursing, LOCKMODE lockmode,
     464             :                       AlterTableUtilityContext *context);
     465             : static void ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode,
     466             :                               AlterTableUtilityContext *context);
     467             : static void ATExecCmd(List **wqueue, AlteredTableInfo *tab,
     468             :                       AlterTableCmd *cmd, LOCKMODE lockmode, AlterTablePass cur_pass,
     469             :                       AlterTableUtilityContext *context);
     470             : static AlterTableCmd *ATParseTransformCmd(List **wqueue, AlteredTableInfo *tab,
     471             :                                           Relation rel, AlterTableCmd *cmd,
     472             :                                           bool recurse, LOCKMODE lockmode,
     473             :                                           AlterTablePass cur_pass,
     474             :                                           AlterTableUtilityContext *context);
     475             : static void ATRewriteTables(AlterTableStmt *parsetree,
     476             :                             List **wqueue, LOCKMODE lockmode,
     477             :                             AlterTableUtilityContext *context);
     478             : static void ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap);
     479             : static AlteredTableInfo *ATGetQueueEntry(List **wqueue, Relation rel);
     480             : static void ATSimplePermissions(AlterTableType cmdtype, Relation rel, int allowed_targets);
     481             : static void ATSimpleRecursion(List **wqueue, Relation rel,
     482             :                               AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode,
     483             :                               AlterTableUtilityContext *context);
     484             : static void ATCheckPartitionsNotInUse(Relation rel, LOCKMODE lockmode);
     485             : static void ATTypedTableRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd,
     486             :                                   LOCKMODE lockmode,
     487             :                                   AlterTableUtilityContext *context);
     488             : static List *find_typed_table_dependencies(Oid typeOid, const char *typeName,
     489             :                                            DropBehavior behavior);
     490             : static void ATPrepAddColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
     491             :                             bool is_view, AlterTableCmd *cmd, LOCKMODE lockmode,
     492             :                             AlterTableUtilityContext *context);
     493             : static ObjectAddress ATExecAddColumn(List **wqueue, AlteredTableInfo *tab,
     494             :                                      Relation rel, AlterTableCmd **cmd,
     495             :                                      bool recurse, bool recursing,
     496             :                                      LOCKMODE lockmode, AlterTablePass cur_pass,
     497             :                                      AlterTableUtilityContext *context);
     498             : static bool check_for_column_name_collision(Relation rel, const char *colname,
     499             :                                             bool if_not_exists);
     500             : static void add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid);
     501             : static void add_column_collation_dependency(Oid relid, int32 attnum, Oid collid);
     502             : static ObjectAddress ATExecDropNotNull(Relation rel, const char *colName, bool recurse,
     503             :                                        LOCKMODE lockmode);
     504             : static void set_attnotnull(List **wqueue, Relation rel, AttrNumber attnum,
     505             :                            bool is_valid, bool queue_validation);
     506             : static ObjectAddress ATExecSetNotNull(List **wqueue, Relation rel,
     507             :                                       char *conName, char *colName,
     508             :                                       bool recurse, bool recursing,
     509             :                                       LOCKMODE lockmode);
     510             : static bool NotNullImpliedByRelConstraints(Relation rel, Form_pg_attribute attr);
     511             : static bool ConstraintImpliedByRelConstraint(Relation scanrel,
     512             :                                              List *testConstraint, List *provenConstraint);
     513             : static ObjectAddress ATExecColumnDefault(Relation rel, const char *colName,
     514             :                                          Node *newDefault, LOCKMODE lockmode);
     515             : static ObjectAddress ATExecCookedColumnDefault(Relation rel, AttrNumber attnum,
     516             :                                                Node *newDefault);
     517             : static ObjectAddress ATExecAddIdentity(Relation rel, const char *colName,
     518             :                                        Node *def, LOCKMODE lockmode, bool recurse, bool recursing);
     519             : static ObjectAddress ATExecSetIdentity(Relation rel, const char *colName,
     520             :                                        Node *def, LOCKMODE lockmode, bool recurse, bool recursing);
     521             : static ObjectAddress ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode,
     522             :                                         bool recurse, bool recursing);
     523             : static ObjectAddress ATExecSetExpression(AlteredTableInfo *tab, Relation rel, const char *colName,
     524             :                                          Node *newExpr, LOCKMODE lockmode);
     525             : static void ATPrepDropExpression(Relation rel, AlterTableCmd *cmd, bool recurse, bool recursing, LOCKMODE lockmode);
     526             : static ObjectAddress ATExecDropExpression(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode);
     527             : static ObjectAddress ATExecSetStatistics(Relation rel, const char *colName, int16 colNum,
     528             :                                          Node *newValue, LOCKMODE lockmode);
     529             : static ObjectAddress ATExecSetOptions(Relation rel, const char *colName,
     530             :                                       Node *options, bool isReset, LOCKMODE lockmode);
     531             : static ObjectAddress ATExecSetStorage(Relation rel, const char *colName,
     532             :                                       Node *newValue, LOCKMODE lockmode);
     533             : static void ATPrepDropColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
     534             :                              AlterTableCmd *cmd, LOCKMODE lockmode,
     535             :                              AlterTableUtilityContext *context);
     536             : static ObjectAddress ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
     537             :                                       DropBehavior behavior,
     538             :                                       bool recurse, bool recursing,
     539             :                                       bool missing_ok, LOCKMODE lockmode,
     540             :                                       ObjectAddresses *addrs);
     541             : static void ATPrepAddPrimaryKey(List **wqueue, Relation rel, AlterTableCmd *cmd,
     542             :                                 bool recurse, LOCKMODE lockmode,
     543             :                                 AlterTableUtilityContext *context);
     544             : static void verifyNotNullPKCompatible(HeapTuple tuple, const char *colname);
     545             : static ObjectAddress ATExecAddIndex(AlteredTableInfo *tab, Relation rel,
     546             :                                     IndexStmt *stmt, bool is_rebuild, LOCKMODE lockmode);
     547             : static ObjectAddress ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
     548             :                                          CreateStatsStmt *stmt, bool is_rebuild, LOCKMODE lockmode);
     549             : static ObjectAddress ATExecAddConstraint(List **wqueue,
     550             :                                          AlteredTableInfo *tab, Relation rel,
     551             :                                          Constraint *newConstraint, bool recurse, bool is_readd,
     552             :                                          LOCKMODE lockmode);
     553             : static char *ChooseForeignKeyConstraintNameAddition(List *colnames);
     554             : static ObjectAddress ATExecAddIndexConstraint(AlteredTableInfo *tab, Relation rel,
     555             :                                               IndexStmt *stmt, LOCKMODE lockmode);
     556             : static ObjectAddress ATAddCheckNNConstraint(List **wqueue,
     557             :                                             AlteredTableInfo *tab, Relation rel,
     558             :                                             Constraint *constr,
     559             :                                             bool recurse, bool recursing, bool is_readd,
     560             :                                             LOCKMODE lockmode);
     561             : static ObjectAddress ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab,
     562             :                                                Relation rel, Constraint *fkconstraint,
     563             :                                                bool recurse, bool recursing,
     564             :                                                LOCKMODE lockmode);
     565             : static int  validateFkOnDeleteSetColumns(int numfks, const int16 *fkattnums,
     566             :                                          int numfksetcols, int16 *fksetcolsattnums,
     567             :                                          List *fksetcols);
     568             : static ObjectAddress addFkConstraint(addFkConstraintSides fkside,
     569             :                                      char *constraintname,
     570             :                                      Constraint *fkconstraint, Relation rel,
     571             :                                      Relation pkrel, Oid indexOid,
     572             :                                      Oid parentConstr,
     573             :                                      int numfks, int16 *pkattnum, int16 *fkattnum,
     574             :                                      Oid *pfeqoperators, Oid *ppeqoperators,
     575             :                                      Oid *ffeqoperators, int numfkdelsetcols,
     576             :                                      int16 *fkdelsetcols, bool is_internal,
     577             :                                      bool with_period);
     578             : static void addFkRecurseReferenced(Constraint *fkconstraint,
     579             :                                    Relation rel, Relation pkrel, Oid indexOid, Oid parentConstr,
     580             :                                    int numfks, int16 *pkattnum, int16 *fkattnum,
     581             :                                    Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators,
     582             :                                    int numfkdelsetcols, int16 *fkdelsetcols,
     583             :                                    bool old_check_ok,
     584             :                                    Oid parentDelTrigger, Oid parentUpdTrigger,
     585             :                                    bool with_period);
     586             : static void addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint,
     587             :                                     Relation rel, Relation pkrel, Oid indexOid, Oid parentConstr,
     588             :                                     int numfks, int16 *pkattnum, int16 *fkattnum,
     589             :                                     Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators,
     590             :                                     int numfkdelsetcols, int16 *fkdelsetcols,
     591             :                                     bool old_check_ok, LOCKMODE lockmode,
     592             :                                     Oid parentInsTrigger, Oid parentUpdTrigger,
     593             :                                     bool with_period);
     594             : static void CloneForeignKeyConstraints(List **wqueue, Relation parentRel,
     595             :                                        Relation partitionRel);
     596             : static void CloneFkReferenced(Relation parentRel, Relation partitionRel);
     597             : static void CloneFkReferencing(List **wqueue, Relation parentRel,
     598             :                                Relation partRel);
     599             : static void createForeignKeyCheckTriggers(Oid myRelOid, Oid refRelOid,
     600             :                                           Constraint *fkconstraint, Oid constraintOid,
     601             :                                           Oid indexOid,
     602             :                                           Oid parentInsTrigger, Oid parentUpdTrigger,
     603             :                                           Oid *insertTrigOid, Oid *updateTrigOid);
     604             : static void createForeignKeyActionTriggers(Oid myRelOid, Oid refRelOid,
     605             :                                            Constraint *fkconstraint, Oid constraintOid,
     606             :                                            Oid indexOid,
     607             :                                            Oid parentDelTrigger, Oid parentUpdTrigger,
     608             :                                            Oid *deleteTrigOid, Oid *updateTrigOid);
     609             : static bool tryAttachPartitionForeignKey(List **wqueue,
     610             :                                          ForeignKeyCacheInfo *fk,
     611             :                                          Relation partition,
     612             :                                          Oid parentConstrOid, int numfks,
     613             :                                          AttrNumber *mapped_conkey, AttrNumber *confkey,
     614             :                                          Oid *conpfeqop,
     615             :                                          Oid parentInsTrigger,
     616             :                                          Oid parentUpdTrigger,
     617             :                                          Relation trigrel);
     618             : static void AttachPartitionForeignKey(List **wqueue, Relation partition,
     619             :                                       Oid partConstrOid, Oid parentConstrOid,
     620             :                                       Oid parentInsTrigger, Oid parentUpdTrigger,
     621             :                                       Relation trigrel);
     622             : static void RemoveInheritedConstraint(Relation conrel, Relation trigrel,
     623             :                                       Oid conoid, Oid conrelid);
     624             : static void DropForeignKeyConstraintTriggers(Relation trigrel, Oid conoid,
     625             :                                              Oid confrelid, Oid conrelid);
     626             : static void GetForeignKeyActionTriggers(Relation trigrel,
     627             :                                         Oid conoid, Oid confrelid, Oid conrelid,
     628             :                                         Oid *deleteTriggerOid,
     629             :                                         Oid *updateTriggerOid);
     630             : static void GetForeignKeyCheckTriggers(Relation trigrel,
     631             :                                        Oid conoid, Oid confrelid, Oid conrelid,
     632             :                                        Oid *insertTriggerOid,
     633             :                                        Oid *updateTriggerOid);
     634             : static void ATExecDropConstraint(Relation rel, const char *constrName,
     635             :                                  DropBehavior behavior, bool recurse,
     636             :                                  bool missing_ok, LOCKMODE lockmode);
     637             : static ObjectAddress dropconstraint_internal(Relation rel,
     638             :                                              HeapTuple constraintTup, DropBehavior behavior,
     639             :                                              bool recurse, bool recursing,
     640             :                                              bool missing_ok, LOCKMODE lockmode);
     641             : static void ATPrepAlterColumnType(List **wqueue,
     642             :                                   AlteredTableInfo *tab, Relation rel,
     643             :                                   bool recurse, bool recursing,
     644             :                                   AlterTableCmd *cmd, LOCKMODE lockmode,
     645             :                                   AlterTableUtilityContext *context);
     646             : static bool ATColumnChangeRequiresRewrite(Node *expr, AttrNumber varattno);
     647             : static ObjectAddress ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
     648             :                                            AlterTableCmd *cmd, LOCKMODE lockmode);
     649             : static void RememberAllDependentForRebuilding(AlteredTableInfo *tab, AlterTableType subtype,
     650             :                                               Relation rel, AttrNumber attnum, const char *colName);
     651             : static void RememberConstraintForRebuilding(Oid conoid, AlteredTableInfo *tab);
     652             : static void RememberIndexForRebuilding(Oid indoid, AlteredTableInfo *tab);
     653             : static void RememberStatisticsForRebuilding(Oid stxoid, AlteredTableInfo *tab);
     654             : static void ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab,
     655             :                                    LOCKMODE lockmode);
     656             : static void ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId,
     657             :                                  char *cmd, List **wqueue, LOCKMODE lockmode,
     658             :                                  bool rewrite);
     659             : static void RebuildConstraintComment(AlteredTableInfo *tab, AlterTablePass pass,
     660             :                                      Oid objid, Relation rel, List *domname,
     661             :                                      const char *conname);
     662             : static void TryReuseIndex(Oid oldId, IndexStmt *stmt);
     663             : static void TryReuseForeignKey(Oid oldId, Constraint *con);
     664             : static ObjectAddress ATExecAlterColumnGenericOptions(Relation rel, const char *colName,
     665             :                                                      List *options, LOCKMODE lockmode);
     666             : static void change_owner_fix_column_acls(Oid relationOid,
     667             :                                          Oid oldOwnerId, Oid newOwnerId);
     668             : static void change_owner_recurse_to_sequences(Oid relationOid,
     669             :                                               Oid newOwnerId, LOCKMODE lockmode);
     670             : static ObjectAddress ATExecClusterOn(Relation rel, const char *indexName,
     671             :                                      LOCKMODE lockmode);
     672             : static void ATExecDropCluster(Relation rel, LOCKMODE lockmode);
     673             : static void ATPrepSetAccessMethod(AlteredTableInfo *tab, Relation rel, const char *amname);
     674             : static void ATExecSetAccessMethodNoStorage(Relation rel, Oid newAccessMethodId);
     675             : static void ATPrepChangePersistence(AlteredTableInfo *tab, Relation rel,
     676             :                                     bool toLogged);
     677             : static void ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel,
     678             :                                 const char *tablespacename, LOCKMODE lockmode);
     679             : static void ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode);
     680             : static void ATExecSetTableSpaceNoStorage(Relation rel, Oid newTableSpace);
     681             : static void ATExecSetRelOptions(Relation rel, List *defList,
     682             :                                 AlterTableType operation,
     683             :                                 LOCKMODE lockmode);
     684             : static void ATExecEnableDisableTrigger(Relation rel, const char *trigname,
     685             :                                        char fires_when, bool skip_system, bool recurse,
     686             :                                        LOCKMODE lockmode);
     687             : static void ATExecEnableDisableRule(Relation rel, const char *rulename,
     688             :                                     char fires_when, LOCKMODE lockmode);
     689             : static void ATPrepAddInherit(Relation child_rel);
     690             : static ObjectAddress ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode);
     691             : static ObjectAddress ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode);
     692             : static void drop_parent_dependency(Oid relid, Oid refclassid, Oid refobjid,
     693             :                                    DependencyType deptype);
     694             : static ObjectAddress ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode);
     695             : static void ATExecDropOf(Relation rel, LOCKMODE lockmode);
     696             : static void ATExecReplicaIdentity(Relation rel, ReplicaIdentityStmt *stmt, LOCKMODE lockmode);
     697             : static void ATExecGenericOptions(Relation rel, List *options);
     698             : static void ATExecSetRowSecurity(Relation rel, bool rls);
     699             : static void ATExecForceNoForceRowSecurity(Relation rel, bool force_rls);
     700             : static ObjectAddress ATExecSetCompression(Relation rel,
     701             :                                           const char *column, Node *newValue, LOCKMODE lockmode);
     702             : 
     703             : static void index_copy_data(Relation rel, RelFileLocator newrlocator);
     704             : static const char *storage_name(char c);
     705             : 
     706             : static void RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid,
     707             :                                             Oid oldRelOid, void *arg);
     708             : static void RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid,
     709             :                                              Oid oldrelid, void *arg);
     710             : static PartitionSpec *transformPartitionSpec(Relation rel, PartitionSpec *partspec);
     711             : static void ComputePartitionAttrs(ParseState *pstate, Relation rel, List *partParams, AttrNumber *partattrs,
     712             :                                   List **partexprs, Oid *partopclass, Oid *partcollation,
     713             :                                   PartitionStrategy strategy);
     714             : static void CreateInheritance(Relation child_rel, Relation parent_rel, bool ispartition);
     715             : static void RemoveInheritance(Relation child_rel, Relation parent_rel,
     716             :                               bool expect_detached);
     717             : static ObjectAddress ATExecAttachPartition(List **wqueue, Relation rel,
     718             :                                            PartitionCmd *cmd,
     719             :                                            AlterTableUtilityContext *context);
     720             : static void AttachPartitionEnsureIndexes(List **wqueue, Relation rel, Relation attachrel);
     721             : static void QueuePartitionConstraintValidation(List **wqueue, Relation scanrel,
     722             :                                                List *partConstraint,
     723             :                                                bool validate_default);
     724             : static void CloneRowTriggersToPartition(Relation parent, Relation partition);
     725             : static void DropClonedTriggersFromPartition(Oid partitionId);
     726             : static ObjectAddress ATExecDetachPartition(List **wqueue, AlteredTableInfo *tab,
     727             :                                            Relation rel, RangeVar *name,
     728             :                                            bool concurrent);
     729             : static void DetachPartitionFinalize(Relation rel, Relation partRel,
     730             :                                     bool concurrent, Oid defaultPartOid);
     731             : static ObjectAddress ATExecDetachPartitionFinalize(Relation rel, RangeVar *name);
     732             : static ObjectAddress ATExecAttachPartitionIdx(List **wqueue, Relation parentIdx,
     733             :                                               RangeVar *name);
     734             : static void validatePartitionedIndex(Relation partedIdx, Relation partedTbl);
     735             : static void refuseDupeIndexAttach(Relation parentIdx, Relation partIdx,
     736             :                                   Relation partitionTbl);
     737             : static void verifyPartitionIndexNotNull(IndexInfo *iinfo, Relation partition);
     738             : static List *GetParentedForeignKeyRefs(Relation partition);
     739             : static void ATDetachCheckNoForeignKeyRefs(Relation partition);
     740             : static char GetAttributeCompression(Oid atttypid, const char *compression);
     741             : static char GetAttributeStorage(Oid atttypid, const char *storagemode);
     742             : 
     743             : static void ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel,
     744             :                                   PartitionCmd *cmd, AlterTableUtilityContext *context);
     745             : static void ATExecSplitPartition(List **wqueue, AlteredTableInfo *tab,
     746             :                                  Relation rel, PartitionCmd *cmd,
     747             :                                  AlterTableUtilityContext *context);
     748             : 
     749             : /* ----------------------------------------------------------------
     750             :  *      DefineRelation
     751             :  *              Creates a new relation.
     752             :  *
     753             :  * stmt carries parsetree information from an ordinary CREATE TABLE statement.
     754             :  * The other arguments are used to extend the behavior for other cases:
     755             :  * relkind: relkind to assign to the new relation
     756             :  * ownerId: if not InvalidOid, use this as the new relation's owner.
     757             :  * typaddress: if not null, it's set to the pg_type entry's address.
     758             :  * queryString: for error reporting
     759             :  *
     760             :  * Note that permissions checks are done against current user regardless of
     761             :  * ownerId.  A nonzero ownerId is used when someone is creating a relation
     762             :  * "on behalf of" someone else, so we still want to see that the current user
     763             :  * has permissions to do it.
     764             :  *
     765             :  * If successful, returns the address of the new relation.
     766             :  * ----------------------------------------------------------------
     767             :  */
     768             : ObjectAddress
     769       65426 : DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
     770             :                ObjectAddress *typaddress, const char *queryString)
     771             : {
     772             :     char        relname[NAMEDATALEN];
     773             :     Oid         namespaceId;
     774             :     Oid         relationId;
     775             :     Oid         tablespaceId;
     776             :     Relation    rel;
     777             :     TupleDesc   descriptor;
     778             :     List       *inheritOids;
     779             :     List       *old_constraints;
     780             :     List       *old_notnulls;
     781             :     List       *rawDefaults;
     782             :     List       *cookedDefaults;
     783             :     List       *nncols;
     784             :     Datum       reloptions;
     785             :     ListCell   *listptr;
     786             :     AttrNumber  attnum;
     787             :     bool        partitioned;
     788       65426 :     const char *const validnsps[] = HEAP_RELOPT_NAMESPACES;
     789             :     Oid         ofTypeId;
     790             :     ObjectAddress address;
     791             :     LOCKMODE    parentLockmode;
     792       65426 :     Oid         accessMethodId = InvalidOid;
     793             : 
     794             :     /*
     795             :      * Truncate relname to appropriate length (probably a waste of time, as
     796             :      * parser should have done this already).
     797             :      */
     798       65426 :     strlcpy(relname, stmt->relation->relname, NAMEDATALEN);
     799             : 
     800             :     /*
     801             :      * Check consistency of arguments
     802             :      */
     803       65426 :     if (stmt->oncommit != ONCOMMIT_NOOP
     804         194 :         && stmt->relation->relpersistence != RELPERSISTENCE_TEMP)
     805          12 :         ereport(ERROR,
     806             :                 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
     807             :                  errmsg("ON COMMIT can only be used on temporary tables")));
     808             : 
     809       65414 :     if (stmt->partspec != NULL)
     810             :     {
     811        5574 :         if (relkind != RELKIND_RELATION)
     812           0 :             elog(ERROR, "unexpected relkind: %d", (int) relkind);
     813             : 
     814        5574 :         relkind = RELKIND_PARTITIONED_TABLE;
     815        5574 :         partitioned = true;
     816             :     }
     817             :     else
     818       59840 :         partitioned = false;
     819             : 
     820       65414 :     if (relkind == RELKIND_PARTITIONED_TABLE &&
     821        5574 :         stmt->relation->relpersistence == RELPERSISTENCE_UNLOGGED)
     822           6 :         ereport(ERROR,
     823             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     824             :                  errmsg("partitioned tables cannot be unlogged")));
     825             : 
     826             :     /*
     827             :      * Look up the namespace in which we are supposed to create the relation,
     828             :      * check we have permission to create there, lock it against concurrent
     829             :      * drop, and mark stmt->relation as RELPERSISTENCE_TEMP if a temporary
     830             :      * namespace is selected.
     831             :      */
     832             :     namespaceId =
     833       65408 :         RangeVarGetAndCheckCreationNamespace(stmt->relation, NoLock, NULL);
     834             : 
     835             :     /*
     836             :      * Security check: disallow creating temp tables from security-restricted
     837             :      * code.  This is needed because calling code might not expect untrusted
     838             :      * tables to appear in pg_temp at the front of its search path.
     839             :      */
     840       65408 :     if (stmt->relation->relpersistence == RELPERSISTENCE_TEMP
     841        3370 :         && InSecurityRestrictedOperation())
     842           0 :         ereport(ERROR,
     843             :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
     844             :                  errmsg("cannot create temporary table within security-restricted operation")));
     845             : 
     846             :     /*
     847             :      * Determine the lockmode to use when scanning parents.  A self-exclusive
     848             :      * lock is needed here.
     849             :      *
     850             :      * For regular inheritance, if two backends attempt to add children to the
     851             :      * same parent simultaneously, and that parent has no pre-existing
     852             :      * children, then both will attempt to update the parent's relhassubclass
     853             :      * field, leading to a "tuple concurrently updated" error.  Also, this
     854             :      * interlocks against a concurrent ANALYZE on the parent table, which
     855             :      * might otherwise be attempting to clear the parent's relhassubclass
     856             :      * field, if its previous children were recently dropped.
     857             :      *
     858             :      * If the child table is a partition, then we instead grab an exclusive
     859             :      * lock on the parent because its partition descriptor will be changed by
     860             :      * addition of the new partition.
     861             :      */
     862       65408 :     parentLockmode = (stmt->partbound != NULL ? AccessExclusiveLock :
     863             :                       ShareUpdateExclusiveLock);
     864             : 
     865             :     /* Determine the list of OIDs of the parents. */
     866       65408 :     inheritOids = NIL;
     867       77066 :     foreach(listptr, stmt->inhRelations)
     868             :     {
     869       11658 :         RangeVar   *rv = (RangeVar *) lfirst(listptr);
     870             :         Oid         parentOid;
     871             : 
     872       11658 :         parentOid = RangeVarGetRelid(rv, parentLockmode, false);
     873             : 
     874             :         /*
     875             :          * Reject duplications in the list of parents.
     876             :          */
     877       11658 :         if (list_member_oid(inheritOids, parentOid))
     878           0 :             ereport(ERROR,
     879             :                     (errcode(ERRCODE_DUPLICATE_TABLE),
     880             :                      errmsg("relation \"%s\" would be inherited from more than once",
     881             :                             get_rel_name(parentOid))));
     882             : 
     883       11658 :         inheritOids = lappend_oid(inheritOids, parentOid);
     884             :     }
     885             : 
     886             :     /*
     887             :      * Select tablespace to use: an explicitly indicated one, or (in the case
     888             :      * of a partitioned table) the parent's, if it has one.
     889             :      */
     890       65408 :     if (stmt->tablespacename)
     891             :     {
     892         140 :         tablespaceId = get_tablespace_oid(stmt->tablespacename, false);
     893             : 
     894         134 :         if (partitioned && tablespaceId == MyDatabaseTableSpace)
     895           6 :             ereport(ERROR,
     896             :                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     897             :                      errmsg("cannot specify default tablespace for partitioned relations")));
     898             :     }
     899       65268 :     else if (stmt->partbound)
     900             :     {
     901             :         Assert(list_length(inheritOids) == 1);
     902        9132 :         tablespaceId = get_rel_tablespace(linitial_oid(inheritOids));
     903             :     }
     904             :     else
     905       56136 :         tablespaceId = InvalidOid;
     906             : 
     907             :     /* still nothing? use the default */
     908       65396 :     if (!OidIsValid(tablespaceId))
     909       65228 :         tablespaceId = GetDefaultTablespace(stmt->relation->relpersistence,
     910             :                                             partitioned);
     911             : 
     912             :     /* Check permissions except when using database's default */
     913       65390 :     if (OidIsValid(tablespaceId) && tablespaceId != MyDatabaseTableSpace)
     914             :     {
     915             :         AclResult   aclresult;
     916             : 
     917         194 :         aclresult = object_aclcheck(TableSpaceRelationId, tablespaceId, GetUserId(),
     918             :                                     ACL_CREATE);
     919         194 :         if (aclresult != ACLCHECK_OK)
     920           6 :             aclcheck_error(aclresult, OBJECT_TABLESPACE,
     921           6 :                            get_tablespace_name(tablespaceId));
     922             :     }
     923             : 
     924             :     /* In all cases disallow placing user relations in pg_global */
     925       65384 :     if (tablespaceId == GLOBALTABLESPACE_OID)
     926          18 :         ereport(ERROR,
     927             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     928             :                  errmsg("only shared relations can be placed in pg_global tablespace")));
     929             : 
     930             :     /* Identify user ID that will own the table */
     931       65366 :     if (!OidIsValid(ownerId))
     932       65120 :         ownerId = GetUserId();
     933             : 
     934             :     /*
     935             :      * Parse and validate reloptions, if any.
     936             :      */
     937       65366 :     reloptions = transformRelOptions((Datum) 0, stmt->options, NULL, validnsps,
     938             :                                      true, false);
     939             : 
     940       65348 :     switch (relkind)
     941             :     {
     942       17102 :         case RELKIND_VIEW:
     943       17102 :             (void) view_reloptions(reloptions, true);
     944       17084 :             break;
     945        5550 :         case RELKIND_PARTITIONED_TABLE:
     946        5550 :             (void) partitioned_table_reloptions(reloptions, true);
     947        5544 :             break;
     948       42696 :         default:
     949       42696 :             (void) heap_reloptions(relkind, reloptions, true);
     950             :     }
     951             : 
     952       65228 :     if (stmt->ofTypename)
     953             :     {
     954             :         AclResult   aclresult;
     955             : 
     956          86 :         ofTypeId = typenameTypeId(NULL, stmt->ofTypename);
     957             : 
     958          86 :         aclresult = object_aclcheck(TypeRelationId, ofTypeId, GetUserId(), ACL_USAGE);
     959          86 :         if (aclresult != ACLCHECK_OK)
     960           6 :             aclcheck_error_type(aclresult, ofTypeId);
     961             :     }
     962             :     else
     963       65142 :         ofTypeId = InvalidOid;
     964             : 
     965             :     /*
     966             :      * Look up inheritance ancestors and generate relation schema, including
     967             :      * inherited attributes.  (Note that stmt->tableElts is destructively
     968             :      * modified by MergeAttributes.)
     969             :      */
     970       64982 :     stmt->tableElts =
     971       65222 :         MergeAttributes(stmt->tableElts, inheritOids,
     972       65222 :                         stmt->relation->relpersistence,
     973       65222 :                         stmt->partbound != NULL,
     974             :                         &old_constraints, &old_notnulls);
     975             : 
     976             :     /*
     977             :      * Create a tuple descriptor from the relation schema.  Note that this
     978             :      * deals with column names, types, and in-descriptor NOT NULL flags, but
     979             :      * not default values, NOT NULL or CHECK constraints; we handle those
     980             :      * below.
     981             :      */
     982       64982 :     descriptor = BuildDescForRelation(stmt->tableElts);
     983             : 
     984             :     /*
     985             :      * Find columns with default values and prepare for insertion of the
     986             :      * defaults.  Pre-cooked (that is, inherited) defaults go into a list of
     987             :      * CookedConstraint structs that we'll pass to heap_create_with_catalog,
     988             :      * while raw defaults go into a list of RawColumnDefault structs that will
     989             :      * be processed by AddRelationNewConstraints.  (We can't deal with raw
     990             :      * expressions until we can do transformExpr.)
     991             :      */
     992       64934 :     rawDefaults = NIL;
     993       64934 :     cookedDefaults = NIL;
     994       64934 :     attnum = 0;
     995             : 
     996      329720 :     foreach(listptr, stmt->tableElts)
     997             :     {
     998      264786 :         ColumnDef  *colDef = lfirst(listptr);
     999             : 
    1000      264786 :         attnum++;
    1001      264786 :         if (colDef->raw_default != NULL)
    1002             :         {
    1003             :             RawColumnDefault *rawEnt;
    1004             : 
    1005             :             Assert(colDef->cooked_default == NULL);
    1006             : 
    1007        3400 :             rawEnt = palloc_object(RawColumnDefault);
    1008        3400 :             rawEnt->attnum = attnum;
    1009        3400 :             rawEnt->raw_default = colDef->raw_default;
    1010        3400 :             rawEnt->generated = colDef->generated;
    1011        3400 :             rawDefaults = lappend(rawDefaults, rawEnt);
    1012             :         }
    1013      261386 :         else if (colDef->cooked_default != NULL)
    1014             :         {
    1015             :             CookedConstraint *cooked;
    1016             : 
    1017         540 :             cooked = palloc_object(CookedConstraint);
    1018         540 :             cooked->contype = CONSTR_DEFAULT;
    1019         540 :             cooked->conoid = InvalidOid; /* until created */
    1020         540 :             cooked->name = NULL;
    1021         540 :             cooked->attnum = attnum;
    1022         540 :             cooked->expr = colDef->cooked_default;
    1023         540 :             cooked->is_enforced = true;
    1024         540 :             cooked->skip_validation = false;
    1025         540 :             cooked->is_local = true; /* not used for defaults */
    1026         540 :             cooked->inhcount = 0;    /* ditto */
    1027         540 :             cooked->is_no_inherit = false;
    1028         540 :             cookedDefaults = lappend(cookedDefaults, cooked);
    1029             :         }
    1030             :     }
    1031             : 
    1032             :     /*
    1033             :      * For relations with table AM and partitioned tables, select access
    1034             :      * method to use: an explicitly indicated one, or (in the case of a
    1035             :      * partitioned table) the parent's, if it has one.
    1036             :      */
    1037       64934 :     if (stmt->accessMethod != NULL)
    1038             :     {
    1039             :         Assert(RELKIND_HAS_TABLE_AM(relkind) || relkind == RELKIND_PARTITIONED_TABLE);
    1040         134 :         accessMethodId = get_table_am_oid(stmt->accessMethod, false);
    1041             :     }
    1042       64800 :     else if (RELKIND_HAS_TABLE_AM(relkind) || relkind == RELKIND_PARTITIONED_TABLE)
    1043             :     {
    1044       40820 :         if (stmt->partbound)
    1045             :         {
    1046             :             Assert(list_length(inheritOids) == 1);
    1047        8950 :             accessMethodId = get_rel_relam(linitial_oid(inheritOids));
    1048             :         }
    1049             : 
    1050       40820 :         if (RELKIND_HAS_TABLE_AM(relkind) && !OidIsValid(accessMethodId))
    1051       35256 :             accessMethodId = get_table_am_oid(default_table_access_method, false);
    1052             :     }
    1053             : 
    1054             :     /*
    1055             :      * Create the relation.  Inherited defaults and CHECK constraints are
    1056             :      * passed in for immediate handling --- since they don't need parsing,
    1057             :      * they can be stored immediately.
    1058             :      */
    1059       64916 :     relationId = heap_create_with_catalog(relname,
    1060             :                                           namespaceId,
    1061             :                                           tablespaceId,
    1062             :                                           InvalidOid,
    1063             :                                           InvalidOid,
    1064             :                                           ofTypeId,
    1065             :                                           ownerId,
    1066             :                                           accessMethodId,
    1067             :                                           descriptor,
    1068             :                                           list_concat(cookedDefaults,
    1069             :                                                       old_constraints),
    1070             :                                           relkind,
    1071       64916 :                                           stmt->relation->relpersistence,
    1072             :                                           false,
    1073             :                                           false,
    1074             :                                           stmt->oncommit,
    1075             :                                           reloptions,
    1076             :                                           true,
    1077             :                                           allowSystemTableMods,
    1078             :                                           false,
    1079             :                                           InvalidOid,
    1080             :                                           typaddress);
    1081             : 
    1082             :     /*
    1083             :      * We must bump the command counter to make the newly-created relation
    1084             :      * tuple visible for opening.
    1085             :      */
    1086       64868 :     CommandCounterIncrement();
    1087             : 
    1088             :     /*
    1089             :      * Open the new relation and acquire exclusive lock on it.  This isn't
    1090             :      * really necessary for locking out other backends (since they can't see
    1091             :      * the new rel anyway until we commit), but it keeps the lock manager from
    1092             :      * complaining about deadlock risks.
    1093             :      */
    1094       64868 :     rel = relation_open(relationId, AccessExclusiveLock);
    1095             : 
    1096             :     /*
    1097             :      * Now add any newly specified column default and generation expressions
    1098             :      * to the new relation.  These are passed to us in the form of raw
    1099             :      * parsetrees; we need to transform them to executable expression trees
    1100             :      * before they can be added. The most convenient way to do that is to
    1101             :      * apply the parser's transformExpr routine, but transformExpr doesn't
    1102             :      * work unless we have a pre-existing relation. So, the transformation has
    1103             :      * to be postponed to this final step of CREATE TABLE.
    1104             :      *
    1105             :      * This needs to be before processing the partitioning clauses because
    1106             :      * those could refer to generated columns.
    1107             :      */
    1108       64868 :     if (rawDefaults)
    1109        2880 :         AddRelationNewConstraints(rel, rawDefaults, NIL,
    1110             :                                   true, true, false, queryString);
    1111             : 
    1112             :     /*
    1113             :      * Make column generation expressions visible for use by partitioning.
    1114             :      */
    1115       64676 :     CommandCounterIncrement();
    1116             : 
    1117             :     /* Process and store partition bound, if any. */
    1118       64676 :     if (stmt->partbound)
    1119             :     {
    1120             :         PartitionBoundSpec *bound;
    1121             :         ParseState *pstate;
    1122        9054 :         Oid         parentId = linitial_oid(inheritOids),
    1123             :                     defaultPartOid;
    1124             :         Relation    parent,
    1125        9054 :                     defaultRel = NULL;
    1126             :         ParseNamespaceItem *nsitem;
    1127             : 
    1128             :         /* Already have strong enough lock on the parent */
    1129        9054 :         parent = table_open(parentId, NoLock);
    1130             : 
    1131             :         /*
    1132             :          * We are going to try to validate the partition bound specification
    1133             :          * against the partition key of parentRel, so it better have one.
    1134             :          */
    1135        9054 :         if (parent->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
    1136          18 :             ereport(ERROR,
    1137             :                     (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
    1138             :                      errmsg("\"%s\" is not partitioned",
    1139             :                             RelationGetRelationName(parent))));
    1140             : 
    1141             :         /*
    1142             :          * The partition constraint of the default partition depends on the
    1143             :          * partition bounds of every other partition. It is possible that
    1144             :          * another backend might be about to execute a query on the default
    1145             :          * partition table, and that the query relies on previously cached
    1146             :          * default partition constraints. We must therefore take a table lock
    1147             :          * strong enough to prevent all queries on the default partition from
    1148             :          * proceeding until we commit and send out a shared-cache-inval notice
    1149             :          * that will make them update their index lists.
    1150             :          *
    1151             :          * Order of locking: The relation being added won't be visible to
    1152             :          * other backends until it is committed, hence here in
    1153             :          * DefineRelation() the order of locking the default partition and the
    1154             :          * relation being added does not matter. But at all other places we
    1155             :          * need to lock the default relation before we lock the relation being
    1156             :          * added or removed i.e. we should take the lock in same order at all
    1157             :          * the places such that lock parent, lock default partition and then
    1158             :          * lock the partition so as to avoid a deadlock.
    1159             :          */
    1160             :         defaultPartOid =
    1161        9036 :             get_default_oid_from_partdesc(RelationGetPartitionDesc(parent,
    1162             :                                                                    true));
    1163        9036 :         if (OidIsValid(defaultPartOid))
    1164         378 :             defaultRel = table_open(defaultPartOid, AccessExclusiveLock);
    1165             : 
    1166             :         /* Transform the bound values */
    1167        9036 :         pstate = make_parsestate(NULL);
    1168        9036 :         pstate->p_sourcetext = queryString;
    1169             : 
    1170             :         /*
    1171             :          * Add an nsitem containing this relation, so that transformExpr
    1172             :          * called on partition bound expressions is able to report errors
    1173             :          * using a proper context.
    1174             :          */
    1175        9036 :         nsitem = addRangeTableEntryForRelation(pstate, rel, AccessShareLock,
    1176             :                                                NULL, false, false);
    1177        9036 :         addNSItemToQuery(pstate, nsitem, false, true, true);
    1178             : 
    1179        9036 :         bound = transformPartitionBound(pstate, parent, stmt->partbound);
    1180             : 
    1181             :         /*
    1182             :          * Check first that the new partition's bound is valid and does not
    1183             :          * overlap with any of existing partitions of the parent.
    1184             :          */
    1185        8832 :         check_new_partition_bound(relname, parent, bound, pstate);
    1186             : 
    1187             :         /*
    1188             :          * If the default partition exists, its partition constraints will
    1189             :          * change after the addition of this new partition such that it won't
    1190             :          * allow any row that qualifies for this new partition. So, check that
    1191             :          * the existing data in the default partition satisfies the constraint
    1192             :          * as it will exist after adding this partition.
    1193             :          */
    1194        8718 :         if (OidIsValid(defaultPartOid))
    1195             :         {
    1196         348 :             check_default_partition_contents(parent, defaultRel, bound);
    1197             :             /* Keep the lock until commit. */
    1198         330 :             table_close(defaultRel, NoLock);
    1199             :         }
    1200             : 
    1201             :         /* Update the pg_class entry. */
    1202        8700 :         StorePartitionBound(rel, parent, bound);
    1203             : 
    1204        8700 :         table_close(parent, NoLock);
    1205             :     }
    1206             : 
    1207             :     /* Store inheritance information for new rel. */
    1208       64322 :     StoreCatalogInheritance(relationId, inheritOids, stmt->partbound != NULL);
    1209             : 
    1210             :     /*
    1211             :      * Process the partitioning specification (if any) and store the partition
    1212             :      * key information into the catalog.
    1213             :      */
    1214       64322 :     if (partitioned)
    1215             :     {
    1216             :         ParseState *pstate;
    1217             :         int         partnatts;
    1218             :         AttrNumber  partattrs[PARTITION_MAX_KEYS];
    1219             :         Oid         partopclass[PARTITION_MAX_KEYS];
    1220             :         Oid         partcollation[PARTITION_MAX_KEYS];
    1221        5544 :         List       *partexprs = NIL;
    1222             : 
    1223        5544 :         pstate = make_parsestate(NULL);
    1224        5544 :         pstate->p_sourcetext = queryString;
    1225             : 
    1226        5544 :         partnatts = list_length(stmt->partspec->partParams);
    1227             : 
    1228             :         /* Protect fixed-size arrays here and in executor */
    1229        5544 :         if (partnatts > PARTITION_MAX_KEYS)
    1230           0 :             ereport(ERROR,
    1231             :                     (errcode(ERRCODE_TOO_MANY_COLUMNS),
    1232             :                      errmsg("cannot partition using more than %d columns",
    1233             :                             PARTITION_MAX_KEYS)));
    1234             : 
    1235             :         /*
    1236             :          * We need to transform the raw parsetrees corresponding to partition
    1237             :          * expressions into executable expression trees.  Like column defaults
    1238             :          * and CHECK constraints, we could not have done the transformation
    1239             :          * earlier.
    1240             :          */
    1241        5544 :         stmt->partspec = transformPartitionSpec(rel, stmt->partspec);
    1242             : 
    1243        5514 :         ComputePartitionAttrs(pstate, rel, stmt->partspec->partParams,
    1244             :                               partattrs, &partexprs, partopclass,
    1245        5514 :                               partcollation, stmt->partspec->strategy);
    1246             : 
    1247        5382 :         StorePartitionKey(rel, stmt->partspec->strategy, partnatts, partattrs,
    1248             :                           partexprs,
    1249             :                           partopclass, partcollation);
    1250             : 
    1251             :         /* make it all visible */
    1252        5382 :         CommandCounterIncrement();
    1253             :     }
    1254             : 
    1255             :     /*
    1256             :      * If we're creating a partition, create now all the indexes, triggers,
    1257             :      * FKs defined in the parent.
    1258             :      *
    1259             :      * We can't do it earlier, because DefineIndex wants to know the partition
    1260             :      * key which we just stored.
    1261             :      */
    1262       64160 :     if (stmt->partbound)
    1263             :     {
    1264        8694 :         Oid         parentId = linitial_oid(inheritOids);
    1265             :         Relation    parent;
    1266             :         List       *idxlist;
    1267             :         ListCell   *cell;
    1268             : 
    1269             :         /* Already have strong enough lock on the parent */
    1270        8694 :         parent = table_open(parentId, NoLock);
    1271        8694 :         idxlist = RelationGetIndexList(parent);
    1272             : 
    1273             :         /*
    1274             :          * For each index in the parent table, create one in the partition
    1275             :          */
    1276       10342 :         foreach(cell, idxlist)
    1277             :         {
    1278        1666 :             Relation    idxRel = index_open(lfirst_oid(cell), AccessShareLock);
    1279             :             AttrMap    *attmap;
    1280             :             IndexStmt  *idxstmt;
    1281             :             Oid         constraintOid;
    1282             : 
    1283        1666 :             if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
    1284             :             {
    1285          36 :                 if (idxRel->rd_index->indisunique)
    1286          12 :                     ereport(ERROR,
    1287             :                             (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    1288             :                              errmsg("cannot create foreign partition of partitioned table \"%s\"",
    1289             :                                     RelationGetRelationName(parent)),
    1290             :                              errdetail("Table \"%s\" contains indexes that are unique.",
    1291             :                                        RelationGetRelationName(parent))));
    1292             :                 else
    1293             :                 {
    1294          24 :                     index_close(idxRel, AccessShareLock);
    1295          24 :                     continue;
    1296             :                 }
    1297             :             }
    1298             : 
    1299        1630 :             attmap = build_attrmap_by_name(RelationGetDescr(rel),
    1300             :                                            RelationGetDescr(parent),
    1301             :                                            false);
    1302             :             idxstmt =
    1303        1630 :                 generateClonedIndexStmt(NULL, idxRel,
    1304             :                                         attmap, &constraintOid);
    1305        1630 :             DefineIndex(NULL,
    1306             :                         RelationGetRelid(rel),
    1307             :                         idxstmt,
    1308             :                         InvalidOid,
    1309             :                         RelationGetRelid(idxRel),
    1310             :                         constraintOid,
    1311             :                         -1,
    1312             :                         false, false, false, false, false);
    1313             : 
    1314        1624 :             index_close(idxRel, AccessShareLock);
    1315             :         }
    1316             : 
    1317        8676 :         list_free(idxlist);
    1318             : 
    1319             :         /*
    1320             :          * If there are any row-level triggers, clone them to the new
    1321             :          * partition.
    1322             :          */
    1323        8676 :         if (parent->trigdesc != NULL)
    1324         474 :             CloneRowTriggersToPartition(parent, rel);
    1325             : 
    1326             :         /*
    1327             :          * And foreign keys too.  Note that because we're freshly creating the
    1328             :          * table, there is no need to verify these new constraints.
    1329             :          */
    1330        8676 :         CloneForeignKeyConstraints(NULL, parent, rel);
    1331             : 
    1332        8676 :         table_close(parent, NoLock);
    1333             :     }
    1334             : 
    1335             :     /*
    1336             :      * Now add any newly specified CHECK constraints to the new relation. Same
    1337             :      * as for defaults above, but these need to come after partitioning is set
    1338             :      * up.
    1339             :      */
    1340       64142 :     if (stmt->constraints)
    1341         742 :         AddRelationNewConstraints(rel, NIL, stmt->constraints,
    1342             :                                   true, true, false, queryString);
    1343             : 
    1344             :     /*
    1345             :      * Finally, merge the not-null constraints that are declared directly with
    1346             :      * those that come from parent relations (making sure to count inheritance
    1347             :      * appropriately for each), create them, and set the attnotnull flag on
    1348             :      * columns that don't yet have it.
    1349             :      */
    1350       64112 :     nncols = AddRelationNotNullConstraints(rel, stmt->nnconstraints,
    1351             :                                            old_notnulls);
    1352      143896 :     foreach_int(attrnum, nncols)
    1353       15828 :         set_attnotnull(NULL, rel, attrnum, true, false);
    1354             : 
    1355       64034 :     ObjectAddressSet(address, RelationRelationId, relationId);
    1356             : 
    1357             :     /*
    1358             :      * Clean up.  We keep lock on new relation (although it shouldn't be
    1359             :      * visible to anyone else anyway, until commit).
    1360             :      */
    1361       64034 :     relation_close(rel, NoLock);
    1362             : 
    1363       64034 :     return address;
    1364             : }
    1365             : 
    1366             : /*
    1367             :  * BuildDescForRelation
    1368             :  *
    1369             :  * Given a list of ColumnDef nodes, build a TupleDesc.
    1370             :  *
    1371             :  * Note: This is only for the limited purpose of table and view creation.  Not
    1372             :  * everything is filled in.  A real tuple descriptor should be obtained from
    1373             :  * the relcache.
    1374             :  */
    1375             : TupleDesc
    1376       68650 : BuildDescForRelation(const List *columns)
    1377             : {
    1378             :     int         natts;
    1379             :     AttrNumber  attnum;
    1380             :     ListCell   *l;
    1381             :     TupleDesc   desc;
    1382             :     char       *attname;
    1383             :     Oid         atttypid;
    1384             :     int32       atttypmod;
    1385             :     Oid         attcollation;
    1386             :     int         attdim;
    1387             : 
    1388             :     /*
    1389             :      * allocate a new tuple descriptor
    1390             :      */
    1391       68650 :     natts = list_length(columns);
    1392       68650 :     desc = CreateTemplateTupleDesc(natts);
    1393             : 
    1394       68650 :     attnum = 0;
    1395             : 
    1396      338436 :     foreach(l, columns)
    1397             :     {
    1398      269846 :         ColumnDef  *entry = lfirst(l);
    1399             :         AclResult   aclresult;
    1400             :         Form_pg_attribute att;
    1401             : 
    1402             :         /*
    1403             :          * for each entry in the list, get the name and type information from
    1404             :          * the list and have TupleDescInitEntry fill in the attribute
    1405             :          * information we need.
    1406             :          */
    1407      269846 :         attnum++;
    1408             : 
    1409      269846 :         attname = entry->colname;
    1410      269846 :         typenameTypeIdAndMod(NULL, entry->typeName, &atttypid, &atttypmod);
    1411             : 
    1412      269846 :         aclresult = object_aclcheck(TypeRelationId, atttypid, GetUserId(), ACL_USAGE);
    1413      269846 :         if (aclresult != ACLCHECK_OK)
    1414          42 :             aclcheck_error_type(aclresult, atttypid);
    1415             : 
    1416      269804 :         attcollation = GetColumnDefCollation(NULL, entry, atttypid);
    1417      269804 :         attdim = list_length(entry->typeName->arrayBounds);
    1418      269804 :         if (attdim > PG_INT16_MAX)
    1419           0 :             ereport(ERROR,
    1420             :                     errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
    1421             :                     errmsg("too many array dimensions"));
    1422             : 
    1423      269804 :         if (entry->typeName->setof)
    1424           0 :             ereport(ERROR,
    1425             :                     (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    1426             :                      errmsg("column \"%s\" cannot be declared SETOF",
    1427             :                             attname)));
    1428             : 
    1429      269804 :         TupleDescInitEntry(desc, attnum, attname,
    1430             :                            atttypid, atttypmod, attdim);
    1431      269804 :         att = TupleDescAttr(desc, attnum - 1);
    1432             : 
    1433             :         /* Override TupleDescInitEntry's settings as requested */
    1434      269804 :         TupleDescInitEntryCollation(desc, attnum, attcollation);
    1435             : 
    1436             :         /* Fill in additional stuff not handled by TupleDescInitEntry */
    1437      269804 :         att->attnotnull = entry->is_not_null;
    1438      269804 :         att->attislocal = entry->is_local;
    1439      269804 :         att->attinhcount = entry->inhcount;
    1440      269804 :         att->attidentity = entry->identity;
    1441      269804 :         att->attgenerated = entry->generated;
    1442      269804 :         att->attcompression = GetAttributeCompression(att->atttypid, entry->compression);
    1443      269792 :         if (entry->storage)
    1444       24998 :             att->attstorage = entry->storage;
    1445      244794 :         else if (entry->storage_name)
    1446          56 :             att->attstorage = GetAttributeStorage(att->atttypid, entry->storage_name);
    1447             : 
    1448      269786 :         populate_compact_attribute(desc, attnum - 1);
    1449             :     }
    1450             : 
    1451       68590 :     return desc;
    1452             : }
    1453             : 
    1454             : /*
    1455             :  * Emit the right error or warning message for a "DROP" command issued on a
    1456             :  * non-existent relation
    1457             :  */
    1458             : static void
    1459        1132 : DropErrorMsgNonExistent(RangeVar *rel, char rightkind, bool missing_ok)
    1460             : {
    1461             :     const struct dropmsgstrings *rentry;
    1462             : 
    1463        1252 :     if (rel->schemaname != NULL &&
    1464         120 :         !OidIsValid(LookupNamespaceNoError(rel->schemaname)))
    1465             :     {
    1466          42 :         if (!missing_ok)
    1467             :         {
    1468           0 :             ereport(ERROR,
    1469             :                     (errcode(ERRCODE_UNDEFINED_SCHEMA),
    1470             :                      errmsg("schema \"%s\" does not exist", rel->schemaname)));
    1471             :         }
    1472             :         else
    1473             :         {
    1474          42 :             ereport(NOTICE,
    1475             :                     (errmsg("schema \"%s\" does not exist, skipping",
    1476             :                             rel->schemaname)));
    1477             :         }
    1478          42 :         return;
    1479             :     }
    1480             : 
    1481        1410 :     for (rentry = dropmsgstringarray; rentry->kind != '\0'; rentry++)
    1482             :     {
    1483        1410 :         if (rentry->kind == rightkind)
    1484             :         {
    1485        1090 :             if (!missing_ok)
    1486             :             {
    1487         138 :                 ereport(ERROR,
    1488             :                         (errcode(rentry->nonexistent_code),
    1489             :                          errmsg(rentry->nonexistent_msg, rel->relname)));
    1490             :             }
    1491             :             else
    1492             :             {
    1493         952 :                 ereport(NOTICE, (errmsg(rentry->skipping_msg, rel->relname)));
    1494         952 :                 break;
    1495             :             }
    1496             :         }
    1497             :     }
    1498             : 
    1499             :     Assert(rentry->kind != '\0');    /* Should be impossible */
    1500             : }
    1501             : 
    1502             : /*
    1503             :  * Emit the right error message for a "DROP" command issued on a
    1504             :  * relation of the wrong type
    1505             :  */
    1506             : static void
    1507           0 : DropErrorMsgWrongType(const char *relname, char wrongkind, char rightkind)
    1508             : {
    1509             :     const struct dropmsgstrings *rentry;
    1510             :     const struct dropmsgstrings *wentry;
    1511             : 
    1512           0 :     for (rentry = dropmsgstringarray; rentry->kind != '\0'; rentry++)
    1513           0 :         if (rentry->kind == rightkind)
    1514           0 :             break;
    1515             :     Assert(rentry->kind != '\0');
    1516             : 
    1517           0 :     for (wentry = dropmsgstringarray; wentry->kind != '\0'; wentry++)
    1518           0 :         if (wentry->kind == wrongkind)
    1519           0 :             break;
    1520             :     /* wrongkind could be something we don't have in our table... */
    1521             : 
    1522           0 :     ereport(ERROR,
    1523             :             (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    1524             :              errmsg(rentry->nota_msg, relname),
    1525             :              (wentry->kind != '\0') ? errhint("%s", _(wentry->drophint_msg)) : 0));
    1526             : }
    1527             : 
    1528             : /*
    1529             :  * RemoveRelations
    1530             :  *      Implements DROP TABLE, DROP INDEX, DROP SEQUENCE, DROP VIEW,
    1531             :  *      DROP MATERIALIZED VIEW, DROP FOREIGN TABLE
    1532             :  */
    1533             : void
    1534       18208 : RemoveRelations(DropStmt *drop)
    1535             : {
    1536             :     ObjectAddresses *objects;
    1537             :     char        relkind;
    1538             :     ListCell   *cell;
    1539       18208 :     int         flags = 0;
    1540       18208 :     LOCKMODE    lockmode = AccessExclusiveLock;
    1541             : 
    1542             :     /* DROP CONCURRENTLY uses a weaker lock, and has some restrictions */
    1543       18208 :     if (drop->concurrent)
    1544             :     {
    1545             :         /*
    1546             :          * Note that for temporary relations this lock may get upgraded later
    1547             :          * on, but as no other session can access a temporary relation, this
    1548             :          * is actually fine.
    1549             :          */
    1550         222 :         lockmode = ShareUpdateExclusiveLock;
    1551             :         Assert(drop->removeType == OBJECT_INDEX);
    1552         222 :         if (list_length(drop->objects) != 1)
    1553           6 :             ereport(ERROR,
    1554             :                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    1555             :                      errmsg("DROP INDEX CONCURRENTLY does not support dropping multiple objects")));
    1556         216 :         if (drop->behavior == DROP_CASCADE)
    1557           0 :             ereport(ERROR,
    1558             :                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    1559             :                      errmsg("DROP INDEX CONCURRENTLY does not support CASCADE")));
    1560             :     }
    1561             : 
    1562             :     /*
    1563             :      * First we identify all the relations, then we delete them in a single
    1564             :      * performMultipleDeletions() call.  This is to avoid unwanted DROP
    1565             :      * RESTRICT errors if one of the relations depends on another.
    1566             :      */
    1567             : 
    1568             :     /* Determine required relkind */
    1569       18202 :     switch (drop->removeType)
    1570             :     {
    1571       15862 :         case OBJECT_TABLE:
    1572       15862 :             relkind = RELKIND_RELATION;
    1573       15862 :             break;
    1574             : 
    1575         904 :         case OBJECT_INDEX:
    1576         904 :             relkind = RELKIND_INDEX;
    1577         904 :             break;
    1578             : 
    1579         178 :         case OBJECT_SEQUENCE:
    1580         178 :             relkind = RELKIND_SEQUENCE;
    1581         178 :             break;
    1582             : 
    1583         968 :         case OBJECT_VIEW:
    1584         968 :             relkind = RELKIND_VIEW;
    1585         968 :             break;
    1586             : 
    1587         126 :         case OBJECT_MATVIEW:
    1588         126 :             relkind = RELKIND_MATVIEW;
    1589         126 :             break;
    1590             : 
    1591         164 :         case OBJECT_FOREIGN_TABLE:
    1592         164 :             relkind = RELKIND_FOREIGN_TABLE;
    1593         164 :             break;
    1594             : 
    1595           0 :         default:
    1596           0 :             elog(ERROR, "unrecognized drop object type: %d",
    1597             :                  (int) drop->removeType);
    1598             :             relkind = 0;        /* keep compiler quiet */
    1599             :             break;
    1600             :     }
    1601             : 
    1602             :     /* Lock and validate each relation; build a list of object addresses */
    1603       18202 :     objects = new_object_addresses();
    1604             : 
    1605       40414 :     foreach(cell, drop->objects)
    1606             :     {
    1607       22376 :         RangeVar   *rel = makeRangeVarFromNameList((List *) lfirst(cell));
    1608             :         Oid         relOid;
    1609             :         ObjectAddress obj;
    1610             :         struct DropRelationCallbackState state;
    1611             : 
    1612             :         /*
    1613             :          * These next few steps are a great deal like relation_openrv, but we
    1614             :          * don't bother building a relcache entry since we don't need it.
    1615             :          *
    1616             :          * Check for shared-cache-inval messages before trying to access the
    1617             :          * relation.  This is needed to cover the case where the name
    1618             :          * identifies a rel that has been dropped and recreated since the
    1619             :          * start of our transaction: if we don't flush the old syscache entry,
    1620             :          * then we'll latch onto that entry and suffer an error later.
    1621             :          */
    1622       22376 :         AcceptInvalidationMessages();
    1623             : 
    1624             :         /* Look up the appropriate relation using namespace search. */
    1625       22376 :         state.expected_relkind = relkind;
    1626       44752 :         state.heap_lockmode = drop->concurrent ?
    1627       22376 :             ShareUpdateExclusiveLock : AccessExclusiveLock;
    1628             :         /* We must initialize these fields to show that no locks are held: */
    1629       22376 :         state.heapOid = InvalidOid;
    1630       22376 :         state.partParentOid = InvalidOid;
    1631             : 
    1632       22376 :         relOid = RangeVarGetRelidExtended(rel, lockmode, RVR_MISSING_OK,
    1633             :                                           RangeVarCallbackForDropRelation,
    1634             :                                           &state);
    1635             : 
    1636             :         /* Not there? */
    1637       22356 :         if (!OidIsValid(relOid))
    1638             :         {
    1639        1132 :             DropErrorMsgNonExistent(rel, relkind, drop->missing_ok);
    1640         994 :             continue;
    1641             :         }
    1642             : 
    1643             :         /*
    1644             :          * Decide if concurrent mode needs to be used here or not.  The
    1645             :          * callback retrieved the rel's persistence for us.
    1646             :          */
    1647       21224 :         if (drop->concurrent &&
    1648         210 :             state.actual_relpersistence != RELPERSISTENCE_TEMP)
    1649             :         {
    1650             :             Assert(list_length(drop->objects) == 1 &&
    1651             :                    drop->removeType == OBJECT_INDEX);
    1652         192 :             flags |= PERFORM_DELETION_CONCURRENTLY;
    1653             :         }
    1654             : 
    1655             :         /*
    1656             :          * Concurrent index drop cannot be used with partitioned indexes,
    1657             :          * either.
    1658             :          */
    1659       21224 :         if ((flags & PERFORM_DELETION_CONCURRENTLY) != 0 &&
    1660         192 :             state.actual_relkind == RELKIND_PARTITIONED_INDEX)
    1661           6 :             ereport(ERROR,
    1662             :                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    1663             :                      errmsg("cannot drop partitioned index \"%s\" concurrently",
    1664             :                             rel->relname)));
    1665             : 
    1666             :         /*
    1667             :          * If we're told to drop a partitioned index, we must acquire lock on
    1668             :          * all the children of its parent partitioned table before proceeding.
    1669             :          * Otherwise we'd try to lock the child index partitions before their
    1670             :          * tables, leading to potential deadlock against other sessions that
    1671             :          * will lock those objects in the other order.
    1672             :          */
    1673       21218 :         if (state.actual_relkind == RELKIND_PARTITIONED_INDEX)
    1674          76 :             (void) find_all_inheritors(state.heapOid,
    1675             :                                        state.heap_lockmode,
    1676             :                                        NULL);
    1677             : 
    1678             :         /* OK, we're ready to delete this one */
    1679       21218 :         obj.classId = RelationRelationId;
    1680       21218 :         obj.objectId = relOid;
    1681       21218 :         obj.objectSubId = 0;
    1682             : 
    1683       21218 :         add_exact_object_address(&obj, objects);
    1684             :     }
    1685             : 
    1686       18038 :     performMultipleDeletions(objects, drop->behavior, flags);
    1687             : 
    1688       17896 :     free_object_addresses(objects);
    1689       17896 : }
    1690             : 
    1691             : /*
    1692             :  * Before acquiring a table lock, check whether we have sufficient rights.
    1693             :  * In the case of DROP INDEX, also try to lock the table before the index.
    1694             :  * Also, if the table to be dropped is a partition, we try to lock the parent
    1695             :  * first.
    1696             :  */
    1697             : static void
    1698       22790 : RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid, Oid oldRelOid,
    1699             :                                 void *arg)
    1700             : {
    1701             :     HeapTuple   tuple;
    1702             :     struct DropRelationCallbackState *state;
    1703             :     char        expected_relkind;
    1704             :     bool        is_partition;
    1705             :     Form_pg_class classform;
    1706             :     LOCKMODE    heap_lockmode;
    1707       22790 :     bool        invalid_system_index = false;
    1708             : 
    1709       22790 :     state = (struct DropRelationCallbackState *) arg;
    1710       22790 :     heap_lockmode = state->heap_lockmode;
    1711             : 
    1712             :     /*
    1713             :      * If we previously locked some other index's heap, and the name we're
    1714             :      * looking up no longer refers to that relation, release the now-useless
    1715             :      * lock.
    1716             :      */
    1717       22790 :     if (relOid != oldRelOid && OidIsValid(state->heapOid))
    1718             :     {
    1719           0 :         UnlockRelationOid(state->heapOid, heap_lockmode);
    1720           0 :         state->heapOid = InvalidOid;
    1721             :     }
    1722             : 
    1723             :     /*
    1724             :      * Similarly, if we previously locked some other partition's heap, and the
    1725             :      * name we're looking up no longer refers to that relation, release the
    1726             :      * now-useless lock.
    1727             :      */
    1728       22790 :     if (relOid != oldRelOid && OidIsValid(state->partParentOid))
    1729             :     {
    1730           0 :         UnlockRelationOid(state->partParentOid, AccessExclusiveLock);
    1731           0 :         state->partParentOid = InvalidOid;
    1732             :     }
    1733             : 
    1734             :     /* Didn't find a relation, so no need for locking or permission checks. */
    1735       22790 :     if (!OidIsValid(relOid))
    1736        1138 :         return;
    1737             : 
    1738       21652 :     tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid));
    1739       21652 :     if (!HeapTupleIsValid(tuple))
    1740           0 :         return;                 /* concurrently dropped, so nothing to do */
    1741       21652 :     classform = (Form_pg_class) GETSTRUCT(tuple);
    1742       21652 :     is_partition = classform->relispartition;
    1743             : 
    1744             :     /* Pass back some data to save lookups in RemoveRelations */
    1745       21652 :     state->actual_relkind = classform->relkind;
    1746       21652 :     state->actual_relpersistence = classform->relpersistence;
    1747             : 
    1748             :     /*
    1749             :      * Both RELKIND_RELATION and RELKIND_PARTITIONED_TABLE are OBJECT_TABLE,
    1750             :      * but RemoveRelations() can only pass one relkind for a given relation.
    1751             :      * It chooses RELKIND_RELATION for both regular and partitioned tables.
    1752             :      * That means we must be careful before giving the wrong type error when
    1753             :      * the relation is RELKIND_PARTITIONED_TABLE.  An equivalent problem
    1754             :      * exists with indexes.
    1755             :      */
    1756       21652 :     if (classform->relkind == RELKIND_PARTITIONED_TABLE)
    1757        3418 :         expected_relkind = RELKIND_RELATION;
    1758       18234 :     else if (classform->relkind == RELKIND_PARTITIONED_INDEX)
    1759          84 :         expected_relkind = RELKIND_INDEX;
    1760             :     else
    1761       18150 :         expected_relkind = classform->relkind;
    1762             : 
    1763       21652 :     if (state->expected_relkind != expected_relkind)
    1764           0 :         DropErrorMsgWrongType(rel->relname, classform->relkind,
    1765           0 :                               state->expected_relkind);
    1766             : 
    1767             :     /* Allow DROP to either table owner or schema owner */
    1768       21652 :     if (!object_ownercheck(RelationRelationId, relOid, GetUserId()) &&
    1769          18 :         !object_ownercheck(NamespaceRelationId, classform->relnamespace, GetUserId()))
    1770          18 :         aclcheck_error(ACLCHECK_NOT_OWNER,
    1771          18 :                        get_relkind_objtype(classform->relkind),
    1772          18 :                        rel->relname);
    1773             : 
    1774             :     /*
    1775             :      * Check the case of a system index that might have been invalidated by a
    1776             :      * failed concurrent process and allow its drop. For the time being, this
    1777             :      * only concerns indexes of toast relations that became invalid during a
    1778             :      * REINDEX CONCURRENTLY process.
    1779             :      */
    1780       21634 :     if (IsSystemClass(relOid, classform) && classform->relkind == RELKIND_INDEX)
    1781             :     {
    1782             :         HeapTuple   locTuple;
    1783             :         Form_pg_index indexform;
    1784             :         bool        indisvalid;
    1785             : 
    1786           0 :         locTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(relOid));
    1787           0 :         if (!HeapTupleIsValid(locTuple))
    1788             :         {
    1789           0 :             ReleaseSysCache(tuple);
    1790           0 :             return;
    1791             :         }
    1792             : 
    1793           0 :         indexform = (Form_pg_index) GETSTRUCT(locTuple);
    1794           0 :         indisvalid = indexform->indisvalid;
    1795           0 :         ReleaseSysCache(locTuple);
    1796             : 
    1797             :         /* Mark object as being an invalid index of system catalogs */
    1798           0 :         if (!indisvalid)
    1799           0 :             invalid_system_index = true;
    1800             :     }
    1801             : 
    1802             :     /* In the case of an invalid index, it is fine to bypass this check */
    1803       21634 :     if (!invalid_system_index && !allowSystemTableMods && IsSystemClass(relOid, classform))
    1804           2 :         ereport(ERROR,
    1805             :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
    1806             :                  errmsg("permission denied: \"%s\" is a system catalog",
    1807             :                         rel->relname)));
    1808             : 
    1809       21632 :     ReleaseSysCache(tuple);
    1810             : 
    1811             :     /*
    1812             :      * In DROP INDEX, attempt to acquire lock on the parent table before
    1813             :      * locking the index.  index_drop() will need this anyway, and since
    1814             :      * regular queries lock tables before their indexes, we risk deadlock if
    1815             :      * we do it the other way around.  No error if we don't find a pg_index
    1816             :      * entry, though --- the relation may have been dropped.  Note that this
    1817             :      * code will execute for either plain or partitioned indexes.
    1818             :      */
    1819       21632 :     if (expected_relkind == RELKIND_INDEX &&
    1820             :         relOid != oldRelOid)
    1821             :     {
    1822         892 :         state->heapOid = IndexGetRelation(relOid, true);
    1823         892 :         if (OidIsValid(state->heapOid))
    1824         892 :             LockRelationOid(state->heapOid, heap_lockmode);
    1825             :     }
    1826             : 
    1827             :     /*
    1828             :      * Similarly, if the relation is a partition, we must acquire lock on its
    1829             :      * parent before locking the partition.  That's because queries lock the
    1830             :      * parent before its partitions, so we risk deadlock if we do it the other
    1831             :      * way around.
    1832             :      */
    1833       21632 :     if (is_partition && relOid != oldRelOid)
    1834             :     {
    1835         618 :         state->partParentOid = get_partition_parent(relOid, true);
    1836         618 :         if (OidIsValid(state->partParentOid))
    1837         618 :             LockRelationOid(state->partParentOid, AccessExclusiveLock);
    1838             :     }
    1839             : }
    1840             : 
    1841             : /*
    1842             :  * ExecuteTruncate
    1843             :  *      Executes a TRUNCATE command.
    1844             :  *
    1845             :  * This is a multi-relation truncate.  We first open and grab exclusive
    1846             :  * lock on all relations involved, checking permissions and otherwise
    1847             :  * verifying that the relation is OK for truncation.  Note that if relations
    1848             :  * are foreign tables, at this stage, we have not yet checked that their
    1849             :  * foreign data in external data sources are OK for truncation.  These are
    1850             :  * checked when foreign data are actually truncated later.  In CASCADE mode,
    1851             :  * relations having FK references to the targeted relations are automatically
    1852             :  * added to the group; in RESTRICT mode, we check that all FK references are
    1853             :  * internal to the group that's being truncated.  Finally all the relations
    1854             :  * are truncated and reindexed.
    1855             :  */
    1856             : void
    1857        1782 : ExecuteTruncate(TruncateStmt *stmt)
    1858             : {
    1859        1782 :     List       *rels = NIL;
    1860        1782 :     List       *relids = NIL;
    1861        1782 :     List       *relids_logged = NIL;
    1862             :     ListCell   *cell;
    1863             : 
    1864             :     /*
    1865             :      * Open, exclusive-lock, and check all the explicitly-specified relations
    1866             :      */
    1867        3776 :     foreach(cell, stmt->relations)
    1868             :     {
    1869        2050 :         RangeVar   *rv = lfirst(cell);
    1870             :         Relation    rel;
    1871        2050 :         bool        recurse = rv->inh;
    1872             :         Oid         myrelid;
    1873        2050 :         LOCKMODE    lockmode = AccessExclusiveLock;
    1874             : 
    1875        2050 :         myrelid = RangeVarGetRelidExtended(rv, lockmode,
    1876             :                                            0, RangeVarCallbackForTruncate,
    1877             :                                            NULL);
    1878             : 
    1879             :         /* don't throw error for "TRUNCATE foo, foo" */
    1880        2012 :         if (list_member_oid(relids, myrelid))
    1881           2 :             continue;
    1882             : 
    1883             :         /* open the relation, we already hold a lock on it */
    1884        2010 :         rel = table_open(myrelid, NoLock);
    1885             : 
    1886             :         /*
    1887             :          * RangeVarGetRelidExtended() has done most checks with its callback,
    1888             :          * but other checks with the now-opened Relation remain.
    1889             :          */
    1890        2010 :         truncate_check_activity(rel);
    1891             : 
    1892        2004 :         rels = lappend(rels, rel);
    1893        2004 :         relids = lappend_oid(relids, myrelid);
    1894             : 
    1895             :         /* Log this relation only if needed for logical decoding */
    1896        2004 :         if (RelationIsLogicallyLogged(rel))
    1897          74 :             relids_logged = lappend_oid(relids_logged, myrelid);
    1898             : 
    1899        2004 :         if (recurse)
    1900             :         {
    1901             :             ListCell   *child;
    1902             :             List       *children;
    1903             : 
    1904        1942 :             children = find_all_inheritors(myrelid, lockmode, NULL);
    1905             : 
    1906        5688 :             foreach(child, children)
    1907             :             {
    1908        3746 :                 Oid         childrelid = lfirst_oid(child);
    1909             : 
    1910        3746 :                 if (list_member_oid(relids, childrelid))
    1911        1942 :                     continue;
    1912             : 
    1913             :                 /* find_all_inheritors already got lock */
    1914        1804 :                 rel = table_open(childrelid, NoLock);
    1915             : 
    1916             :                 /*
    1917             :                  * It is possible that the parent table has children that are
    1918             :                  * temp tables of other backends.  We cannot safely access
    1919             :                  * such tables (because of buffering issues), and the best
    1920             :                  * thing to do is to silently ignore them.  Note that this
    1921             :                  * check is the same as one of the checks done in
    1922             :                  * truncate_check_activity() called below, still it is kept
    1923             :                  * here for simplicity.
    1924             :                  */
    1925        1804 :                 if (RELATION_IS_OTHER_TEMP(rel))
    1926             :                 {
    1927           8 :                     table_close(rel, lockmode);
    1928           8 :                     continue;
    1929             :                 }
    1930             : 
    1931             :                 /*
    1932             :                  * Inherited TRUNCATE commands perform access permission
    1933             :                  * checks on the parent table only. So we skip checking the
    1934             :                  * children's permissions and don't call
    1935             :                  * truncate_check_perms() here.
    1936             :                  */
    1937        1796 :                 truncate_check_rel(RelationGetRelid(rel), rel->rd_rel);
    1938        1796 :                 truncate_check_activity(rel);
    1939             : 
    1940        1796 :                 rels = lappend(rels, rel);
    1941        1796 :                 relids = lappend_oid(relids, childrelid);
    1942             : 
    1943             :                 /* Log this relation only if needed for logical decoding */
    1944        1796 :                 if (RelationIsLogicallyLogged(rel))
    1945          22 :                     relids_logged = lappend_oid(relids_logged, childrelid);
    1946             :             }
    1947             :         }
    1948          62 :         else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
    1949          12 :             ereport(ERROR,
    1950             :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    1951             :                      errmsg("cannot truncate only a partitioned table"),
    1952             :                      errhint("Do not specify the ONLY keyword, or use TRUNCATE ONLY on the partitions directly.")));
    1953             :     }
    1954             : 
    1955        1726 :     ExecuteTruncateGuts(rels, relids, relids_logged,
    1956        1726 :                         stmt->behavior, stmt->restart_seqs, false);
    1957             : 
    1958             :     /* And close the rels */
    1959        5278 :     foreach(cell, rels)
    1960             :     {
    1961        3634 :         Relation    rel = (Relation) lfirst(cell);
    1962             : 
    1963        3634 :         table_close(rel, NoLock);
    1964             :     }
    1965        1644 : }
    1966             : 
    1967             : /*
    1968             :  * ExecuteTruncateGuts
    1969             :  *
    1970             :  * Internal implementation of TRUNCATE.  This is called by the actual TRUNCATE
    1971             :  * command (see above) as well as replication subscribers that execute a
    1972             :  * replicated TRUNCATE action.
    1973             :  *
    1974             :  * explicit_rels is the list of Relations to truncate that the command
    1975             :  * specified.  relids is the list of Oids corresponding to explicit_rels.
    1976             :  * relids_logged is the list of Oids (a subset of relids) that require
    1977             :  * WAL-logging.  This is all a bit redundant, but the existing callers have
    1978             :  * this information handy in this form.
    1979             :  */
    1980             : void
    1981        1762 : ExecuteTruncateGuts(List *explicit_rels,
    1982             :                     List *relids,
    1983             :                     List *relids_logged,
    1984             :                     DropBehavior behavior, bool restart_seqs,
    1985             :                     bool run_as_table_owner)
    1986             : {
    1987             :     List       *rels;
    1988        1762 :     List       *seq_relids = NIL;
    1989        1762 :     HTAB       *ft_htab = NULL;
    1990             :     EState     *estate;
    1991             :     ResultRelInfo *resultRelInfos;
    1992             :     ResultRelInfo *resultRelInfo;
    1993             :     SubTransactionId mySubid;
    1994             :     ListCell   *cell;
    1995             :     Oid        *logrelids;
    1996             : 
    1997             :     /*
    1998             :      * Check the explicitly-specified relations.
    1999             :      *
    2000             :      * In CASCADE mode, suck in all referencing relations as well.  This
    2001             :      * requires multiple iterations to find indirectly-dependent relations. At
    2002             :      * each phase, we need to exclusive-lock new rels before looking for their
    2003             :      * dependencies, else we might miss something.  Also, we check each rel as
    2004             :      * soon as we open it, to avoid a faux pas such as holding lock for a long
    2005             :      * time on a rel we have no permissions for.
    2006             :      */
    2007        1762 :     rels = list_copy(explicit_rels);
    2008        1762 :     if (behavior == DROP_CASCADE)
    2009             :     {
    2010             :         for (;;)
    2011          40 :         {
    2012             :             List       *newrelids;
    2013             : 
    2014          80 :             newrelids = heap_truncate_find_FKs(relids);
    2015          80 :             if (newrelids == NIL)
    2016          40 :                 break;          /* nothing else to add */
    2017             : 
    2018         134 :             foreach(cell, newrelids)
    2019             :             {
    2020          94 :                 Oid         relid = lfirst_oid(cell);
    2021             :                 Relation    rel;
    2022             : 
    2023          94 :                 rel = table_open(relid, AccessExclusiveLock);
    2024          94 :                 ereport(NOTICE,
    2025             :                         (errmsg("truncate cascades to table \"%s\"",
    2026             :                                 RelationGetRelationName(rel))));
    2027          94 :                 truncate_check_rel(relid, rel->rd_rel);
    2028          94 :                 truncate_check_perms(relid, rel->rd_rel);
    2029          94 :                 truncate_check_activity(rel);
    2030          94 :                 rels = lappend(rels, rel);
    2031          94 :                 relids = lappend_oid(relids, relid);
    2032             : 
    2033             :                 /* Log this relation only if needed for logical decoding */
    2034          94 :                 if (RelationIsLogicallyLogged(rel))
    2035           0 :                     relids_logged = lappend_oid(relids_logged, relid);
    2036             :             }
    2037             :         }
    2038             :     }
    2039             : 
    2040             :     /*
    2041             :      * Check foreign key references.  In CASCADE mode, this should be
    2042             :      * unnecessary since we just pulled in all the references; but as a
    2043             :      * cross-check, do it anyway if in an Assert-enabled build.
    2044             :      */
    2045             : #ifdef USE_ASSERT_CHECKING
    2046             :     heap_truncate_check_FKs(rels, false);
    2047             : #else
    2048        1762 :     if (behavior == DROP_RESTRICT)
    2049        1722 :         heap_truncate_check_FKs(rels, false);
    2050             : #endif
    2051             : 
    2052             :     /*
    2053             :      * If we are asked to restart sequences, find all the sequences, lock them
    2054             :      * (we need AccessExclusiveLock for ResetSequence), and check permissions.
    2055             :      * We want to do this early since it's pointless to do all the truncation
    2056             :      * work only to fail on sequence permissions.
    2057             :      */
    2058        1688 :     if (restart_seqs)
    2059             :     {
    2060          48 :         foreach(cell, rels)
    2061             :         {
    2062          24 :             Relation    rel = (Relation) lfirst(cell);
    2063          24 :             List       *seqlist = getOwnedSequences(RelationGetRelid(rel));
    2064             :             ListCell   *seqcell;
    2065             : 
    2066          58 :             foreach(seqcell, seqlist)
    2067             :             {
    2068          34 :                 Oid         seq_relid = lfirst_oid(seqcell);
    2069             :                 Relation    seq_rel;
    2070             : 
    2071          34 :                 seq_rel = relation_open(seq_relid, AccessExclusiveLock);
    2072             : 
    2073             :                 /* This check must match AlterSequence! */
    2074          34 :                 if (!object_ownercheck(RelationRelationId, seq_relid, GetUserId()))
    2075           0 :                     aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_SEQUENCE,
    2076           0 :                                    RelationGetRelationName(seq_rel));
    2077             : 
    2078          34 :                 seq_relids = lappend_oid(seq_relids, seq_relid);
    2079             : 
    2080          34 :                 relation_close(seq_rel, NoLock);
    2081             :             }
    2082             :         }
    2083             :     }
    2084             : 
    2085             :     /* Prepare to catch AFTER triggers. */
    2086        1688 :     AfterTriggerBeginQuery();
    2087             : 
    2088             :     /*
    2089             :      * To fire triggers, we'll need an EState as well as a ResultRelInfo for
    2090             :      * each relation.  We don't need to call ExecOpenIndices, though.
    2091             :      *
    2092             :      * We put the ResultRelInfos in the es_opened_result_relations list, even
    2093             :      * though we don't have a range table and don't populate the
    2094             :      * es_result_relations array.  That's a bit bogus, but it's enough to make
    2095             :      * ExecGetTriggerResultRel() find them.
    2096             :      */
    2097        1688 :     estate = CreateExecutorState();
    2098             :     resultRelInfos = (ResultRelInfo *)
    2099        1688 :         palloc(list_length(rels) * sizeof(ResultRelInfo));
    2100        1688 :     resultRelInfo = resultRelInfos;
    2101        5490 :     foreach(cell, rels)
    2102             :     {
    2103        3802 :         Relation    rel = (Relation) lfirst(cell);
    2104             : 
    2105        3802 :         InitResultRelInfo(resultRelInfo,
    2106             :                           rel,
    2107             :                           0,    /* dummy rangetable index */
    2108             :                           NULL,
    2109             :                           0);
    2110        3802 :         estate->es_opened_result_relations =
    2111        3802 :             lappend(estate->es_opened_result_relations, resultRelInfo);
    2112        3802 :         resultRelInfo++;
    2113             :     }
    2114             : 
    2115             :     /*
    2116             :      * Process all BEFORE STATEMENT TRUNCATE triggers before we begin
    2117             :      * truncating (this is because one of them might throw an error). Also, if
    2118             :      * we were to allow them to prevent statement execution, that would need
    2119             :      * to be handled here.
    2120             :      */
    2121        1688 :     resultRelInfo = resultRelInfos;
    2122        5490 :     foreach(cell, rels)
    2123             :     {
    2124             :         UserContext ucxt;
    2125             : 
    2126        3802 :         if (run_as_table_owner)
    2127          66 :             SwitchToUntrustedUser(resultRelInfo->ri_RelationDesc->rd_rel->relowner,
    2128             :                                   &ucxt);
    2129        3802 :         ExecBSTruncateTriggers(estate, resultRelInfo);
    2130        3802 :         if (run_as_table_owner)
    2131          66 :             RestoreUserContext(&ucxt);
    2132        3802 :         resultRelInfo++;
    2133             :     }
    2134             : 
    2135             :     /*
    2136             :      * OK, truncate each table.
    2137             :      */
    2138        1688 :     mySubid = GetCurrentSubTransactionId();
    2139             : 
    2140        5490 :     foreach(cell, rels)
    2141             :     {
    2142        3802 :         Relation    rel = (Relation) lfirst(cell);
    2143             : 
    2144             :         /* Skip partitioned tables as there is nothing to do */
    2145        3802 :         if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
    2146         704 :             continue;
    2147             : 
    2148             :         /*
    2149             :          * Build the lists of foreign tables belonging to each foreign server
    2150             :          * and pass each list to the foreign data wrapper's callback function,
    2151             :          * so that each server can truncate its all foreign tables in bulk.
    2152             :          * Each list is saved as a single entry in a hash table that uses the
    2153             :          * server OID as lookup key.
    2154             :          */
    2155        3098 :         if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
    2156          34 :         {
    2157          34 :             Oid         serverid = GetForeignServerIdByRelId(RelationGetRelid(rel));
    2158             :             bool        found;
    2159             :             ForeignTruncateInfo *ft_info;
    2160             : 
    2161             :             /* First time through, initialize hashtable for foreign tables */
    2162          34 :             if (!ft_htab)
    2163             :             {
    2164             :                 HASHCTL     hctl;
    2165             : 
    2166          30 :                 memset(&hctl, 0, sizeof(HASHCTL));
    2167          30 :                 hctl.keysize = sizeof(Oid);
    2168          30 :                 hctl.entrysize = sizeof(ForeignTruncateInfo);
    2169          30 :                 hctl.hcxt = CurrentMemoryContext;
    2170             : 
    2171          30 :                 ft_htab = hash_create("TRUNCATE for Foreign Tables",
    2172             :                                       32,   /* start small and extend */
    2173             :                                       &hctl,
    2174             :                                       HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);
    2175             :             }
    2176             : 
    2177             :             /* Find or create cached entry for the foreign table */
    2178          34 :             ft_info = hash_search(ft_htab, &serverid, HASH_ENTER, &found);
    2179          34 :             if (!found)
    2180          30 :                 ft_info->rels = NIL;
    2181             : 
    2182             :             /*
    2183             :              * Save the foreign table in the entry of the server that the
    2184             :              * foreign table belongs to.
    2185             :              */
    2186          34 :             ft_info->rels = lappend(ft_info->rels, rel);
    2187          34 :             continue;
    2188             :         }
    2189             : 
    2190             :         /*
    2191             :          * Normally, we need a transaction-safe truncation here.  However, if
    2192             :          * the table was either created in the current (sub)transaction or has
    2193             :          * a new relfilenumber in the current (sub)transaction, then we can
    2194             :          * just truncate it in-place, because a rollback would cause the whole
    2195             :          * table or the current physical file to be thrown away anyway.
    2196             :          */
    2197        3064 :         if (rel->rd_createSubid == mySubid ||
    2198        3038 :             rel->rd_newRelfilelocatorSubid == mySubid)
    2199             :         {
    2200             :             /* Immediate, non-rollbackable truncation is OK */
    2201          90 :             heap_truncate_one_rel(rel);
    2202             :         }
    2203             :         else
    2204             :         {
    2205             :             Oid         heap_relid;
    2206             :             Oid         toast_relid;
    2207        2974 :             ReindexParams reindex_params = {0};
    2208             : 
    2209             :             /*
    2210             :              * This effectively deletes all rows in the table, and may be done
    2211             :              * in a serializable transaction.  In that case we must record a
    2212             :              * rw-conflict in to this transaction from each transaction
    2213             :              * holding a predicate lock on the table.
    2214             :              */
    2215        2974 :             CheckTableForSerializableConflictIn(rel);
    2216             : 
    2217             :             /*
    2218             :              * Need the full transaction-safe pushups.
    2219             :              *
    2220             :              * Create a new empty storage file for the relation, and assign it
    2221             :              * as the relfilenumber value. The old storage file is scheduled
    2222             :              * for deletion at commit.
    2223             :              */
    2224        2974 :             RelationSetNewRelfilenumber(rel, rel->rd_rel->relpersistence);
    2225             : 
    2226        2974 :             heap_relid = RelationGetRelid(rel);
    2227             : 
    2228             :             /*
    2229             :              * The same for the toast table, if any.
    2230             :              */
    2231        2974 :             toast_relid = rel->rd_rel->reltoastrelid;
    2232        2974 :             if (OidIsValid(toast_relid))
    2233             :             {
    2234        1784 :                 Relation    toastrel = relation_open(toast_relid,
    2235             :                                                      AccessExclusiveLock);
    2236             : 
    2237        1784 :                 RelationSetNewRelfilenumber(toastrel,
    2238        1784 :                                             toastrel->rd_rel->relpersistence);
    2239        1784 :                 table_close(toastrel, NoLock);
    2240             :             }
    2241             : 
    2242             :             /*
    2243             :              * Reconstruct the indexes to match, and we're done.
    2244             :              */
    2245        2974 :             reindex_relation(NULL, heap_relid, REINDEX_REL_PROCESS_TOAST,
    2246             :                              &reindex_params);
    2247             :         }
    2248             : 
    2249        3064 :         pgstat_count_truncate(rel);
    2250             :     }
    2251             : 
    2252             :     /* Now go through the hash table, and truncate foreign tables */
    2253        1688 :     if (ft_htab)
    2254             :     {
    2255             :         ForeignTruncateInfo *ft_info;
    2256             :         HASH_SEQ_STATUS seq;
    2257             : 
    2258          30 :         hash_seq_init(&seq, ft_htab);
    2259             : 
    2260          30 :         PG_TRY();
    2261             :         {
    2262          52 :             while ((ft_info = hash_seq_search(&seq)) != NULL)
    2263             :             {
    2264          30 :                 FdwRoutine *routine = GetFdwRoutineByServerId(ft_info->serverid);
    2265             : 
    2266             :                 /* truncate_check_rel() has checked that already */
    2267             :                 Assert(routine->ExecForeignTruncate != NULL);
    2268             : 
    2269          30 :                 routine->ExecForeignTruncate(ft_info->rels,
    2270             :                                              behavior,
    2271             :                                              restart_seqs);
    2272             :             }
    2273             :         }
    2274           8 :         PG_FINALLY();
    2275             :         {
    2276          30 :             hash_destroy(ft_htab);
    2277             :         }
    2278          30 :         PG_END_TRY();
    2279             :     }
    2280             : 
    2281             :     /*
    2282             :      * Restart owned sequences if we were asked to.
    2283             :      */
    2284        1714 :     foreach(cell, seq_relids)
    2285             :     {
    2286          34 :         Oid         seq_relid = lfirst_oid(cell);
    2287             : 
    2288          34 :         ResetSequence(seq_relid);
    2289             :     }
    2290             : 
    2291             :     /*
    2292             :      * Write a WAL record to allow this set of actions to be logically
    2293             :      * decoded.
    2294             :      *
    2295             :      * Assemble an array of relids so we can write a single WAL record for the
    2296             :      * whole action.
    2297             :      */
    2298        1680 :     if (relids_logged != NIL)
    2299             :     {
    2300             :         xl_heap_truncate xlrec;
    2301          62 :         int         i = 0;
    2302             : 
    2303             :         /* should only get here if effective_wal_level is 'logical' */
    2304             :         Assert(XLogLogicalInfoActive());
    2305             : 
    2306          62 :         logrelids = palloc(list_length(relids_logged) * sizeof(Oid));
    2307         160 :         foreach(cell, relids_logged)
    2308          98 :             logrelids[i++] = lfirst_oid(cell);
    2309             : 
    2310          62 :         xlrec.dbId = MyDatabaseId;
    2311          62 :         xlrec.nrelids = list_length(relids_logged);
    2312          62 :         xlrec.flags = 0;
    2313          62 :         if (behavior == DROP_CASCADE)
    2314           2 :             xlrec.flags |= XLH_TRUNCATE_CASCADE;
    2315          62 :         if (restart_seqs)
    2316           4 :             xlrec.flags |= XLH_TRUNCATE_RESTART_SEQS;
    2317             : 
    2318          62 :         XLogBeginInsert();
    2319          62 :         XLogRegisterData(&xlrec, SizeOfHeapTruncate);
    2320          62 :         XLogRegisterData(logrelids, list_length(relids_logged) * sizeof(Oid));
    2321             : 
    2322          62 :         XLogSetRecordFlags(XLOG_INCLUDE_ORIGIN);
    2323             : 
    2324          62 :         (void) XLogInsert(RM_HEAP_ID, XLOG_HEAP_TRUNCATE);
    2325             :     }
    2326             : 
    2327             :     /*
    2328             :      * Process all AFTER STATEMENT TRUNCATE triggers.
    2329             :      */
    2330        1680 :     resultRelInfo = resultRelInfos;
    2331        5474 :     foreach(cell, rels)
    2332             :     {
    2333             :         UserContext ucxt;
    2334             : 
    2335        3794 :         if (run_as_table_owner)
    2336          66 :             SwitchToUntrustedUser(resultRelInfo->ri_RelationDesc->rd_rel->relowner,
    2337             :                                   &ucxt);
    2338        3794 :         ExecASTruncateTriggers(estate, resultRelInfo);
    2339        3794 :         if (run_as_table_owner)
    2340          66 :             RestoreUserContext(&ucxt);
    2341        3794 :         resultRelInfo++;
    2342             :     }
    2343             : 
    2344             :     /* Handle queued AFTER triggers */
    2345        1680 :     AfterTriggerEndQuery(estate);
    2346             : 
    2347             :     /* We can clean up the EState now */
    2348        1680 :     FreeExecutorState(estate);
    2349             : 
    2350             :     /*
    2351             :      * Close any rels opened by CASCADE (can't do this while EState still
    2352             :      * holds refs)
    2353             :      */
    2354        1680 :     rels = list_difference_ptr(rels, explicit_rels);
    2355        1774 :     foreach(cell, rels)
    2356             :     {
    2357          94 :         Relation    rel = (Relation) lfirst(cell);
    2358             : 
    2359          94 :         table_close(rel, NoLock);
    2360             :     }
    2361        1680 : }
    2362             : 
    2363             : /*
    2364             :  * Check that a given relation is safe to truncate.  Subroutine for
    2365             :  * ExecuteTruncate() and RangeVarCallbackForTruncate().
    2366             :  */
    2367             : static void
    2368        4092 : truncate_check_rel(Oid relid, Form_pg_class reltuple)
    2369             : {
    2370        4092 :     char       *relname = NameStr(reltuple->relname);
    2371             : 
    2372             :     /*
    2373             :      * Only allow truncate on regular tables, foreign tables using foreign
    2374             :      * data wrappers supporting TRUNCATE and partitioned tables (although, the
    2375             :      * latter are only being included here for the following checks; no
    2376             :      * physical truncation will occur in their case.).
    2377             :      */
    2378        4092 :     if (reltuple->relkind == RELKIND_FOREIGN_TABLE)
    2379             :     {
    2380          38 :         Oid         serverid = GetForeignServerIdByRelId(relid);
    2381          38 :         FdwRoutine *fdwroutine = GetFdwRoutineByServerId(serverid);
    2382             : 
    2383          36 :         if (!fdwroutine->ExecForeignTruncate)
    2384           2 :             ereport(ERROR,
    2385             :                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    2386             :                      errmsg("cannot truncate foreign table \"%s\"",
    2387             :                             relname)));
    2388             :     }
    2389        4054 :     else if (reltuple->relkind != RELKIND_RELATION &&
    2390         720 :              reltuple->relkind != RELKIND_PARTITIONED_TABLE)
    2391           0 :         ereport(ERROR,
    2392             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    2393             :                  errmsg("\"%s\" is not a table", relname)));
    2394             : 
    2395             :     /*
    2396             :      * Most system catalogs can't be truncated at all, or at least not unless
    2397             :      * allow_system_table_mods=on. As an exception, however, we allow
    2398             :      * pg_largeobject and pg_largeobject_metadata to be truncated as part of
    2399             :      * pg_upgrade, because we need to change its relfilenode to match the old
    2400             :      * cluster, and allowing a TRUNCATE command to be executed is the easiest
    2401             :      * way of doing that.
    2402             :      */
    2403        4088 :     if (!allowSystemTableMods && IsSystemClass(relid, reltuple)
    2404         122 :         && (!IsBinaryUpgrade ||
    2405          60 :             (relid != LargeObjectRelationId &&
    2406             :              relid != LargeObjectMetadataRelationId)))
    2407           2 :         ereport(ERROR,
    2408             :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
    2409             :                  errmsg("permission denied: \"%s\" is a system catalog",
    2410             :                         relname)));
    2411             : 
    2412        4086 :     InvokeObjectTruncateHook(relid);
    2413        4086 : }
    2414             : 
    2415             : /*
    2416             :  * Check that current user has the permission to truncate given relation.
    2417             :  */
    2418             : static void
    2419        2290 : truncate_check_perms(Oid relid, Form_pg_class reltuple)
    2420             : {
    2421        2290 :     char       *relname = NameStr(reltuple->relname);
    2422             :     AclResult   aclresult;
    2423             : 
    2424             :     /* Permissions checks */
    2425        2290 :     aclresult = pg_class_aclcheck(relid, GetUserId(), ACL_TRUNCATE);
    2426        2290 :     if (aclresult != ACLCHECK_OK)
    2427          32 :         aclcheck_error(aclresult, get_relkind_objtype(reltuple->relkind),
    2428             :                        relname);
    2429        2258 : }
    2430             : 
    2431             : /*
    2432             :  * Set of extra sanity checks to check if a given relation is safe to
    2433             :  * truncate.  This is split with truncate_check_rel() as
    2434             :  * RangeVarCallbackForTruncate() cannot open a Relation yet.
    2435             :  */
    2436             : static void
    2437        3900 : truncate_check_activity(Relation rel)
    2438             : {
    2439             :     /*
    2440             :      * Don't allow truncate on temp tables of other backends ... their local
    2441             :      * buffer manager is not going to cope.
    2442             :      */
    2443        3900 :     if (RELATION_IS_OTHER_TEMP(rel))
    2444           0 :         ereport(ERROR,
    2445             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    2446             :                  errmsg("cannot truncate temporary tables of other sessions")));
    2447             : 
    2448             :     /*
    2449             :      * Also check for active uses of the relation in the current transaction,
    2450             :      * including open scans and pending AFTER trigger events.
    2451             :      */
    2452        3900 :     CheckTableNotInUse(rel, "TRUNCATE");
    2453        3894 : }
    2454             : 
    2455             : /*
    2456             :  * storage_name
    2457             :  *    returns the name corresponding to a typstorage/attstorage enum value
    2458             :  */
    2459             : static const char *
    2460          24 : storage_name(char c)
    2461             : {
    2462          24 :     switch (c)
    2463             :     {
    2464           0 :         case TYPSTORAGE_PLAIN:
    2465           0 :             return "PLAIN";
    2466           0 :         case TYPSTORAGE_EXTERNAL:
    2467           0 :             return "EXTERNAL";
    2468          12 :         case TYPSTORAGE_EXTENDED:
    2469          12 :             return "EXTENDED";
    2470          12 :         case TYPSTORAGE_MAIN:
    2471          12 :             return "MAIN";
    2472           0 :         default:
    2473           0 :             return "???";
    2474             :     }
    2475             : }
    2476             : 
    2477             : /*----------
    2478             :  * MergeAttributes
    2479             :  *      Returns new schema given initial schema and superclasses.
    2480             :  *
    2481             :  * Input arguments:
    2482             :  * 'columns' is the column/attribute definition for the table. (It's a list
    2483             :  *      of ColumnDef's.) It is destructively changed.
    2484             :  * 'supers' is a list of OIDs of parent relations, already locked by caller.
    2485             :  * 'relpersistence' is the persistence type of the table.
    2486             :  * 'is_partition' tells if the table is a partition.
    2487             :  *
    2488             :  * Output arguments:
    2489             :  * 'supconstr' receives a list of CookedConstraint representing
    2490             :  *      CHECK constraints belonging to parent relations, updated as
    2491             :  *      necessary to be valid for the child.
    2492             :  * 'supnotnulls' receives a list of CookedConstraint representing
    2493             :  *      not-null constraints based on those from parent relations.
    2494             :  *
    2495             :  * Return value:
    2496             :  * Completed schema list.
    2497             :  *
    2498             :  * Notes:
    2499             :  *    The order in which the attributes are inherited is very important.
    2500             :  *    Intuitively, the inherited attributes should come first. If a table
    2501             :  *    inherits from multiple parents, the order of those attributes are
    2502             :  *    according to the order of the parents specified in CREATE TABLE.
    2503             :  *
    2504             :  *    Here's an example:
    2505             :  *
    2506             :  *      create table person (name text, age int4, location point);
    2507             :  *      create table emp (salary int4, manager text) inherits(person);
    2508             :  *      create table student (gpa float8) inherits (person);
    2509             :  *      create table stud_emp (percent int4) inherits (emp, student);
    2510             :  *
    2511             :  *    The order of the attributes of stud_emp is:
    2512             :  *
    2513             :  *                          person {1:name, 2:age, 3:location}
    2514             :  *                          /    \
    2515             :  *             {6:gpa}  student   emp {4:salary, 5:manager}
    2516             :  *                          \    /
    2517             :  *                         stud_emp {7:percent}
    2518             :  *
    2519             :  *     If the same attribute name appears multiple times, then it appears
    2520             :  *     in the result table in the proper location for its first appearance.
    2521             :  *
    2522             :  *     Constraints (including not-null constraints) for the child table
    2523             :  *     are the union of all relevant constraints, from both the child schema
    2524             :  *     and parent tables.  In addition, in legacy inheritance, each column that
    2525             :  *     appears in a primary key in any of the parents also gets a NOT NULL
    2526             :  *     constraint (partitioning doesn't need this, because the PK itself gets
    2527             :  *     inherited.)
    2528             :  *
    2529             :  *     The default value for a child column is defined as:
    2530             :  *      (1) If the child schema specifies a default, that value is used.
    2531             :  *      (2) If neither the child nor any parent specifies a default, then
    2532             :  *          the column will not have a default.
    2533             :  *      (3) If conflicting defaults are inherited from different parents
    2534             :  *          (and not overridden by the child), an error is raised.
    2535             :  *      (4) Otherwise the inherited default is used.
    2536             :  *
    2537             :  *      Note that the default-value infrastructure is used for generated
    2538             :  *      columns' expressions too, so most of the preceding paragraph applies
    2539             :  *      to generation expressions too.  We insist that a child column be
    2540             :  *      generated if and only if its parent(s) are, but it need not have
    2541             :  *      the same generation expression.
    2542             :  *----------
    2543             :  */
    2544             : static List *
    2545       65222 : MergeAttributes(List *columns, const List *supers, char relpersistence,
    2546             :                 bool is_partition, List **supconstr, List **supnotnulls)
    2547             : {
    2548       65222 :     List       *inh_columns = NIL;
    2549       65222 :     List       *constraints = NIL;
    2550       65222 :     List       *nnconstraints = NIL;
    2551       65222 :     bool        have_bogus_defaults = false;
    2552             :     int         child_attno;
    2553             :     static Node bogus_marker = {0}; /* marks conflicting defaults */
    2554       65222 :     List       *saved_columns = NIL;
    2555             :     ListCell   *lc;
    2556             : 
    2557             :     /*
    2558             :      * Check for and reject tables with too many columns. We perform this
    2559             :      * check relatively early for two reasons: (a) we don't run the risk of
    2560             :      * overflowing an AttrNumber in subsequent code (b) an O(n^2) algorithm is
    2561             :      * okay if we're processing <= 1600 columns, but could take minutes to
    2562             :      * execute if the user attempts to create a table with hundreds of
    2563             :      * thousands of columns.
    2564             :      *
    2565             :      * Note that we also need to check that we do not exceed this figure after
    2566             :      * including columns from inherited relations.
    2567             :      */
    2568       65222 :     if (list_length(columns) > MaxHeapAttributeNumber)
    2569           0 :         ereport(ERROR,
    2570             :                 (errcode(ERRCODE_TOO_MANY_COLUMNS),
    2571             :                  errmsg("tables can have at most %d columns",
    2572             :                         MaxHeapAttributeNumber)));
    2573             : 
    2574             :     /*
    2575             :      * Check for duplicate names in the explicit list of attributes.
    2576             :      *
    2577             :      * Although we might consider merging such entries in the same way that we
    2578             :      * handle name conflicts for inherited attributes, it seems to make more
    2579             :      * sense to assume such conflicts are errors.
    2580             :      *
    2581             :      * We don't use foreach() here because we have two nested loops over the
    2582             :      * columns list, with possible element deletions in the inner one.  If we
    2583             :      * used foreach_delete_current() it could only fix up the state of one of
    2584             :      * the loops, so it seems cleaner to use looping over list indexes for
    2585             :      * both loops.  Note that any deletion will happen beyond where the outer
    2586             :      * loop is, so its index never needs adjustment.
    2587             :      */
    2588      307672 :     for (int coldefpos = 0; coldefpos < list_length(columns); coldefpos++)
    2589             :     {
    2590      242474 :         ColumnDef  *coldef = list_nth_node(ColumnDef, columns, coldefpos);
    2591             : 
    2592      242474 :         if (!is_partition && coldef->typeName == NULL)
    2593             :         {
    2594             :             /*
    2595             :              * Typed table column option that does not belong to a column from
    2596             :              * the type.  This works because the columns from the type come
    2597             :              * first in the list.  (We omit this check for partition column
    2598             :              * lists; those are processed separately below.)
    2599             :              */
    2600           6 :             ereport(ERROR,
    2601             :                     (errcode(ERRCODE_UNDEFINED_COLUMN),
    2602             :                      errmsg("column \"%s\" does not exist",
    2603             :                             coldef->colname)));
    2604             :         }
    2605             : 
    2606             :         /* restpos scans all entries beyond coldef; incr is in loop body */
    2607     6632378 :         for (int restpos = coldefpos + 1; restpos < list_length(columns);)
    2608             :         {
    2609     6389928 :             ColumnDef  *restdef = list_nth_node(ColumnDef, columns, restpos);
    2610             : 
    2611     6389928 :             if (strcmp(coldef->colname, restdef->colname) == 0)
    2612             :             {
    2613          50 :                 if (coldef->is_from_type)
    2614             :                 {
    2615             :                     /*
    2616             :                      * merge the column options into the column from the type
    2617             :                      */
    2618          32 :                     coldef->is_not_null = restdef->is_not_null;
    2619          32 :                     coldef->raw_default = restdef->raw_default;
    2620          32 :                     coldef->cooked_default = restdef->cooked_default;
    2621          32 :                     coldef->constraints = restdef->constraints;
    2622          32 :                     coldef->is_from_type = false;
    2623          32 :                     columns = list_delete_nth_cell(columns, restpos);
    2624             :                 }
    2625             :                 else
    2626          18 :                     ereport(ERROR,
    2627             :                             (errcode(ERRCODE_DUPLICATE_COLUMN),
    2628             :                              errmsg("column \"%s\" specified more than once",
    2629             :                                     coldef->colname)));
    2630             :             }
    2631             :             else
    2632     6389878 :                 restpos++;
    2633             :         }
    2634             :     }
    2635             : 
    2636             :     /*
    2637             :      * In case of a partition, there are no new column definitions, only dummy
    2638             :      * ColumnDefs created for column constraints.  Set them aside for now and
    2639             :      * process them at the end.
    2640             :      */
    2641       65198 :     if (is_partition)
    2642             :     {
    2643        9120 :         saved_columns = columns;
    2644        9120 :         columns = NIL;
    2645             :     }
    2646             : 
    2647             :     /*
    2648             :      * Scan the parents left-to-right, and merge their attributes to form a
    2649             :      * list of inherited columns (inh_columns).
    2650             :      */
    2651       65198 :     child_attno = 0;
    2652       76748 :     foreach(lc, supers)
    2653             :     {
    2654       11634 :         Oid         parent = lfirst_oid(lc);
    2655             :         Relation    relation;
    2656             :         TupleDesc   tupleDesc;
    2657             :         TupleConstr *constr;
    2658             :         AttrMap    *newattmap;
    2659             :         List       *inherited_defaults;
    2660             :         List       *cols_with_defaults;
    2661             :         List       *nnconstrs;
    2662             :         ListCell   *lc1;
    2663             :         ListCell   *lc2;
    2664       11634 :         Bitmapset  *nncols = NULL;
    2665             : 
    2666             :         /* caller already got lock */
    2667       11634 :         relation = table_open(parent, NoLock);
    2668             : 
    2669             :         /*
    2670             :          * Check for active uses of the parent partitioned table in the
    2671             :          * current transaction, such as being used in some manner by an
    2672             :          * enclosing command.
    2673             :          */
    2674       11634 :         if (is_partition)
    2675        9120 :             CheckTableNotInUse(relation, "CREATE TABLE .. PARTITION OF");
    2676             : 
    2677             :         /*
    2678             :          * We do not allow partitioned tables and partitions to participate in
    2679             :          * regular inheritance.
    2680             :          */
    2681       11628 :         if (relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && !is_partition)
    2682           6 :             ereport(ERROR,
    2683             :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    2684             :                      errmsg("cannot inherit from partitioned table \"%s\"",
    2685             :                             RelationGetRelationName(relation))));
    2686       11622 :         if (relation->rd_rel->relispartition && !is_partition)
    2687           6 :             ereport(ERROR,
    2688             :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    2689             :                      errmsg("cannot inherit from partition \"%s\"",
    2690             :                             RelationGetRelationName(relation))));
    2691             : 
    2692       11616 :         if (relation->rd_rel->relkind != RELKIND_RELATION &&
    2693        9116 :             relation->rd_rel->relkind != RELKIND_FOREIGN_TABLE &&
    2694        9096 :             relation->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
    2695           0 :             ereport(ERROR,
    2696             :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    2697             :                      errmsg("inherited relation \"%s\" is not a table or foreign table",
    2698             :                             RelationGetRelationName(relation))));
    2699             : 
    2700             :         /*
    2701             :          * If the parent is permanent, so must be all of its partitions.  Note
    2702             :          * that inheritance allows that case.
    2703             :          */
    2704       11616 :         if (is_partition &&
    2705        9114 :             relation->rd_rel->relpersistence != RELPERSISTENCE_TEMP &&
    2706             :             relpersistence == RELPERSISTENCE_TEMP)
    2707           6 :             ereport(ERROR,
    2708             :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    2709             :                      errmsg("cannot create a temporary relation as partition of permanent relation \"%s\"",
    2710             :                             RelationGetRelationName(relation))));
    2711             : 
    2712             :         /* Permanent rels cannot inherit from temporary ones */
    2713       11610 :         if (relpersistence != RELPERSISTENCE_TEMP &&
    2714       11196 :             relation->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
    2715          24 :             ereport(ERROR,
    2716             :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    2717             :                      errmsg(!is_partition
    2718             :                             ? "cannot inherit from temporary relation \"%s\""
    2719             :                             : "cannot create a permanent relation as partition of temporary relation \"%s\"",
    2720             :                             RelationGetRelationName(relation))));
    2721             : 
    2722             :         /* If existing rel is temp, it must belong to this session */
    2723       11586 :         if (RELATION_IS_OTHER_TEMP(relation))
    2724           0 :             ereport(ERROR,
    2725             :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    2726             :                      errmsg(!is_partition
    2727             :                             ? "cannot inherit from temporary relation of another session"
    2728             :                             : "cannot create as partition of temporary relation of another session")));
    2729             : 
    2730             :         /*
    2731             :          * We should have an UNDER permission flag for this, but for now,
    2732             :          * demand that creator of a child table own the parent.
    2733             :          */
    2734       11586 :         if (!object_ownercheck(RelationRelationId, RelationGetRelid(relation), GetUserId()))
    2735           0 :             aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(relation->rd_rel->relkind),
    2736           0 :                            RelationGetRelationName(relation));
    2737             : 
    2738       11586 :         tupleDesc = RelationGetDescr(relation);
    2739       11586 :         constr = tupleDesc->constr;
    2740             : 
    2741             :         /*
    2742             :          * newattmap->attnums[] will contain the child-table attribute numbers
    2743             :          * for the attributes of this parent table.  (They are not the same
    2744             :          * for parents after the first one, nor if we have dropped columns.)
    2745             :          */
    2746       11586 :         newattmap = make_attrmap(tupleDesc->natts);
    2747             : 
    2748             :         /* We can't process inherited defaults until newattmap is complete. */
    2749       11586 :         inherited_defaults = cols_with_defaults = NIL;
    2750             : 
    2751             :         /*
    2752             :          * Request attnotnull on columns that have a not-null constraint
    2753             :          * that's not marked NO INHERIT (even if not valid).
    2754             :          */
    2755       11586 :         nnconstrs = RelationGetNotNullConstraints(RelationGetRelid(relation),
    2756             :                                                   true, false);
    2757       25788 :         foreach_ptr(CookedConstraint, cc, nnconstrs)
    2758        2616 :             nncols = bms_add_member(nncols, cc->attnum);
    2759             : 
    2760       35628 :         for (AttrNumber parent_attno = 1; parent_attno <= tupleDesc->natts;
    2761       24042 :              parent_attno++)
    2762             :         {
    2763       24078 :             Form_pg_attribute attribute = TupleDescAttr(tupleDesc,
    2764             :                                                         parent_attno - 1);
    2765       24078 :             char       *attributeName = NameStr(attribute->attname);
    2766             :             int         exist_attno;
    2767             :             ColumnDef  *newdef;
    2768             :             ColumnDef  *mergeddef;
    2769             : 
    2770             :             /*
    2771             :              * Ignore dropped columns in the parent.
    2772             :              */
    2773       24078 :             if (attribute->attisdropped)
    2774         198 :                 continue;       /* leave newattmap->attnums entry as zero */
    2775             : 
    2776             :             /*
    2777             :              * Create new column definition
    2778             :              */
    2779       23880 :             newdef = makeColumnDef(attributeName, attribute->atttypid,
    2780             :                                    attribute->atttypmod, attribute->attcollation);
    2781       23880 :             newdef->storage = attribute->attstorage;
    2782       23880 :             newdef->generated = attribute->attgenerated;
    2783       23880 :             if (CompressionMethodIsValid(attribute->attcompression))
    2784          36 :                 newdef->compression =
    2785          36 :                     pstrdup(GetCompressionMethodName(attribute->attcompression));
    2786             : 
    2787             :             /*
    2788             :              * Regular inheritance children are independent enough not to
    2789             :              * inherit identity columns.  But partitions are integral part of
    2790             :              * a partitioned table and inherit identity column.
    2791             :              */
    2792       23880 :             if (is_partition)
    2793       19270 :                 newdef->identity = attribute->attidentity;
    2794             : 
    2795             :             /*
    2796             :              * Does it match some previously considered column from another
    2797             :              * parent?
    2798             :              */
    2799       23880 :             exist_attno = findAttrByName(attributeName, inh_columns);
    2800       23880 :             if (exist_attno > 0)
    2801             :             {
    2802             :                 /*
    2803             :                  * Yes, try to merge the two column definitions.
    2804             :                  */
    2805         370 :                 mergeddef = MergeInheritedAttribute(inh_columns, exist_attno, newdef);
    2806             : 
    2807         334 :                 newattmap->attnums[parent_attno - 1] = exist_attno;
    2808             : 
    2809             :                 /*
    2810             :                  * Partitions have only one parent, so conflict should never
    2811             :                  * occur.
    2812             :                  */
    2813             :                 Assert(!is_partition);
    2814             :             }
    2815             :             else
    2816             :             {
    2817             :                 /*
    2818             :                  * No, create a new inherited column
    2819             :                  */
    2820       23510 :                 newdef->inhcount = 1;
    2821       23510 :                 newdef->is_local = false;
    2822       23510 :                 inh_columns = lappend(inh_columns, newdef);
    2823             : 
    2824       23510 :                 newattmap->attnums[parent_attno - 1] = ++child_attno;
    2825       23510 :                 mergeddef = newdef;
    2826             :             }
    2827             : 
    2828             :             /*
    2829             :              * mark attnotnull if parent has it
    2830             :              */
    2831       23844 :             if (bms_is_member(parent_attno, nncols))
    2832        2616 :                 mergeddef->is_not_null = true;
    2833             : 
    2834             :             /*
    2835             :              * Locate default/generation expression if any
    2836             :              */
    2837       23844 :             if (attribute->atthasdef)
    2838             :             {
    2839             :                 Node       *this_default;
    2840             : 
    2841         854 :                 this_default = TupleDescGetDefault(tupleDesc, parent_attno);
    2842         854 :                 if (this_default == NULL)
    2843           0 :                     elog(ERROR, "default expression not found for attribute %d of relation \"%s\"",
    2844             :                          parent_attno, RelationGetRelationName(relation));
    2845             : 
    2846             :                 /*
    2847             :                  * If it's a GENERATED default, it might contain Vars that
    2848             :                  * need to be mapped to the inherited column(s)' new numbers.
    2849             :                  * We can't do that till newattmap is ready, so just remember
    2850             :                  * all the inherited default expressions for the moment.
    2851             :                  */
    2852         854 :                 inherited_defaults = lappend(inherited_defaults, this_default);
    2853         854 :                 cols_with_defaults = lappend(cols_with_defaults, mergeddef);
    2854             :             }
    2855             :         }
    2856             : 
    2857             :         /*
    2858             :          * Now process any inherited default expressions, adjusting attnos
    2859             :          * using the completed newattmap map.
    2860             :          */
    2861       12404 :         forboth(lc1, inherited_defaults, lc2, cols_with_defaults)
    2862             :         {
    2863         854 :             Node       *this_default = (Node *) lfirst(lc1);
    2864         854 :             ColumnDef  *def = (ColumnDef *) lfirst(lc2);
    2865             :             bool        found_whole_row;
    2866             : 
    2867             :             /* Adjust Vars to match new table's column numbering */
    2868         854 :             this_default = map_variable_attnos(this_default,
    2869             :                                                1, 0,
    2870             :                                                newattmap,
    2871             :                                                InvalidOid, &found_whole_row);
    2872             : 
    2873             :             /*
    2874             :              * For the moment we have to reject whole-row variables.  We could
    2875             :              * convert them, if we knew the new table's rowtype OID, but that
    2876             :              * hasn't been assigned yet.  (A variable could only appear in a
    2877             :              * generation expression, so the error message is correct.)
    2878             :              */
    2879         854 :             if (found_whole_row)
    2880           0 :                 ereport(ERROR,
    2881             :                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    2882             :                          errmsg("cannot convert whole-row table reference"),
    2883             :                          errdetail("Generation expression for column \"%s\" contains a whole-row reference to table \"%s\".",
    2884             :                                    def->colname,
    2885             :                                    RelationGetRelationName(relation))));
    2886             : 
    2887             :             /*
    2888             :              * If we already had a default from some prior parent, check to
    2889             :              * see if they are the same.  If so, no problem; if not, mark the
    2890             :              * column as having a bogus default.  Below, we will complain if
    2891             :              * the bogus default isn't overridden by the child columns.
    2892             :              */
    2893             :             Assert(def->raw_default == NULL);
    2894         854 :             if (def->cooked_default == NULL)
    2895         812 :                 def->cooked_default = this_default;
    2896          42 :             else if (!equal(def->cooked_default, this_default))
    2897             :             {
    2898          36 :                 def->cooked_default = &bogus_marker;
    2899          36 :                 have_bogus_defaults = true;
    2900             :             }
    2901             :         }
    2902             : 
    2903             :         /*
    2904             :          * Now copy the CHECK constraints of this parent, adjusting attnos
    2905             :          * using the completed newattmap map.  Identically named constraints
    2906             :          * are merged if possible, else we throw error.
    2907             :          */
    2908       11550 :         if (constr && constr->num_check > 0)
    2909             :         {
    2910         352 :             ConstrCheck *check = constr->check;
    2911             : 
    2912        1106 :             for (int i = 0; i < constr->num_check; i++)
    2913             :             {
    2914         754 :                 char       *name = check[i].ccname;
    2915             :                 Node       *expr;
    2916             :                 bool        found_whole_row;
    2917             : 
    2918             :                 /* ignore if the constraint is non-inheritable */
    2919         754 :                 if (check[i].ccnoinherit)
    2920          48 :                     continue;
    2921             : 
    2922             :                 /* Adjust Vars to match new table's column numbering */
    2923         706 :                 expr = map_variable_attnos(stringToNode(check[i].ccbin),
    2924             :                                            1, 0,
    2925             :                                            newattmap,
    2926             :                                            InvalidOid, &found_whole_row);
    2927             : 
    2928             :                 /*
    2929             :                  * For the moment we have to reject whole-row variables. We
    2930             :                  * could convert them, if we knew the new table's rowtype OID,
    2931             :                  * but that hasn't been assigned yet.
    2932             :                  */
    2933         706 :                 if (found_whole_row)
    2934           0 :                     ereport(ERROR,
    2935             :                             (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    2936             :                              errmsg("cannot convert whole-row table reference"),
    2937             :                              errdetail("Constraint \"%s\" contains a whole-row reference to table \"%s\".",
    2938             :                                        name,
    2939             :                                        RelationGetRelationName(relation))));
    2940             : 
    2941         706 :                 constraints = MergeCheckConstraint(constraints, name, expr,
    2942         706 :                                                    check[i].ccenforced);
    2943             :             }
    2944             :         }
    2945             : 
    2946             :         /*
    2947             :          * Also copy the not-null constraints from this parent.  The
    2948             :          * attnotnull markings were already installed above.
    2949             :          */
    2950       25716 :         foreach_ptr(CookedConstraint, nn, nnconstrs)
    2951             :         {
    2952             :             Assert(nn->contype == CONSTR_NOTNULL);
    2953             : 
    2954        2616 :             nn->attnum = newattmap->attnums[nn->attnum - 1];
    2955             : 
    2956        2616 :             nnconstraints = lappend(nnconstraints, nn);
    2957             :         }
    2958             : 
    2959       11550 :         free_attrmap(newattmap);
    2960             : 
    2961             :         /*
    2962             :          * Close the parent rel, but keep our lock on it until xact commit.
    2963             :          * That will prevent someone else from deleting or ALTERing the parent
    2964             :          * before the child is committed.
    2965             :          */
    2966       11550 :         table_close(relation, NoLock);
    2967             :     }
    2968             : 
    2969             :     /*
    2970             :      * If we had no inherited attributes, the result columns are just the
    2971             :      * explicitly declared columns.  Otherwise, we need to merge the declared
    2972             :      * columns into the inherited column list.  Although, we never have any
    2973             :      * explicitly declared columns if the table is a partition.
    2974             :      */
    2975       65114 :     if (inh_columns != NIL)
    2976             :     {
    2977       11114 :         int         newcol_attno = 0;
    2978             : 
    2979       12088 :         foreach(lc, columns)
    2980             :         {
    2981        1052 :             ColumnDef  *newdef = lfirst_node(ColumnDef, lc);
    2982        1052 :             char       *attributeName = newdef->colname;
    2983             :             int         exist_attno;
    2984             : 
    2985             :             /*
    2986             :              * Partitions have only one parent and have no column definitions
    2987             :              * of their own, so conflict should never occur.
    2988             :              */
    2989             :             Assert(!is_partition);
    2990             : 
    2991        1052 :             newcol_attno++;
    2992             : 
    2993             :             /*
    2994             :              * Does it match some inherited column?
    2995             :              */
    2996        1052 :             exist_attno = findAttrByName(attributeName, inh_columns);
    2997        1052 :             if (exist_attno > 0)
    2998             :             {
    2999             :                 /*
    3000             :                  * Yes, try to merge the two column definitions.
    3001             :                  */
    3002         380 :                 MergeChildAttribute(inh_columns, exist_attno, newcol_attno, newdef);
    3003             :             }
    3004             :             else
    3005             :             {
    3006             :                 /*
    3007             :                  * No, attach new column unchanged to result columns.
    3008             :                  */
    3009         672 :                 inh_columns = lappend(inh_columns, newdef);
    3010             :             }
    3011             :         }
    3012             : 
    3013       11036 :         columns = inh_columns;
    3014             : 
    3015             :         /*
    3016             :          * Check that we haven't exceeded the legal # of columns after merging
    3017             :          * in inherited columns.
    3018             :          */
    3019       11036 :         if (list_length(columns) > MaxHeapAttributeNumber)
    3020           0 :             ereport(ERROR,
    3021             :                     (errcode(ERRCODE_TOO_MANY_COLUMNS),
    3022             :                      errmsg("tables can have at most %d columns",
    3023             :                             MaxHeapAttributeNumber)));
    3024             :     }
    3025             : 
    3026             :     /*
    3027             :      * Now that we have the column definition list for a partition, we can
    3028             :      * check whether the columns referenced in the column constraint specs
    3029             :      * actually exist.  Also, merge column defaults.
    3030             :      */
    3031       65036 :     if (is_partition)
    3032             :     {
    3033        9308 :         foreach(lc, saved_columns)
    3034             :         {
    3035         254 :             ColumnDef  *restdef = lfirst(lc);
    3036         254 :             bool        found = false;
    3037             :             ListCell   *l;
    3038             : 
    3039         960 :             foreach(l, columns)
    3040             :             {
    3041         742 :                 ColumnDef  *coldef = lfirst(l);
    3042             : 
    3043         742 :                 if (strcmp(coldef->colname, restdef->colname) == 0)
    3044             :                 {
    3045         254 :                     found = true;
    3046             : 
    3047             :                     /*
    3048             :                      * Check for conflicts related to generated columns.
    3049             :                      *
    3050             :                      * Same rules as above: generated-ness has to match the
    3051             :                      * parent, but the contents of the generation expression
    3052             :                      * can be different.
    3053             :                      */
    3054         254 :                     if (coldef->generated)
    3055             :                     {
    3056         146 :                         if (restdef->raw_default && !restdef->generated)
    3057          12 :                             ereport(ERROR,
    3058             :                                     (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
    3059             :                                      errmsg("column \"%s\" inherits from generated column but specifies default",
    3060             :                                             restdef->colname)));
    3061         134 :                         if (restdef->identity)
    3062           0 :                             ereport(ERROR,
    3063             :                                     (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
    3064             :                                      errmsg("column \"%s\" inherits from generated column but specifies identity",
    3065             :                                             restdef->colname)));
    3066             :                     }
    3067             :                     else
    3068             :                     {
    3069         108 :                         if (restdef->generated)
    3070          12 :                             ereport(ERROR,
    3071             :                                     (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
    3072             :                                      errmsg("child column \"%s\" specifies generation expression",
    3073             :                                             restdef->colname),
    3074             :                                      errhint("A child table column cannot be generated unless its parent column is.")));
    3075             :                     }
    3076             : 
    3077         230 :                     if (coldef->generated && restdef->generated && coldef->generated != restdef->generated)
    3078          12 :                         ereport(ERROR,
    3079             :                                 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
    3080             :                                  errmsg("column \"%s\" inherits from generated column of different kind",
    3081             :                                         restdef->colname),
    3082             :                                  errdetail("Parent column is %s, child column is %s.",
    3083             :                                            coldef->generated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL",
    3084             :                                            restdef->generated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL")));
    3085             : 
    3086             :                     /*
    3087             :                      * Override the parent's default value for this column
    3088             :                      * (coldef->cooked_default) with the partition's local
    3089             :                      * definition (restdef->raw_default), if there's one. It
    3090             :                      * should be physically impossible to get a cooked default
    3091             :                      * in the local definition or a raw default in the
    3092             :                      * inherited definition, but make sure they're nulls, for
    3093             :                      * future-proofing.
    3094             :                      */
    3095             :                     Assert(restdef->cooked_default == NULL);
    3096             :                     Assert(coldef->raw_default == NULL);
    3097         218 :                     if (restdef->raw_default)
    3098             :                     {
    3099         146 :                         coldef->raw_default = restdef->raw_default;
    3100         146 :                         coldef->cooked_default = NULL;
    3101             :                     }
    3102             :                 }
    3103             :             }
    3104             : 
    3105             :             /* complain for constraints on columns not in parent */
    3106         218 :             if (!found)
    3107           0 :                 ereport(ERROR,
    3108             :                         (errcode(ERRCODE_UNDEFINED_COLUMN),
    3109             :                          errmsg("column \"%s\" does not exist",
    3110             :                                 restdef->colname)));
    3111             :         }
    3112             :     }
    3113             : 
    3114             :     /*
    3115             :      * If we found any conflicting parent default values, check to make sure
    3116             :      * they were overridden by the child.
    3117             :      */
    3118       65000 :     if (have_bogus_defaults)
    3119             :     {
    3120          90 :         foreach(lc, columns)
    3121             :         {
    3122          72 :             ColumnDef  *def = lfirst(lc);
    3123             : 
    3124          72 :             if (def->cooked_default == &bogus_marker)
    3125             :             {
    3126          18 :                 if (def->generated)
    3127          12 :                     ereport(ERROR,
    3128             :                             (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
    3129             :                              errmsg("column \"%s\" inherits conflicting generation expressions",
    3130             :                                     def->colname),
    3131             :                              errhint("To resolve the conflict, specify a generation expression explicitly.")));
    3132             :                 else
    3133           6 :                     ereport(ERROR,
    3134             :                             (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
    3135             :                              errmsg("column \"%s\" inherits conflicting default values",
    3136             :                                     def->colname),
    3137             :                              errhint("To resolve the conflict, specify a default explicitly.")));
    3138             :             }
    3139             :         }
    3140             :     }
    3141             : 
    3142       64982 :     *supconstr = constraints;
    3143       64982 :     *supnotnulls = nnconstraints;
    3144             : 
    3145       64982 :     return columns;
    3146             : }
    3147             : 
    3148             : 
    3149             : /*
    3150             :  * MergeCheckConstraint
    3151             :  *      Try to merge an inherited CHECK constraint with previous ones
    3152             :  *
    3153             :  * If we inherit identically-named constraints from multiple parents, we must
    3154             :  * merge them, or throw an error if they don't have identical definitions.
    3155             :  *
    3156             :  * constraints is a list of CookedConstraint structs for previous constraints.
    3157             :  *
    3158             :  * If the new constraint matches an existing one, then the existing
    3159             :  * constraint's inheritance count is updated.  If there is a conflict (same
    3160             :  * name but different expression), throw an error.  If the constraint neither
    3161             :  * matches nor conflicts with an existing one, a new constraint is appended to
    3162             :  * the list.
    3163             :  */
    3164             : static List *
    3165         706 : MergeCheckConstraint(List *constraints, const char *name, Node *expr, bool is_enforced)
    3166             : {
    3167             :     ListCell   *lc;
    3168             :     CookedConstraint *newcon;
    3169             : 
    3170        2230 :     foreach(lc, constraints)
    3171             :     {
    3172        1674 :         CookedConstraint *ccon = (CookedConstraint *) lfirst(lc);
    3173             : 
    3174             :         Assert(ccon->contype == CONSTR_CHECK);
    3175             : 
    3176             :         /* Non-matching names never conflict */
    3177        1674 :         if (strcmp(ccon->name, name) != 0)
    3178        1524 :             continue;
    3179             : 
    3180         150 :         if (equal(expr, ccon->expr))
    3181             :         {
    3182             :             /* OK to merge constraint with existing */
    3183         150 :             if (pg_add_s16_overflow(ccon->inhcount, 1,
    3184             :                                     &ccon->inhcount))
    3185           0 :                 ereport(ERROR,
    3186             :                         errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
    3187             :                         errmsg("too many inheritance parents"));
    3188             : 
    3189             :             /*
    3190             :              * When enforceability differs, the merged constraint should be
    3191             :              * marked as ENFORCED because one of the parents is ENFORCED.
    3192             :              */
    3193         150 :             if (!ccon->is_enforced && is_enforced)
    3194             :             {
    3195          48 :                 ccon->is_enforced = true;
    3196          48 :                 ccon->skip_validation = false;
    3197             :             }
    3198             : 
    3199         150 :             return constraints;
    3200             :         }
    3201             : 
    3202           0 :         ereport(ERROR,
    3203             :                 (errcode(ERRCODE_DUPLICATE_OBJECT),
    3204             :                  errmsg("check constraint name \"%s\" appears multiple times but with different expressions",
    3205             :                         name)));
    3206             :     }
    3207             : 
    3208             :     /*
    3209             :      * Constraint couldn't be merged with an existing one and also didn't
    3210             :      * conflict with an existing one, so add it as a new one to the list.
    3211             :      */
    3212         556 :     newcon = palloc0_object(CookedConstraint);
    3213         556 :     newcon->contype = CONSTR_CHECK;
    3214         556 :     newcon->name = pstrdup(name);
    3215         556 :     newcon->expr = expr;
    3216         556 :     newcon->inhcount = 1;
    3217         556 :     newcon->is_enforced = is_enforced;
    3218         556 :     newcon->skip_validation = !is_enforced;
    3219         556 :     return lappend(constraints, newcon);
    3220             : }
    3221             : 
    3222             : /*
    3223             :  * MergeChildAttribute
    3224             :  *      Merge given child attribute definition into given inherited attribute.
    3225             :  *
    3226             :  * Input arguments:
    3227             :  * 'inh_columns' is the list of inherited ColumnDefs.
    3228             :  * 'exist_attno' is the number of the inherited attribute in inh_columns
    3229             :  * 'newcol_attno' is the attribute number in child table's schema definition
    3230             :  * 'newdef' is the column/attribute definition from the child table.
    3231             :  *
    3232             :  * The ColumnDef in 'inh_columns' list is modified.  The child attribute's
    3233             :  * ColumnDef remains unchanged.
    3234             :  *
    3235             :  * Notes:
    3236             :  * - The attribute is merged according to the rules laid out in the prologue
    3237             :  *   of MergeAttributes().
    3238             :  * - If matching inherited attribute exists but the child attribute can not be
    3239             :  *   merged into it, the function throws respective errors.
    3240             :  * - A partition can not have its own column definitions. Hence this function
    3241             :  *   is applicable only to a regular inheritance child.
    3242             :  */
    3243             : static void
    3244         380 : MergeChildAttribute(List *inh_columns, int exist_attno, int newcol_attno, const ColumnDef *newdef)
    3245             : {
    3246         380 :     char       *attributeName = newdef->colname;
    3247             :     ColumnDef  *inhdef;
    3248             :     Oid         inhtypeid,
    3249             :                 newtypeid;
    3250             :     int32       inhtypmod,
    3251             :                 newtypmod;
    3252             :     Oid         inhcollid,
    3253             :                 newcollid;
    3254             : 
    3255         380 :     if (exist_attno == newcol_attno)
    3256         346 :         ereport(NOTICE,
    3257             :                 (errmsg("merging column \"%s\" with inherited definition",
    3258             :                         attributeName)));
    3259             :     else
    3260          34 :         ereport(NOTICE,
    3261             :                 (errmsg("moving and merging column \"%s\" with inherited definition", attributeName),
    3262             :                  errdetail("User-specified column moved to the position of the inherited column.")));
    3263             : 
    3264         380 :     inhdef = list_nth_node(ColumnDef, inh_columns, exist_attno - 1);
    3265             : 
    3266             :     /*
    3267             :      * Must have the same type and typmod
    3268             :      */
    3269         380 :     typenameTypeIdAndMod(NULL, inhdef->typeName, &inhtypeid, &inhtypmod);
    3270         380 :     typenameTypeIdAndMod(NULL, newdef->typeName, &newtypeid, &newtypmod);
    3271         380 :     if (inhtypeid != newtypeid || inhtypmod != newtypmod)
    3272          12 :         ereport(ERROR,
    3273             :                 (errcode(ERRCODE_DATATYPE_MISMATCH),
    3274             :                  errmsg("column \"%s\" has a type conflict",
    3275             :                         attributeName),
    3276             :                  errdetail("%s versus %s",
    3277             :                            format_type_with_typemod(inhtypeid, inhtypmod),
    3278             :                            format_type_with_typemod(newtypeid, newtypmod))));
    3279             : 
    3280             :     /*
    3281             :      * Must have the same collation
    3282             :      */
    3283         368 :     inhcollid = GetColumnDefCollation(NULL, inhdef, inhtypeid);
    3284         368 :     newcollid = GetColumnDefCollation(NULL, newdef, newtypeid);
    3285         368 :     if (inhcollid != newcollid)
    3286           6 :         ereport(ERROR,
    3287             :                 (errcode(ERRCODE_COLLATION_MISMATCH),
    3288             :                  errmsg("column \"%s\" has a collation conflict",
    3289             :                         attributeName),
    3290             :                  errdetail("\"%s\" versus \"%s\"",
    3291             :                            get_collation_name(inhcollid),
    3292             :                            get_collation_name(newcollid))));
    3293             : 
    3294             :     /*
    3295             :      * Identity is never inherited by a regular inheritance child. Pick
    3296             :      * child's identity definition if there's one.
    3297             :      */
    3298         362 :     inhdef->identity = newdef->identity;
    3299             : 
    3300             :     /*
    3301             :      * Copy storage parameter
    3302             :      */
    3303         362 :     if (inhdef->storage == 0)
    3304           0 :         inhdef->storage = newdef->storage;
    3305         362 :     else if (newdef->storage != 0 && inhdef->storage != newdef->storage)
    3306           6 :         ereport(ERROR,
    3307             :                 (errcode(ERRCODE_DATATYPE_MISMATCH),
    3308             :                  errmsg("column \"%s\" has a storage parameter conflict",
    3309             :                         attributeName),
    3310             :                  errdetail("%s versus %s",
    3311             :                            storage_name(inhdef->storage),
    3312             :                            storage_name(newdef->storage))));
    3313             : 
    3314             :     /*
    3315             :      * Copy compression parameter
    3316             :      */
    3317         356 :     if (inhdef->compression == NULL)
    3318         350 :         inhdef->compression = newdef->compression;
    3319           6 :     else if (newdef->compression != NULL)
    3320             :     {
    3321           6 :         if (strcmp(inhdef->compression, newdef->compression) != 0)
    3322           6 :             ereport(ERROR,
    3323             :                     (errcode(ERRCODE_DATATYPE_MISMATCH),
    3324             :                      errmsg("column \"%s\" has a compression method conflict",
    3325             :                             attributeName),
    3326             :                      errdetail("%s versus %s", inhdef->compression, newdef->compression)));
    3327             :     }
    3328             : 
    3329             :     /*
    3330             :      * Merge of not-null constraints = OR 'em together
    3331             :      */
    3332         350 :     inhdef->is_not_null |= newdef->is_not_null;
    3333             : 
    3334             :     /*
    3335             :      * Check for conflicts related to generated columns.
    3336             :      *
    3337             :      * If the parent column is generated, the child column will be made a
    3338             :      * generated column if it isn't already.  If it is a generated column,
    3339             :      * we'll take its generation expression in preference to the parent's.  We
    3340             :      * must check that the child column doesn't specify a default value or
    3341             :      * identity, which matches the rules for a single column in
    3342             :      * parse_utilcmd.c.
    3343             :      *
    3344             :      * Conversely, if the parent column is not generated, the child column
    3345             :      * can't be either.  (We used to allow that, but it results in being able
    3346             :      * to override the generation expression via UPDATEs through the parent.)
    3347             :      */
    3348         350 :     if (inhdef->generated)
    3349             :     {
    3350          62 :         if (newdef->raw_default && !newdef->generated)
    3351          12 :             ereport(ERROR,
    3352             :                     (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
    3353             :                      errmsg("column \"%s\" inherits from generated column but specifies default",
    3354             :                             inhdef->colname)));
    3355          50 :         if (newdef->identity)
    3356          12 :             ereport(ERROR,
    3357             :                     (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
    3358             :                      errmsg("column \"%s\" inherits from generated column but specifies identity",
    3359             :                             inhdef->colname)));
    3360             :     }
    3361             :     else
    3362             :     {
    3363         288 :         if (newdef->generated)
    3364          12 :             ereport(ERROR,
    3365             :                     (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
    3366             :                      errmsg("child column \"%s\" specifies generation expression",
    3367             :                             inhdef->colname),
    3368             :                      errhint("A child table column cannot be generated unless its parent column is.")));
    3369             :     }
    3370             : 
    3371         314 :     if (inhdef->generated && newdef->generated && newdef->generated != inhdef->generated)
    3372          12 :         ereport(ERROR,
    3373             :                 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
    3374             :                  errmsg("column \"%s\" inherits from generated column of different kind",
    3375             :                         inhdef->colname),
    3376             :                  errdetail("Parent column is %s, child column is %s.",
    3377             :                            inhdef->generated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL",
    3378             :                            newdef->generated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL")));
    3379             : 
    3380             :     /*
    3381             :      * If new def has a default, override previous default
    3382             :      */
    3383         302 :     if (newdef->raw_default != NULL)
    3384             :     {
    3385          30 :         inhdef->raw_default = newdef->raw_default;
    3386          30 :         inhdef->cooked_default = newdef->cooked_default;
    3387             :     }
    3388             : 
    3389             :     /* Mark the column as locally defined */
    3390         302 :     inhdef->is_local = true;
    3391         302 : }
    3392             : 
    3393             : /*
    3394             :  * MergeInheritedAttribute
    3395             :  *      Merge given parent attribute definition into specified attribute
    3396             :  *      inherited from the previous parents.
    3397             :  *
    3398             :  * Input arguments:
    3399             :  * 'inh_columns' is the list of previously inherited ColumnDefs.
    3400             :  * 'exist_attno' is the number the existing matching attribute in inh_columns.
    3401             :  * 'newdef' is the new parent column/attribute definition to be merged.
    3402             :  *
    3403             :  * The matching ColumnDef in 'inh_columns' list is modified and returned.
    3404             :  *
    3405             :  * Notes:
    3406             :  * - The attribute is merged according to the rules laid out in the prologue
    3407             :  *   of MergeAttributes().
    3408             :  * - If matching inherited attribute exists but the new attribute can not be
    3409             :  *   merged into it, the function throws respective errors.
    3410             :  * - A partition inherits from only a single parent. Hence this function is
    3411             :  *   applicable only to a regular inheritance.
    3412             :  */
    3413             : static ColumnDef *
    3414         370 : MergeInheritedAttribute(List *inh_columns,
    3415             :                         int exist_attno,
    3416             :                         const ColumnDef *newdef)
    3417             : {
    3418         370 :     char       *attributeName = newdef->colname;
    3419             :     ColumnDef  *prevdef;
    3420             :     Oid         prevtypeid,
    3421             :                 newtypeid;
    3422             :     int32       prevtypmod,
    3423             :                 newtypmod;
    3424             :     Oid         prevcollid,
    3425             :                 newcollid;
    3426             : 
    3427         370 :     ereport(NOTICE,
    3428             :             (errmsg("merging multiple inherited definitions of column \"%s\"",
    3429             :                     attributeName)));
    3430         370 :     prevdef = list_nth_node(ColumnDef, inh_columns, exist_attno - 1);
    3431             : 
    3432             :     /*
    3433             :      * Must have the same type and typmod
    3434             :      */
    3435         370 :     typenameTypeIdAndMod(NULL, prevdef->typeName, &prevtypeid, &prevtypmod);
    3436         370 :     typenameTypeIdAndMod(NULL, newdef->typeName, &newtypeid, &newtypmod);
    3437         370 :     if (prevtypeid != newtypeid || prevtypmod != newtypmod)
    3438           0 :         ereport(ERROR,
    3439             :                 (errcode(ERRCODE_DATATYPE_MISMATCH),
    3440             :                  errmsg("inherited column \"%s\" has a type conflict",
    3441             :                         attributeName),
    3442             :                  errdetail("%s versus %s",
    3443             :                            format_type_with_typemod(prevtypeid, prevtypmod),
    3444             :                            format_type_with_typemod(newtypeid, newtypmod))));
    3445             : 
    3446             :     /*
    3447             :      * Must have the same collation
    3448             :      */
    3449         370 :     prevcollid = GetColumnDefCollation(NULL, prevdef, prevtypeid);
    3450         370 :     newcollid = GetColumnDefCollation(NULL, newdef, newtypeid);
    3451         370 :     if (prevcollid != newcollid)
    3452           0 :         ereport(ERROR,
    3453             :                 (errcode(ERRCODE_COLLATION_MISMATCH),
    3454             :                  errmsg("inherited column \"%s\" has a collation conflict",
    3455             :                         attributeName),
    3456             :                  errdetail("\"%s\" versus \"%s\"",
    3457             :                            get_collation_name(prevcollid),
    3458             :                            get_collation_name(newcollid))));
    3459             : 
    3460             :     /*
    3461             :      * Copy/check storage parameter
    3462             :      */
    3463         370 :     if (prevdef->storage == 0)
    3464           0 :         prevdef->storage = newdef->storage;
    3465         370 :     else if (prevdef->storage != newdef->storage)
    3466           6 :         ereport(ERROR,
    3467             :                 (errcode(ERRCODE_DATATYPE_MISMATCH),
    3468             :                  errmsg("inherited column \"%s\" has a storage parameter conflict",
    3469             :                         attributeName),
    3470             :                  errdetail("%s versus %s",
    3471             :                            storage_name(prevdef->storage),
    3472             :                            storage_name(newdef->storage))));
    3473             : 
    3474             :     /*
    3475             :      * Copy/check compression parameter
    3476             :      */
    3477         364 :     if (prevdef->compression == NULL)
    3478         346 :         prevdef->compression = newdef->compression;
    3479          18 :     else if (newdef->compression != NULL)
    3480             :     {
    3481           6 :         if (strcmp(prevdef->compression, newdef->compression) != 0)
    3482           6 :             ereport(ERROR,
    3483             :                     (errcode(ERRCODE_DATATYPE_MISMATCH),
    3484             :                      errmsg("column \"%s\" has a compression method conflict",
    3485             :                             attributeName),
    3486             :                      errdetail("%s versus %s",
    3487             :                                prevdef->compression, newdef->compression)));
    3488             :     }
    3489             : 
    3490             :     /*
    3491             :      * Check for GENERATED conflicts
    3492             :      */
    3493         358 :     if (prevdef->generated != newdef->generated)
    3494          24 :         ereport(ERROR,
    3495             :                 (errcode(ERRCODE_DATATYPE_MISMATCH),
    3496             :                  errmsg("inherited column \"%s\" has a generation conflict",
    3497             :                         attributeName)));
    3498             : 
    3499             :     /*
    3500             :      * Default and other constraints are handled by the caller.
    3501             :      */
    3502             : 
    3503         334 :     if (pg_add_s16_overflow(prevdef->inhcount, 1,
    3504             :                             &prevdef->inhcount))
    3505           0 :         ereport(ERROR,
    3506             :                 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
    3507             :                 errmsg("too many inheritance parents"));
    3508             : 
    3509         334 :     return prevdef;
    3510             : }
    3511             : 
    3512             : /*
    3513             :  * StoreCatalogInheritance
    3514             :  *      Updates the system catalogs with proper inheritance information.
    3515             :  *
    3516             :  * supers is a list of the OIDs of the new relation's direct ancestors.
    3517             :  */
    3518             : static void
    3519       64322 : StoreCatalogInheritance(Oid relationId, List *supers,
    3520             :                         bool child_is_partition)
    3521             : {
    3522             :     Relation    relation;
    3523             :     int32       seqNumber;
    3524             :     ListCell   *entry;
    3525             : 
    3526             :     /*
    3527             :      * sanity checks
    3528             :      */
    3529             :     Assert(OidIsValid(relationId));
    3530             : 
    3531       64322 :     if (supers == NIL)
    3532       53646 :         return;
    3533             : 
    3534             :     /*
    3535             :      * Store INHERITS information in pg_inherits using direct ancestors only.
    3536             :      * Also enter dependencies on the direct ancestors, and make sure they are
    3537             :      * marked with relhassubclass = true.
    3538             :      *
    3539             :      * (Once upon a time, both direct and indirect ancestors were found here
    3540             :      * and then entered into pg_ipl.  Since that catalog doesn't exist
    3541             :      * anymore, there's no need to look for indirect ancestors.)
    3542             :      */
    3543       10676 :     relation = table_open(InheritsRelationId, RowExclusiveLock);
    3544             : 
    3545       10676 :     seqNumber = 1;
    3546       21686 :     foreach(entry, supers)
    3547             :     {
    3548       11010 :         Oid         parentOid = lfirst_oid(entry);
    3549             : 
    3550       11010 :         StoreCatalogInheritance1(relationId, parentOid, seqNumber, relation,
    3551             :                                  child_is_partition);
    3552       11010 :         seqNumber++;
    3553             :     }
    3554             : 
    3555       10676 :     table_close(relation, RowExclusiveLock);
    3556             : }
    3557             : 
    3558             : /*
    3559             :  * Make catalog entries showing relationId as being an inheritance child
    3560             :  * of parentOid.  inhRelation is the already-opened pg_inherits catalog.
    3561             :  */
    3562             : static void
    3563       14244 : StoreCatalogInheritance1(Oid relationId, Oid parentOid,
    3564             :                          int32 seqNumber, Relation inhRelation,
    3565             :                          bool child_is_partition)
    3566             : {
    3567             :     ObjectAddress childobject,
    3568             :                 parentobject;
    3569             : 
    3570             :     /* store the pg_inherits row */
    3571       14244 :     StoreSingleInheritance(relationId, parentOid, seqNumber);
    3572             : 
    3573             :     /*
    3574             :      * Store a dependency too
    3575             :      */
    3576       14244 :     parentobject.classId = RelationRelationId;
    3577       14244 :     parentobject.objectId = parentOid;
    3578       14244 :     parentobject.objectSubId = 0;
    3579       14244 :     childobject.classId = RelationRelationId;
    3580       14244 :     childobject.objectId = relationId;
    3581       14244 :     childobject.objectSubId = 0;
    3582             : 
    3583       14244 :     recordDependencyOn(&childobject, &parentobject,
    3584             :                        child_dependency_type(child_is_partition));
    3585             : 
    3586             :     /*
    3587             :      * Post creation hook of this inheritance. Since object_access_hook
    3588             :      * doesn't take multiple object identifiers, we relay oid of parent
    3589             :      * relation using auxiliary_id argument.
    3590             :      */
    3591       14244 :     InvokeObjectPostAlterHookArg(InheritsRelationId,
    3592             :                                  relationId, 0,
    3593             :                                  parentOid, false);
    3594             : 
    3595             :     /*
    3596             :      * Mark the parent as having subclasses.
    3597             :      */
    3598       14244 :     SetRelationHasSubclass(parentOid, true);
    3599       14244 : }
    3600             : 
    3601             : /*
    3602             :  * Look for an existing column entry with the given name.
    3603             :  *
    3604             :  * Returns the index (starting with 1) if attribute already exists in columns,
    3605             :  * 0 if it doesn't.
    3606             :  */
    3607             : static int
    3608       24932 : findAttrByName(const char *attributeName, const List *columns)
    3609             : {
    3610             :     ListCell   *lc;
    3611       24932 :     int         i = 1;
    3612             : 
    3613       45936 :     foreach(lc, columns)
    3614             :     {
    3615       21754 :         if (strcmp(attributeName, lfirst_node(ColumnDef, lc)->colname) == 0)
    3616         750 :             return i;
    3617             : 
    3618       21004 :         i++;
    3619             :     }
    3620       24182 :     return 0;
    3621             : }
    3622             : 
    3623             : 
    3624             : /*
    3625             :  * SetRelationHasSubclass
    3626             :  *      Set the value of the relation's relhassubclass field in pg_class.
    3627             :  *
    3628             :  * It's always safe to set this field to true, because all SQL commands are
    3629             :  * ready to see true and then find no children.  On the other hand, commands
    3630             :  * generally assume zero children if this is false.
    3631             :  *
    3632             :  * Caller must hold any self-exclusive lock until end of transaction.  If the
    3633             :  * new value is false, caller must have acquired that lock before reading the
    3634             :  * evidence that justified the false value.  That way, it properly waits if
    3635             :  * another backend is simultaneously concluding no need to change the tuple
    3636             :  * (new and old values are true).
    3637             :  *
    3638             :  * NOTE: an important side-effect of this operation is that an SI invalidation
    3639             :  * message is sent out to all backends --- including me --- causing plans
    3640             :  * referencing the relation to be rebuilt with the new list of children.
    3641             :  * This must happen even if we find that no change is needed in the pg_class
    3642             :  * row.
    3643             :  */
    3644             : void
    3645       17990 : SetRelationHasSubclass(Oid relationId, bool relhassubclass)
    3646             : {
    3647             :     Relation    relationRelation;
    3648             :     HeapTuple   tuple;
    3649             :     Form_pg_class classtuple;
    3650             : 
    3651             :     Assert(CheckRelationOidLockedByMe(relationId,
    3652             :                                       ShareUpdateExclusiveLock, false) ||
    3653             :            CheckRelationOidLockedByMe(relationId,
    3654             :                                       ShareRowExclusiveLock, true));
    3655             : 
    3656             :     /*
    3657             :      * Fetch a modifiable copy of the tuple, modify it, update pg_class.
    3658             :      */
    3659       17990 :     relationRelation = table_open(RelationRelationId, RowExclusiveLock);
    3660       17990 :     tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relationId));
    3661       17990 :     if (!HeapTupleIsValid(tuple))
    3662           0 :         elog(ERROR, "cache lookup failed for relation %u", relationId);
    3663       17990 :     classtuple = (Form_pg_class) GETSTRUCT(tuple);
    3664             : 
    3665       17990 :     if (classtuple->relhassubclass != relhassubclass)
    3666             :     {
    3667        8494 :         classtuple->relhassubclass = relhassubclass;
    3668        8494 :         CatalogTupleUpdate(relationRelation, &tuple->t_self, tuple);
    3669             :     }
    3670             :     else
    3671             :     {
    3672             :         /* no need to change tuple, but force relcache rebuild anyway */
    3673        9496 :         CacheInvalidateRelcacheByTuple(tuple);
    3674             :     }
    3675             : 
    3676       17990 :     heap_freetuple(tuple);
    3677       17990 :     table_close(relationRelation, RowExclusiveLock);
    3678       17990 : }
    3679             : 
    3680             : /*
    3681             :  * CheckRelationTableSpaceMove
    3682             :  *      Check if relation can be moved to new tablespace.
    3683             :  *
    3684             :  * NOTE: The caller must hold AccessExclusiveLock on the relation.
    3685             :  *
    3686             :  * Returns true if the relation can be moved to the new tablespace; raises
    3687             :  * an error if it is not possible to do the move; returns false if the move
    3688             :  * would have no effect.
    3689             :  */
    3690             : bool
    3691         232 : CheckRelationTableSpaceMove(Relation rel, Oid newTableSpaceId)
    3692             : {
    3693             :     Oid         oldTableSpaceId;
    3694             : 
    3695             :     /*
    3696             :      * No work if no change in tablespace.  Note that MyDatabaseTableSpace is
    3697             :      * stored as 0.
    3698             :      */
    3699         232 :     oldTableSpaceId = rel->rd_rel->reltablespace;
    3700         232 :     if (newTableSpaceId == oldTableSpaceId ||
    3701         224 :         (newTableSpaceId == MyDatabaseTableSpace && oldTableSpaceId == 0))
    3702          16 :         return false;
    3703             : 
    3704             :     /*
    3705             :      * We cannot support moving mapped relations into different tablespaces.
    3706             :      * (In particular this eliminates all shared catalogs.)
    3707             :      */
    3708         216 :     if (RelationIsMapped(rel))
    3709           0 :         ereport(ERROR,
    3710             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    3711             :                  errmsg("cannot move system relation \"%s\"",
    3712             :                         RelationGetRelationName(rel))));
    3713             : 
    3714             :     /* Cannot move a non-shared relation into pg_global */
    3715         216 :     if (newTableSpaceId == GLOBALTABLESPACE_OID)
    3716          12 :         ereport(ERROR,
    3717             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    3718             :                  errmsg("only shared relations can be placed in pg_global tablespace")));
    3719             : 
    3720             :     /*
    3721             :      * Do not allow moving temp tables of other backends ... their local
    3722             :      * buffer manager is not going to cope.
    3723             :      */
    3724         204 :     if (RELATION_IS_OTHER_TEMP(rel))
    3725           0 :         ereport(ERROR,
    3726             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    3727             :                  errmsg("cannot move temporary tables of other sessions")));
    3728             : 
    3729         204 :     return true;
    3730             : }
    3731             : 
    3732             : /*
    3733             :  * SetRelationTableSpace
    3734             :  *      Set new reltablespace and relfilenumber in pg_class entry.
    3735             :  *
    3736             :  * newTableSpaceId is the new tablespace for the relation, and
    3737             :  * newRelFilenumber its new filenumber.  If newRelFilenumber is
    3738             :  * InvalidRelFileNumber, this field is not updated.
    3739             :  *
    3740             :  * NOTE: The caller must hold AccessExclusiveLock on the relation.
    3741             :  *
    3742             :  * The caller of this routine had better check if a relation can be
    3743             :  * moved to this new tablespace by calling CheckRelationTableSpaceMove()
    3744             :  * first, and is responsible for making the change visible with
    3745             :  * CommandCounterIncrement().
    3746             :  */
    3747             : void
    3748         204 : SetRelationTableSpace(Relation rel,
    3749             :                       Oid newTableSpaceId,
    3750             :                       RelFileNumber newRelFilenumber)
    3751             : {
    3752             :     Relation    pg_class;
    3753             :     HeapTuple   tuple;
    3754             :     ItemPointerData otid;
    3755             :     Form_pg_class rd_rel;
    3756         204 :     Oid         reloid = RelationGetRelid(rel);
    3757             : 
    3758             :     Assert(CheckRelationTableSpaceMove(rel, newTableSpaceId));
    3759             : 
    3760             :     /* Get a modifiable copy of the relation's pg_class row. */
    3761         204 :     pg_class = table_open(RelationRelationId, RowExclusiveLock);
    3762             : 
    3763         204 :     tuple = SearchSysCacheLockedCopy1(RELOID, ObjectIdGetDatum(reloid));
    3764         204 :     if (!HeapTupleIsValid(tuple))
    3765           0 :         elog(ERROR, "cache lookup failed for relation %u", reloid);
    3766         204 :     otid = tuple->t_self;
    3767         204 :     rd_rel = (Form_pg_class) GETSTRUCT(tuple);
    3768             : 
    3769             :     /* Update the pg_class row. */
    3770         408 :     rd_rel->reltablespace = (newTableSpaceId == MyDatabaseTableSpace) ?
    3771         204 :         InvalidOid : newTableSpaceId;
    3772         204 :     if (RelFileNumberIsValid(newRelFilenumber))
    3773         160 :         rd_rel->relfilenode = newRelFilenumber;
    3774         204 :     CatalogTupleUpdate(pg_class, &otid, tuple);
    3775         204 :     UnlockTuple(pg_class, &otid, InplaceUpdateTupleLock);
    3776             : 
    3777             :     /*
    3778             :      * Record dependency on tablespace.  This is only required for relations
    3779             :      * that have no physical storage.
    3780             :      */
    3781         204 :     if (!RELKIND_HAS_STORAGE(rel->rd_rel->relkind))
    3782          30 :         changeDependencyOnTablespace(RelationRelationId, reloid,
    3783             :                                      rd_rel->reltablespace);
    3784             : 
    3785         204 :     heap_freetuple(tuple);
    3786         204 :     table_close(pg_class, RowExclusiveLock);
    3787         204 : }
    3788             : 
    3789             : /*
    3790             :  *      renameatt_check         - basic sanity checks before attribute rename
    3791             :  */
    3792             : static void
    3793        1018 : renameatt_check(Oid myrelid, Form_pg_class classform, bool recursing)
    3794             : {
    3795        1018 :     char        relkind = classform->relkind;
    3796             : 
    3797        1018 :     if (classform->reloftype && !recursing)
    3798           6 :         ereport(ERROR,
    3799             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    3800             :                  errmsg("cannot rename column of typed table")));
    3801             : 
    3802             :     /*
    3803             :      * Renaming the columns of sequences or toast tables doesn't actually
    3804             :      * break anything from the system's point of view, since internal
    3805             :      * references are by attnum.  But it doesn't seem right to allow users to
    3806             :      * change names that are hardcoded into the system, hence the following
    3807             :      * restriction.
    3808             :      */
    3809        1012 :     if (relkind != RELKIND_RELATION &&
    3810          84 :         relkind != RELKIND_VIEW &&
    3811          84 :         relkind != RELKIND_MATVIEW &&
    3812          36 :         relkind != RELKIND_COMPOSITE_TYPE &&
    3813          36 :         relkind != RELKIND_INDEX &&
    3814          36 :         relkind != RELKIND_PARTITIONED_INDEX &&
    3815           0 :         relkind != RELKIND_FOREIGN_TABLE &&
    3816             :         relkind != RELKIND_PARTITIONED_TABLE)
    3817           0 :         ereport(ERROR,
    3818             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    3819             :                  errmsg("cannot rename columns of relation \"%s\"",
    3820             :                         NameStr(classform->relname)),
    3821             :                  errdetail_relkind_not_supported(relkind)));
    3822             : 
    3823             :     /*
    3824             :      * permissions checking.  only the owner of a class can change its schema.
    3825             :      */
    3826        1012 :     if (!object_ownercheck(RelationRelationId, myrelid, GetUserId()))
    3827           0 :         aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(myrelid)),
    3828           0 :                        NameStr(classform->relname));
    3829        1012 :     if (!allowSystemTableMods && IsSystemClass(myrelid, classform))
    3830           2 :         ereport(ERROR,
    3831             :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
    3832             :                  errmsg("permission denied: \"%s\" is a system catalog",
    3833             :                         NameStr(classform->relname))));
    3834        1010 : }
    3835             : 
    3836             : /*
    3837             :  *      renameatt_internal      - workhorse for renameatt
    3838             :  *
    3839             :  * Return value is the attribute number in the 'myrelid' relation.
    3840             :  */
    3841             : static AttrNumber
    3842         552 : renameatt_internal(Oid myrelid,
    3843             :                    const char *oldattname,
    3844             :                    const char *newattname,
    3845             :                    bool recurse,
    3846             :                    bool recursing,
    3847             :                    int expected_parents,
    3848             :                    DropBehavior behavior)
    3849             : {
    3850             :     Relation    targetrelation;
    3851             :     Relation    attrelation;
    3852             :     HeapTuple   atttup;
    3853             :     Form_pg_attribute attform;
    3854             :     AttrNumber  attnum;
    3855             : 
    3856             :     /*
    3857             :      * Grab an exclusive lock on the target table, which we will NOT release
    3858             :      * until end of transaction.
    3859             :      */
    3860         552 :     targetrelation = relation_open(myrelid, AccessExclusiveLock);
    3861         552 :     renameatt_check(myrelid, RelationGetForm(targetrelation), recursing);
    3862             : 
    3863             :     /*
    3864             :      * if the 'recurse' flag is set then we are supposed to rename this
    3865             :      * attribute in all classes that inherit from 'relname' (as well as in
    3866             :      * 'relname').
    3867             :      *
    3868             :      * any permissions or problems with duplicate attributes will cause the
    3869             :      * whole transaction to abort, which is what we want -- all or nothing.
    3870             :      */
    3871         552 :     if (recurse)
    3872             :     {
    3873             :         List       *child_oids,
    3874             :                    *child_numparents;
    3875             :         ListCell   *lo,
    3876             :                    *li;
    3877             : 
    3878             :         /*
    3879             :          * we need the number of parents for each child so that the recursive
    3880             :          * calls to renameatt() can determine whether there are any parents
    3881             :          * outside the inheritance hierarchy being processed.
    3882             :          */
    3883         248 :         child_oids = find_all_inheritors(myrelid, AccessExclusiveLock,
    3884             :                                          &child_numparents);
    3885             : 
    3886             :         /*
    3887             :          * find_all_inheritors does the recursive search of the inheritance
    3888             :          * hierarchy, so all we have to do is process all of the relids in the
    3889             :          * list that it returns.
    3890             :          */
    3891         734 :         forboth(lo, child_oids, li, child_numparents)
    3892             :         {
    3893         516 :             Oid         childrelid = lfirst_oid(lo);
    3894         516 :             int         numparents = lfirst_int(li);
    3895             : 
    3896         516 :             if (childrelid == myrelid)
    3897         248 :                 continue;
    3898             :             /* note we need not recurse again */
    3899         268 :             renameatt_internal(childrelid, oldattname, newattname, false, true, numparents, behavior);
    3900             :         }
    3901             :     }
    3902             :     else
    3903             :     {
    3904             :         /*
    3905             :          * If we are told not to recurse, there had better not be any child
    3906             :          * tables; else the rename would put them out of step.
    3907             :          *
    3908             :          * expected_parents will only be 0 if we are not already recursing.
    3909             :          */
    3910         340 :         if (expected_parents == 0 &&
    3911          36 :             find_inheritance_children(myrelid, NoLock) != NIL)
    3912          12 :             ereport(ERROR,
    3913             :                     (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    3914             :                      errmsg("inherited column \"%s\" must be renamed in child tables too",
    3915             :                             oldattname)));
    3916             :     }
    3917             : 
    3918             :     /* rename attributes in typed tables of composite type */
    3919         510 :     if (targetrelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
    3920             :     {
    3921             :         List       *child_oids;
    3922             :         ListCell   *lo;
    3923             : 
    3924          24 :         child_oids = find_typed_table_dependencies(targetrelation->rd_rel->reltype,
    3925          24 :                                                    RelationGetRelationName(targetrelation),
    3926             :                                                    behavior);
    3927             : 
    3928          24 :         foreach(lo, child_oids)
    3929           6 :             renameatt_internal(lfirst_oid(lo), oldattname, newattname, true, true, 0, behavior);
    3930             :     }
    3931             : 
    3932         504 :     attrelation = table_open(AttributeRelationId, RowExclusiveLock);
    3933             : 
    3934         504 :     atttup = SearchSysCacheCopyAttName(myrelid, oldattname);
    3935         504 :     if (!HeapTupleIsValid(atttup))
    3936          24 :         ereport(ERROR,
    3937             :                 (errcode(ERRCODE_UNDEFINED_COLUMN),
    3938             :                  errmsg("column \"%s\" does not exist",
    3939             :                         oldattname)));
    3940         480 :     attform = (Form_pg_attribute) GETSTRUCT(atttup);
    3941             : 
    3942         480 :     attnum = attform->attnum;
    3943         480 :     if (attnum <= 0)
    3944           0 :         ereport(ERROR,
    3945             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    3946             :                  errmsg("cannot rename system column \"%s\"",
    3947             :                         oldattname)));
    3948             : 
    3949             :     /*
    3950             :      * if the attribute is inherited, forbid the renaming.  if this is a
    3951             :      * top-level call to renameatt(), then expected_parents will be 0, so the
    3952             :      * effect of this code will be to prohibit the renaming if the attribute
    3953             :      * is inherited at all.  if this is a recursive call to renameatt(),
    3954             :      * expected_parents will be the number of parents the current relation has
    3955             :      * within the inheritance hierarchy being processed, so we'll prohibit the
    3956             :      * renaming only if there are additional parents from elsewhere.
    3957             :      */
    3958         480 :     if (attform->attinhcount > expected_parents)
    3959          30 :         ereport(ERROR,
    3960             :                 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    3961             :                  errmsg("cannot rename inherited column \"%s\"",
    3962             :                         oldattname)));
    3963             : 
    3964             :     /* new name should not already exist */
    3965         450 :     (void) check_for_column_name_collision(targetrelation, newattname, false);
    3966             : 
    3967             :     /* apply the update */
    3968         438 :     namestrcpy(&(attform->attname), newattname);
    3969             : 
    3970         438 :     CatalogTupleUpdate(attrelation, &atttup->t_self, atttup);
    3971             : 
    3972         438 :     InvokeObjectPostAlterHook(RelationRelationId, myrelid, attnum);
    3973             : 
    3974         438 :     heap_freetuple(atttup);
    3975             : 
    3976         438 :     table_close(attrelation, RowExclusiveLock);
    3977             : 
    3978         438 :     relation_close(targetrelation, NoLock); /* close rel but keep lock */
    3979             : 
    3980         438 :     return attnum;
    3981             : }
    3982             : 
    3983             : /*
    3984             :  * Perform permissions and integrity checks before acquiring a relation lock.
    3985             :  */
    3986             : static void
    3987         418 : RangeVarCallbackForRenameAttribute(const RangeVar *rv, Oid relid, Oid oldrelid,
    3988             :                                    void *arg)
    3989             : {
    3990             :     HeapTuple   tuple;
    3991             :     Form_pg_class form;
    3992             : 
    3993         418 :     tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
    3994         418 :     if (!HeapTupleIsValid(tuple))
    3995          36 :         return;                 /* concurrently dropped */
    3996         382 :     form = (Form_pg_class) GETSTRUCT(tuple);
    3997         382 :     renameatt_check(relid, form, false);
    3998         374 :     ReleaseSysCache(tuple);
    3999             : }
    4000             : 
    4001             : /*
    4002             :  *      renameatt       - changes the name of an attribute in a relation
    4003             :  *
    4004             :  * The returned ObjectAddress is that of the renamed column.
    4005             :  */
    4006             : ObjectAddress
    4007         316 : renameatt(RenameStmt *stmt)
    4008             : {
    4009             :     Oid         relid;
    4010             :     AttrNumber  attnum;
    4011             :     ObjectAddress address;
    4012             : 
    4013             :     /* lock level taken here should match renameatt_internal */
    4014         316 :     relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
    4015         316 :                                      stmt->missing_ok ? RVR_MISSING_OK : 0,
    4016             :                                      RangeVarCallbackForRenameAttribute,
    4017             :                                      NULL);
    4018             : 
    4019         302 :     if (!OidIsValid(relid))
    4020             :     {
    4021          24 :         ereport(NOTICE,
    4022             :                 (errmsg("relation \"%s\" does not exist, skipping",
    4023             :                         stmt->relation->relname)));
    4024          24 :         return InvalidObjectAddress;
    4025             :     }
    4026             : 
    4027             :     attnum =
    4028         278 :         renameatt_internal(relid,
    4029         278 :                            stmt->subname,    /* old att name */
    4030         278 :                            stmt->newname,    /* new att name */
    4031         278 :                            stmt->relation->inh, /* recursive? */
    4032             :                            false,   /* recursing? */
    4033             :                            0,   /* expected inhcount */
    4034             :                            stmt->behavior);
    4035             : 
    4036         194 :     ObjectAddressSubSet(address, RelationRelationId, relid, attnum);
    4037             : 
    4038         194 :     return address;
    4039             : }
    4040             : 
    4041             : /*
    4042             :  * same logic as renameatt_internal
    4043             :  */
    4044             : static ObjectAddress
    4045          90 : rename_constraint_internal(Oid myrelid,
    4046             :                            Oid mytypid,
    4047             :                            const char *oldconname,
    4048             :                            const char *newconname,
    4049             :                            bool recurse,
    4050             :                            bool recursing,
    4051             :                            int expected_parents)
    4052             : {
    4053          90 :     Relation    targetrelation = NULL;
    4054             :     Oid         constraintOid;
    4055             :     HeapTuple   tuple;
    4056             :     Form_pg_constraint con;
    4057             :     ObjectAddress address;
    4058             : 
    4059             :     Assert(!myrelid || !mytypid);
    4060             : 
    4061          90 :     if (mytypid)
    4062             :     {
    4063           6 :         constraintOid = get_domain_constraint_oid(mytypid, oldconname, false);
    4064             :     }
    4065             :     else
    4066             :     {
    4067          84 :         targetrelation = relation_open(myrelid, AccessExclusiveLock);
    4068             : 
    4069             :         /*
    4070             :          * don't tell it whether we're recursing; we allow changing typed
    4071             :          * tables here
    4072             :          */
    4073          84 :         renameatt_check(myrelid, RelationGetForm(targetrelation), false);
    4074             : 
    4075          84 :         constraintOid = get_relation_constraint_oid(myrelid, oldconname, false);
    4076             :     }
    4077             : 
    4078          90 :     tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constraintOid));
    4079          90 :     if (!HeapTupleIsValid(tuple))
    4080           0 :         elog(ERROR, "cache lookup failed for constraint %u",
    4081             :              constraintOid);
    4082          90 :     con = (Form_pg_constraint) GETSTRUCT(tuple);
    4083             : 
    4084          90 :     if (myrelid &&
    4085          84 :         (con->contype == CONSTRAINT_CHECK ||
    4086          24 :          con->contype == CONSTRAINT_NOTNULL) &&
    4087          66 :         !con->connoinherit)
    4088             :     {
    4089          54 :         if (recurse)
    4090             :         {
    4091             :             List       *child_oids,
    4092             :                        *child_numparents;
    4093             :             ListCell   *lo,
    4094             :                        *li;
    4095             : 
    4096          36 :             child_oids = find_all_inheritors(myrelid, AccessExclusiveLock,
    4097             :                                              &child_numparents);
    4098             : 
    4099          84 :             forboth(lo, child_oids, li, child_numparents)
    4100             :             {
    4101          48 :                 Oid         childrelid = lfirst_oid(lo);
    4102          48 :                 int         numparents = lfirst_int(li);
    4103             : 
    4104          48 :                 if (childrelid == myrelid)
    4105          36 :                     continue;
    4106             : 
    4107          12 :                 rename_constraint_internal(childrelid, InvalidOid, oldconname, newconname, false, true, numparents);
    4108             :             }
    4109             :         }
    4110             :         else
    4111             :         {
    4112          24 :             if (expected_parents == 0 &&
    4113           6 :                 find_inheritance_children(myrelid, NoLock) != NIL)
    4114           6 :                 ereport(ERROR,
    4115             :                         (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    4116             :                          errmsg("inherited constraint \"%s\" must be renamed in child tables too",
    4117             :                                 oldconname)));
    4118             :         }
    4119             : 
    4120          48 :         if (con->coninhcount > expected_parents)
    4121           6 :             ereport(ERROR,
    4122             :                     (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    4123             :                      errmsg("cannot rename inherited constraint \"%s\"",
    4124             :                             oldconname)));
    4125             :     }
    4126             : 
    4127          78 :     if (con->conindid
    4128          18 :         && (con->contype == CONSTRAINT_PRIMARY
    4129           6 :             || con->contype == CONSTRAINT_UNIQUE
    4130           0 :             || con->contype == CONSTRAINT_EXCLUSION))
    4131             :         /* rename the index; this renames the constraint as well */
    4132          18 :         RenameRelationInternal(con->conindid, newconname, false, true);
    4133             :     else
    4134          60 :         RenameConstraintById(constraintOid, newconname);
    4135             : 
    4136          78 :     ObjectAddressSet(address, ConstraintRelationId, constraintOid);
    4137             : 
    4138          78 :     ReleaseSysCache(tuple);
    4139             : 
    4140          78 :     if (targetrelation)
    4141             :     {
    4142             :         /*
    4143             :          * Invalidate relcache so as others can see the new constraint name.
    4144             :          */
    4145          72 :         CacheInvalidateRelcache(targetrelation);
    4146             : 
    4147          72 :         relation_close(targetrelation, NoLock); /* close rel but keep lock */
    4148             :     }
    4149             : 
    4150          78 :     return address;
    4151             : }
    4152             : 
    4153             : ObjectAddress
    4154          84 : RenameConstraint(RenameStmt *stmt)
    4155             : {
    4156          84 :     Oid         relid = InvalidOid;
    4157          84 :     Oid         typid = InvalidOid;
    4158             : 
    4159          84 :     if (stmt->renameType == OBJECT_DOMCONSTRAINT)
    4160             :     {
    4161             :         Relation    rel;
    4162             :         HeapTuple   tup;
    4163             : 
    4164           6 :         typid = typenameTypeId(NULL, makeTypeNameFromNameList(castNode(List, stmt->object)));
    4165           6 :         rel = table_open(TypeRelationId, RowExclusiveLock);
    4166           6 :         tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
    4167           6 :         if (!HeapTupleIsValid(tup))
    4168           0 :             elog(ERROR, "cache lookup failed for type %u", typid);
    4169           6 :         checkDomainOwner(tup);
    4170           6 :         ReleaseSysCache(tup);
    4171           6 :         table_close(rel, NoLock);
    4172             :     }
    4173             :     else
    4174             :     {
    4175             :         /* lock level taken here should match rename_constraint_internal */
    4176          78 :         relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
    4177          78 :                                          stmt->missing_ok ? RVR_MISSING_OK : 0,
    4178             :                                          RangeVarCallbackForRenameAttribute,
    4179             :                                          NULL);
    4180          78 :         if (!OidIsValid(relid))
    4181             :         {
    4182           6 :             ereport(NOTICE,
    4183             :                     (errmsg("relation \"%s\" does not exist, skipping",
    4184             :                             stmt->relation->relname)));
    4185           6 :             return InvalidObjectAddress;
    4186             :         }
    4187             :     }
    4188             : 
    4189             :     return
    4190          78 :         rename_constraint_internal(relid, typid,
    4191          78 :                                    stmt->subname,
    4192          78 :                                    stmt->newname,
    4193         150 :                                    (stmt->relation &&
    4194          72 :                                     stmt->relation->inh), /* recursive? */
    4195             :                                    false,   /* recursing? */
    4196             :                                    0 /* expected inhcount */ );
    4197             : }
    4198             : 
    4199             : /*
    4200             :  * Execute ALTER TABLE/INDEX/SEQUENCE/VIEW/MATERIALIZED VIEW/FOREIGN TABLE
    4201             :  * RENAME
    4202             :  */
    4203             : ObjectAddress
    4204         512 : RenameRelation(RenameStmt *stmt)
    4205             : {
    4206         512 :     bool        is_index_stmt = stmt->renameType == OBJECT_INDEX;
    4207             :     Oid         relid;
    4208             :     ObjectAddress address;
    4209             : 
    4210             :     /*
    4211             :      * Grab an exclusive lock on the target table, index, sequence, view,
    4212             :      * materialized view, or foreign table, which we will NOT release until
    4213             :      * end of transaction.
    4214             :      *
    4215             :      * Lock level used here should match RenameRelationInternal, to avoid lock
    4216             :      * escalation.  However, because ALTER INDEX can be used with any relation
    4217             :      * type, we mustn't believe without verification.
    4218             :      */
    4219             :     for (;;)
    4220          12 :     {
    4221             :         LOCKMODE    lockmode;
    4222             :         char        relkind;
    4223             :         bool        obj_is_index;
    4224             : 
    4225         524 :         lockmode = is_index_stmt ? ShareUpdateExclusiveLock : AccessExclusiveLock;
    4226             : 
    4227         524 :         relid = RangeVarGetRelidExtended(stmt->relation, lockmode,
    4228         524 :                                          stmt->missing_ok ? RVR_MISSING_OK : 0,
    4229             :                                          RangeVarCallbackForAlterRelation,
    4230             :                                          stmt);
    4231             : 
    4232         474 :         if (!OidIsValid(relid))
    4233             :         {
    4234          18 :             ereport(NOTICE,
    4235             :                     (errmsg("relation \"%s\" does not exist, skipping",
    4236             :                             stmt->relation->relname)));
    4237          18 :             return InvalidObjectAddress;
    4238             :         }
    4239             : 
    4240             :         /*
    4241             :          * We allow mismatched statement and object types (e.g., ALTER INDEX
    4242             :          * to rename a table), but we might've used the wrong lock level.  If
    4243             :          * that happens, retry with the correct lock level.  We don't bother
    4244             :          * if we already acquired AccessExclusiveLock with an index, however.
    4245             :          */
    4246         456 :         relkind = get_rel_relkind(relid);
    4247         456 :         obj_is_index = (relkind == RELKIND_INDEX ||
    4248             :                         relkind == RELKIND_PARTITIONED_INDEX);
    4249         456 :         if (obj_is_index || is_index_stmt == obj_is_index)
    4250             :             break;
    4251             : 
    4252          12 :         UnlockRelationOid(relid, lockmode);
    4253          12 :         is_index_stmt = obj_is_index;
    4254             :     }
    4255             : 
    4256             :     /* Do the work */
    4257         444 :     RenameRelationInternal(relid, stmt->newname, false, is_index_stmt);
    4258             : 
    4259         432 :     ObjectAddressSet(address, RelationRelationId, relid);
    4260             : 
    4261         432 :     return address;
    4262             : }
    4263             : 
    4264             : /*
    4265             :  *      RenameRelationInternal - change the name of a relation
    4266             :  */
    4267             : void
    4268        1744 : RenameRelationInternal(Oid myrelid, const char *newrelname, bool is_internal, bool is_index)
    4269             : {
    4270             :     Relation    targetrelation;
    4271             :     Relation    relrelation;    /* for RELATION relation */
    4272             :     ItemPointerData otid;
    4273             :     HeapTuple   reltup;
    4274             :     Form_pg_class relform;
    4275             :     Oid         namespaceId;
    4276             : 
    4277             :     /*
    4278             :      * Grab a lock on the target relation, which we will NOT release until end
    4279             :      * of transaction.  We need at least a self-exclusive lock so that
    4280             :      * concurrent DDL doesn't overwrite the rename if they start updating
    4281             :      * while still seeing the old version.  The lock also guards against
    4282             :      * triggering relcache reloads in concurrent sessions, which might not
    4283             :      * handle this information changing under them.  For indexes, we can use a
    4284             :      * reduced lock level because RelationReloadIndexInfo() handles indexes
    4285             :      * specially.
    4286             :      */
    4287        1744 :     targetrelation = relation_open(myrelid, is_index ? ShareUpdateExclusiveLock : AccessExclusiveLock);
    4288        1744 :     namespaceId = RelationGetNamespace(targetrelation);
    4289             : 
    4290             :     /*
    4291             :      * Find relation's pg_class tuple, and make sure newrelname isn't in use.
    4292             :      */
    4293        1744 :     relrelation = table_open(RelationRelationId, RowExclusiveLock);
    4294             : 
    4295        1744 :     reltup = SearchSysCacheLockedCopy1(RELOID, ObjectIdGetDatum(myrelid));
    4296        1744 :     if (!HeapTupleIsValid(reltup))  /* shouldn't happen */
    4297           0 :         elog(ERROR, "cache lookup failed for relation %u", myrelid);
    4298        1744 :     otid = reltup->t_self;
    4299        1744 :     relform = (Form_pg_class) GETSTRUCT(reltup);
    4300             : 
    4301        1744 :     if (get_relname_relid(newrelname, namespaceId) != InvalidOid)
    4302          12 :         ereport(ERROR,
    4303             :                 (errcode(ERRCODE_DUPLICATE_TABLE),
    4304             :                  errmsg("relation \"%s\" already exists",
    4305             :                         newrelname)));
    4306             : 
    4307             :     /*
    4308             :      * RenameRelation is careful not to believe the caller's idea of the
    4309             :      * relation kind being handled.  We don't have to worry about this, but
    4310             :      * let's not be totally oblivious to it.  We can process an index as
    4311             :      * not-an-index, but not the other way around.
    4312             :      */
    4313             :     Assert(!is_index ||
    4314             :            is_index == (targetrelation->rd_rel->relkind == RELKIND_INDEX ||
    4315             :                         targetrelation->rd_rel->relkind == RELKIND_PARTITIONED_INDEX));
    4316             : 
    4317             :     /*
    4318             :      * Update pg_class tuple with new relname.  (Scribbling on reltup is OK
    4319             :      * because it's a copy...)
    4320             :      */
    4321        1732 :     namestrcpy(&(relform->relname), newrelname);
    4322             : 
    4323        1732 :     CatalogTupleUpdate(relrelation, &otid, reltup);
    4324        1732 :     UnlockTuple(relrelation, &otid, InplaceUpdateTupleLock);
    4325             : 
    4326        1732 :     InvokeObjectPostAlterHookArg(RelationRelationId, myrelid, 0,
    4327             :                                  InvalidOid, is_internal);
    4328             : 
    4329        1732 :     heap_freetuple(reltup);
    4330        1732 :     table_close(relrelation, RowExclusiveLock);
    4331             : 
    4332             :     /*
    4333             :      * Also rename the associated type, if any.
    4334             :      */
    4335        1732 :     if (OidIsValid(targetrelation->rd_rel->reltype))
    4336         190 :         RenameTypeInternal(targetrelation->rd_rel->reltype,
    4337             :                            newrelname, namespaceId);
    4338             : 
    4339             :     /*
    4340             :      * Also rename the associated constraint, if any.
    4341             :      */
    4342        1732 :     if (targetrelation->rd_rel->relkind == RELKIND_INDEX ||
    4343         940 :         targetrelation->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
    4344             :     {
    4345         810 :         Oid         constraintId = get_index_constraint(myrelid);
    4346             : 
    4347         810 :         if (OidIsValid(constraintId))
    4348          36 :             RenameConstraintById(constraintId, newrelname);
    4349             :     }
    4350             : 
    4351             :     /*
    4352             :      * Close rel, but keep lock!
    4353             :      */
    4354        1732 :     relation_close(targetrelation, NoLock);
    4355        1732 : }
    4356             : 
    4357             : /*
    4358             :  *      ResetRelRewrite - reset relrewrite
    4359             :  */
    4360             : void
    4361         598 : ResetRelRewrite(Oid myrelid)
    4362             : {
    4363             :     Relation    relrelation;    /* for RELATION relation */
    4364             :     HeapTuple   reltup;
    4365             :     Form_pg_class relform;
    4366             : 
    4367             :     /*
    4368             :      * Find relation's pg_class tuple.
    4369             :      */
    4370         598 :     relrelation = table_open(RelationRelationId, RowExclusiveLock);
    4371             : 
    4372         598 :     reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(myrelid));
    4373         598 :     if (!HeapTupleIsValid(reltup))  /* shouldn't happen */
    4374           0 :         elog(ERROR, "cache lookup failed for relation %u", myrelid);
    4375         598 :     relform = (Form_pg_class) GETSTRUCT(reltup);
    4376             : 
    4377             :     /*
    4378             :      * Update pg_class tuple.
    4379             :      */
    4380         598 :     relform->relrewrite = InvalidOid;
    4381             : 
    4382         598 :     CatalogTupleUpdate(relrelation, &reltup->t_self, reltup);
    4383             : 
    4384         598 :     heap_freetuple(reltup);
    4385         598 :     table_close(relrelation, RowExclusiveLock);
    4386         598 : }
    4387             : 
    4388             : /*
    4389             :  * Disallow ALTER TABLE (and similar commands) when the current backend has
    4390             :  * any open reference to the target table besides the one just acquired by
    4391             :  * the calling command; this implies there's an open cursor or active plan.
    4392             :  * We need this check because our lock doesn't protect us against stomping
    4393             :  * on our own foot, only other people's feet!
    4394             :  *
    4395             :  * For ALTER TABLE, the only case known to cause serious trouble is ALTER
    4396             :  * COLUMN TYPE, and some changes are obviously pretty benign, so this could
    4397             :  * possibly be relaxed to only error out for certain types of alterations.
    4398             :  * But the use-case for allowing any of these things is not obvious, so we
    4399             :  * won't work hard at it for now.
    4400             :  *
    4401             :  * We also reject these commands if there are any pending AFTER trigger events
    4402             :  * for the rel.  This is certainly necessary for the rewriting variants of
    4403             :  * ALTER TABLE, because they don't preserve tuple TIDs and so the pending
    4404             :  * events would try to fetch the wrong tuples.  It might be overly cautious
    4405             :  * in other cases, but again it seems better to err on the side of paranoia.
    4406             :  *
    4407             :  * REINDEX calls this with "rel" referencing the index to be rebuilt; here
    4408             :  * we are worried about active indexscans on the index.  The trigger-event
    4409             :  * check can be skipped, since we are doing no damage to the parent table.
    4410             :  *
    4411             :  * The statement name (eg, "ALTER TABLE") is passed for use in error messages.
    4412             :  */
    4413             : void
    4414      177776 : CheckTableNotInUse(Relation rel, const char *stmt)
    4415             : {
    4416             :     int         expected_refcnt;
    4417             : 
    4418      177776 :     expected_refcnt = rel->rd_isnailed ? 2 : 1;
    4419      177776 :     if (rel->rd_refcnt != expected_refcnt)
    4420          42 :         ereport(ERROR,
    4421             :                 (errcode(ERRCODE_OBJECT_IN_USE),
    4422             :         /* translator: first %s is a SQL command, eg ALTER TABLE */
    4423             :                  errmsg("cannot %s \"%s\" because it is being used by active queries in this session",
    4424             :                         stmt, RelationGetRelationName(rel))));
    4425             : 
    4426      177734 :     if (rel->rd_rel->relkind != RELKIND_INDEX &&
    4427      290218 :         rel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX &&
    4428      143988 :         AfterTriggerPendingOnRel(RelationGetRelid(rel)))
    4429          18 :         ereport(ERROR,
    4430             :                 (errcode(ERRCODE_OBJECT_IN_USE),
    4431             :         /* translator: first %s is a SQL command, eg ALTER TABLE */
    4432             :                  errmsg("cannot %s \"%s\" because it has pending trigger events",
    4433             :                         stmt, RelationGetRelationName(rel))));
    4434      177716 : }
    4435             : 
    4436             : /*
    4437             :  * CheckAlterTableIsSafe
    4438             :  *      Verify that it's safe to allow ALTER TABLE on this relation.
    4439             :  *
    4440             :  * This consists of CheckTableNotInUse() plus a check that the relation
    4441             :  * isn't another session's temp table.  We must split out the temp-table
    4442             :  * check because there are callers of CheckTableNotInUse() that don't want
    4443             :  * that, notably DROP TABLE.  (We must allow DROP or we couldn't clean out
    4444             :  * an orphaned temp schema.)  Compare truncate_check_activity().
    4445             :  */
    4446             : static void
    4447       63480 : CheckAlterTableIsSafe(Relation rel)
    4448             : {
    4449             :     /*
    4450             :      * Don't allow ALTER on temp tables of other backends.  Their local buffer
    4451             :      * manager is not going to cope if we need to change the table's contents.
    4452             :      * Even if we don't, there may be optimizations that assume temp tables
    4453             :      * aren't subject to such interference.
    4454             :      */
    4455       63480 :     if (RELATION_IS_OTHER_TEMP(rel))
    4456           0 :         ereport(ERROR,
    4457             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    4458             :                  errmsg("cannot alter temporary tables of other sessions")));
    4459             : 
    4460             :     /*
    4461             :      * Also check for active uses of the relation in the current transaction,
    4462             :      * including open scans and pending AFTER trigger events.
    4463             :      */
    4464       63480 :     CheckTableNotInUse(rel, "ALTER TABLE");
    4465       63444 : }
    4466             : 
    4467             : /*
    4468             :  * AlterTableLookupRelation
    4469             :  *      Look up, and lock, the OID for the relation named by an alter table
    4470             :  *      statement.
    4471             :  */
    4472             : Oid
    4473       33692 : AlterTableLookupRelation(AlterTableStmt *stmt, LOCKMODE lockmode)
    4474             : {
    4475       67284 :     return RangeVarGetRelidExtended(stmt->relation, lockmode,
    4476       33692 :                                     stmt->missing_ok ? RVR_MISSING_OK : 0,
    4477             :                                     RangeVarCallbackForAlterRelation,
    4478             :                                     stmt);
    4479             : }
    4480             : 
    4481             : /*
    4482             :  * AlterTable
    4483             :  *      Execute ALTER TABLE, which can be a list of subcommands
    4484             :  *
    4485             :  * ALTER TABLE is performed in three phases:
    4486             :  *      1. Examine subcommands and perform pre-transformation checking.
    4487             :  *      2. Validate and transform subcommands, and update system catalogs.
    4488             :  *      3. Scan table(s) to check new constraints, and optionally recopy
    4489             :  *         the data into new table(s).
    4490             :  * Phase 3 is not performed unless one or more of the subcommands requires
    4491             :  * it.  The intention of this design is to allow multiple independent
    4492             :  * updates of the table schema to be performed with only one pass over the
    4493             :  * data.
    4494             :  *
    4495             :  * ATPrepCmd performs phase 1.  A "work queue" entry is created for
    4496             :  * each table to be affected (there may be multiple affected tables if the
    4497             :  * commands traverse a table inheritance hierarchy).  Also we do preliminary
    4498             :  * validation of the subcommands.  Because earlier subcommands may change
    4499             :  * the catalog state seen by later commands, there are limits to what can
    4500             :  * be done in this phase.  Generally, this phase acquires table locks,
    4501             :  * checks permissions and relkind, and recurses to find child tables.
    4502             :  *
    4503             :  * ATRewriteCatalogs performs phase 2 for each affected table.
    4504             :  * Certain subcommands need to be performed before others to avoid
    4505             :  * unnecessary conflicts; for example, DROP COLUMN should come before
    4506             :  * ADD COLUMN.  Therefore phase 1 divides the subcommands into multiple
    4507             :  * lists, one for each logical "pass" of phase 2.
    4508             :  *
    4509             :  * ATRewriteTables performs phase 3 for those tables that need it.
    4510             :  *
    4511             :  * For most subcommand types, phases 2 and 3 do no explicit recursion,
    4512             :  * since phase 1 already does it.  However, for certain subcommand types
    4513             :  * it is only possible to determine how to recurse at phase 2 time; for
    4514             :  * those cases, phase 1 sets the cmd->recurse flag.
    4515             :  *
    4516             :  * Thanks to the magic of MVCC, an error anywhere along the way rolls back
    4517             :  * the whole operation; we don't have to do anything special to clean up.
    4518             :  *
    4519             :  * The caller must lock the relation, with an appropriate lock level
    4520             :  * for the subcommands requested, using AlterTableGetLockLevel(stmt->cmds)
    4521             :  * or higher. We pass the lock level down
    4522             :  * so that we can apply it recursively to inherited tables. Note that the
    4523             :  * lock level we want as we recurse might well be higher than required for
    4524             :  * that specific subcommand. So we pass down the overall lock requirement,
    4525             :  * rather than reassess it at lower levels.
    4526             :  *
    4527             :  * The caller also provides a "context" which is to be passed back to
    4528             :  * utility.c when we need to execute a subcommand such as CREATE INDEX.
    4529             :  * Some of the fields therein, such as the relid, are used here as well.
    4530             :  */
    4531             : void
    4532       33454 : AlterTable(AlterTableStmt *stmt, LOCKMODE lockmode,
    4533             :            AlterTableUtilityContext *context)
    4534             : {
    4535             :     Relation    rel;
    4536             : 
    4537             :     /* Caller is required to provide an adequate lock. */
    4538       33454 :     rel = relation_open(context->relid, NoLock);
    4539             : 
    4540       33454 :     CheckAlterTableIsSafe(rel);
    4541             : 
    4542       33436 :     ATController(stmt, rel, stmt->cmds, stmt->relation->inh, lockmode, context);
    4543       29392 : }
    4544             : 
    4545             : /*
    4546             :  * AlterTableInternal
    4547             :  *
    4548             :  * ALTER TABLE with target specified by OID
    4549             :  *
    4550             :  * We do not reject if the relation is already open, because it's quite
    4551             :  * likely that one or more layers of caller have it open.  That means it
    4552             :  * is unsafe to use this entry point for alterations that could break
    4553             :  * existing query plans.  On the assumption it's not used for such, we
    4554             :  * don't have to reject pending AFTER triggers, either.
    4555             :  *
    4556             :  * Also, since we don't have an AlterTableUtilityContext, this cannot be
    4557             :  * used for any subcommand types that require parse transformation or
    4558             :  * could generate subcommands that have to be passed to ProcessUtility.
    4559             :  */
    4560             : void
    4561         278 : AlterTableInternal(Oid relid, List *cmds, bool recurse)
    4562             : {
    4563             :     Relation    rel;
    4564         278 :     LOCKMODE    lockmode = AlterTableGetLockLevel(cmds);
    4565             : 
    4566         278 :     rel = relation_open(relid, lockmode);
    4567             : 
    4568         278 :     EventTriggerAlterTableRelid(relid);
    4569             : 
    4570         278 :     ATController(NULL, rel, cmds, recurse, lockmode, NULL);
    4571         278 : }
    4572             : 
    4573             : /*
    4574             :  * AlterTableGetLockLevel
    4575             :  *
    4576             :  * Sets the overall lock level required for the supplied list of subcommands.
    4577             :  * Policy for doing this set according to needs of AlterTable(), see
    4578             :  * comments there for overall explanation.
    4579             :  *
    4580             :  * Function is called before and after parsing, so it must give same
    4581             :  * answer each time it is called. Some subcommands are transformed
    4582             :  * into other subcommand types, so the transform must never be made to a
    4583             :  * lower lock level than previously assigned. All transforms are noted below.
    4584             :  *
    4585             :  * Since this is called before we lock the table we cannot use table metadata
    4586             :  * to influence the type of lock we acquire.
    4587             :  *
    4588             :  * There should be no lockmodes hardcoded into the subcommand functions. All
    4589             :  * lockmode decisions for ALTER TABLE are made here only. The one exception is
    4590             :  * ALTER TABLE RENAME which is treated as a different statement type T_RenameStmt
    4591             :  * and does not travel through this section of code and cannot be combined with
    4592             :  * any of the subcommands given here.
    4593             :  *
    4594             :  * Note that Hot Standby only knows about AccessExclusiveLocks on the primary
    4595             :  * so any changes that might affect SELECTs running on standbys need to use
    4596             :  * AccessExclusiveLocks even if you think a lesser lock would do, unless you
    4597             :  * have a solution for that also.
    4598             :  *
    4599             :  * Also note that pg_dump uses only an AccessShareLock, meaning that anything
    4600             :  * that takes a lock less than AccessExclusiveLock can change object definitions
    4601             :  * while pg_dump is running. Be careful to check that the appropriate data is
    4602             :  * derived by pg_dump using an MVCC snapshot, rather than syscache lookups,
    4603             :  * otherwise we might end up with an inconsistent dump that can't restore.
    4604             :  */
    4605             : LOCKMODE
    4606       33970 : AlterTableGetLockLevel(List *cmds)
    4607             : {
    4608             :     /*
    4609             :      * This only works if we read catalog tables using MVCC snapshots.
    4610             :      */
    4611             :     ListCell   *lcmd;
    4612       33970 :     LOCKMODE    lockmode = ShareUpdateExclusiveLock;
    4613             : 
    4614       69136 :     foreach(lcmd, cmds)
    4615             :     {
    4616       35166 :         AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);
    4617       35166 :         LOCKMODE    cmd_lockmode = AccessExclusiveLock; /* default for compiler */
    4618             : 
    4619       35166 :         switch (cmd->subtype)
    4620             :         {
    4621             :                 /*
    4622             :                  * These subcommands rewrite the heap, so require full locks.
    4623             :                  */
    4624        3742 :             case AT_AddColumn:  /* may rewrite heap, in some cases and visible
    4625             :                                  * to SELECT */
    4626             :             case AT_SetAccessMethod:    /* must rewrite heap */
    4627             :             case AT_SetTableSpace:  /* must rewrite heap */
    4628             :             case AT_AlterColumnType:    /* must rewrite heap */
    4629        3742 :                 cmd_lockmode = AccessExclusiveLock;
    4630        3742 :                 break;
    4631             : 
    4632             :                 /*
    4633             :                  * These subcommands may require addition of toast tables. If
    4634             :                  * we add a toast table to a table currently being scanned, we
    4635             :                  * might miss data added to the new toast table by concurrent
    4636             :                  * insert transactions.
    4637             :                  */
    4638         238 :             case AT_SetStorage: /* may add toast tables, see
    4639             :                                  * ATRewriteCatalogs() */
    4640         238 :                 cmd_lockmode = AccessExclusiveLock;
    4641         238 :                 break;
    4642             : 
    4643             :                 /*
    4644             :                  * Removing constraints can affect SELECTs that have been
    4645             :                  * optimized assuming the constraint holds true. See also
    4646             :                  * CloneFkReferenced.
    4647             :                  */
    4648        1130 :             case AT_DropConstraint: /* as DROP INDEX */
    4649             :             case AT_DropNotNull:    /* may change some SQL plans */
    4650        1130 :                 cmd_lockmode = AccessExclusiveLock;
    4651        1130 :                 break;
    4652             : 
    4653             :                 /*
    4654             :                  * Subcommands that may be visible to concurrent SELECTs
    4655             :                  */
    4656        1794 :             case AT_DropColumn: /* change visible to SELECT */
    4657             :             case AT_AddColumnToView:    /* CREATE VIEW */
    4658             :             case AT_DropOids:   /* used to equiv to DropColumn */
    4659             :             case AT_EnableAlwaysRule:   /* may change SELECT rules */
    4660             :             case AT_EnableReplicaRule:  /* may change SELECT rules */
    4661             :             case AT_EnableRule: /* may change SELECT rules */
    4662             :             case AT_DisableRule:    /* may change SELECT rules */
    4663        1794 :                 cmd_lockmode = AccessExclusiveLock;
    4664        1794 :                 break;
    4665             : 
    4666             :                 /*
    4667             :                  * Changing owner may remove implicit SELECT privileges
    4668             :                  */
    4669        2082 :             case AT_ChangeOwner:    /* change visible to SELECT */
    4670        2082 :                 cmd_lockmode = AccessExclusiveLock;
    4671        2082 :                 break;
    4672             : 
    4673             :                 /*
    4674             :                  * Changing foreign table options may affect optimization.
    4675             :                  */
    4676         254 :             case AT_GenericOptions:
    4677             :             case AT_AlterColumnGenericOptions:
    4678         254 :                 cmd_lockmode = AccessExclusiveLock;
    4679         254 :                 break;
    4680             : 
    4681             :                 /*
    4682             :                  * These subcommands affect write operations only.
    4683             :                  */
    4684         342 :             case AT_EnableTrig:
    4685             :             case AT_EnableAlwaysTrig:
    4686             :             case AT_EnableReplicaTrig:
    4687             :             case AT_EnableTrigAll:
    4688             :             case AT_EnableTrigUser:
    4689             :             case AT_DisableTrig:
    4690             :             case AT_DisableTrigAll:
    4691             :             case AT_DisableTrigUser:
    4692         342 :                 cmd_lockmode = ShareRowExclusiveLock;
    4693         342 :                 break;
    4694             : 
    4695             :                 /*
    4696             :                  * These subcommands affect write operations only. XXX
    4697             :                  * Theoretically, these could be ShareRowExclusiveLock.
    4698             :                  */
    4699        2962 :             case AT_ColumnDefault:
    4700             :             case AT_CookedColumnDefault:
    4701             :             case AT_AlterConstraint:
    4702             :             case AT_AddIndex:   /* from ADD CONSTRAINT */
    4703             :             case AT_AddIndexConstraint:
    4704             :             case AT_ReplicaIdentity:
    4705             :             case AT_SetNotNull:
    4706             :             case AT_EnableRowSecurity:
    4707             :             case AT_DisableRowSecurity:
    4708             :             case AT_ForceRowSecurity:
    4709             :             case AT_NoForceRowSecurity:
    4710             :             case AT_AddIdentity:
    4711             :             case AT_DropIdentity:
    4712             :             case AT_SetIdentity:
    4713             :             case AT_SetExpression:
    4714             :             case AT_DropExpression:
    4715             :             case AT_SetCompression:
    4716        2962 :                 cmd_lockmode = AccessExclusiveLock;
    4717        2962 :                 break;
    4718             : 
    4719       15956 :             case AT_AddConstraint:
    4720             :             case AT_ReAddConstraint:    /* becomes AT_AddConstraint */
    4721             :             case AT_ReAddDomainConstraint:  /* becomes AT_AddConstraint */
    4722       15956 :                 if (IsA(cmd->def, Constraint))
    4723             :                 {
    4724       15956 :                     Constraint *con = (Constraint *) cmd->def;
    4725             : 
    4726       15956 :                     switch (con->contype)
    4727             :                     {
    4728       12146 :                         case CONSTR_EXCLUSION:
    4729             :                         case CONSTR_PRIMARY:
    4730             :                         case CONSTR_UNIQUE:
    4731             : 
    4732             :                             /*
    4733             :                              * Cases essentially the same as CREATE INDEX. We
    4734             :                              * could reduce the lock strength to ShareLock if
    4735             :                              * we can work out how to allow concurrent catalog
    4736             :                              * updates. XXX Might be set down to
    4737             :                              * ShareRowExclusiveLock but requires further
    4738             :                              * analysis.
    4739             :                              */
    4740       12146 :                             cmd_lockmode = AccessExclusiveLock;
    4741       12146 :                             break;
    4742        2618 :                         case CONSTR_FOREIGN:
    4743             : 
    4744             :                             /*
    4745             :                              * We add triggers to both tables when we add a
    4746             :                              * Foreign Key, so the lock level must be at least
    4747             :                              * as strong as CREATE TRIGGER.
    4748             :                              */
    4749        2618 :                             cmd_lockmode = ShareRowExclusiveLock;
    4750        2618 :                             break;
    4751             : 
    4752        1192 :                         default:
    4753        1192 :                             cmd_lockmode = AccessExclusiveLock;
    4754             :                     }
    4755             :                 }
    4756       15956 :                 break;
    4757             : 
    4758             :                 /*
    4759             :                  * These subcommands affect inheritance behaviour. Queries
    4760             :                  * started before us will continue to see the old inheritance
    4761             :                  * behaviour, while queries started after we commit will see
    4762             :                  * new behaviour. No need to prevent reads or writes to the
    4763             :                  * subtable while we hook it up though. Changing the TupDesc
    4764             :                  * may be a problem, so keep highest lock.
    4765             :                  */
    4766         558 :             case AT_AddInherit:
    4767             :             case AT_DropInherit:
    4768         558 :                 cmd_lockmode = AccessExclusiveLock;
    4769         558 :                 break;
    4770             : 
    4771             :                 /*
    4772             :                  * These subcommands affect implicit row type conversion. They
    4773             :                  * have affects similar to CREATE/DROP CAST on queries. don't
    4774             :                  * provide for invalidating parse trees as a result of such
    4775             :                  * changes, so we keep these at AccessExclusiveLock.
    4776             :                  */
    4777          72 :             case AT_AddOf:
    4778             :             case AT_DropOf:
    4779          72 :                 cmd_lockmode = AccessExclusiveLock;
    4780          72 :                 break;
    4781             : 
    4782             :                 /*
    4783             :                  * Only used by CREATE OR REPLACE VIEW which must conflict
    4784             :                  * with an SELECTs currently using the view.
    4785             :                  */
    4786         194 :             case AT_ReplaceRelOptions:
    4787         194 :                 cmd_lockmode = AccessExclusiveLock;
    4788         194 :                 break;
    4789             : 
    4790             :                 /*
    4791             :                  * These subcommands affect general strategies for performance
    4792             :                  * and maintenance, though don't change the semantic results
    4793             :                  * from normal data reads and writes. Delaying an ALTER TABLE
    4794             :                  * behind currently active writes only delays the point where
    4795             :                  * the new strategy begins to take effect, so there is no
    4796             :                  * benefit in waiting. In this case the minimum restriction
    4797             :                  * applies: we don't currently allow concurrent catalog
    4798             :                  * updates.
    4799             :                  */
    4800         234 :             case AT_SetStatistics:  /* Uses MVCC in getTableAttrs() */
    4801             :             case AT_ClusterOn:  /* Uses MVCC in getIndexes() */
    4802             :             case AT_DropCluster:    /* Uses MVCC in getIndexes() */
    4803             :             case AT_SetOptions: /* Uses MVCC in getTableAttrs() */
    4804             :             case AT_ResetOptions:   /* Uses MVCC in getTableAttrs() */
    4805         234 :                 cmd_lockmode = ShareUpdateExclusiveLock;
    4806         234 :                 break;
    4807             : 
    4808         112 :             case AT_SetLogged:
    4809             :             case AT_SetUnLogged:
    4810         112 :                 cmd_lockmode = AccessExclusiveLock;
    4811         112 :                 break;
    4812             : 
    4813         482 :             case AT_ValidateConstraint: /* Uses MVCC in getConstraints() */
    4814         482 :                 cmd_lockmode = ShareUpdateExclusiveLock;
    4815         482 :                 break;
    4816             : 
    4817             :                 /*
    4818             :                  * Rel options are more complex than first appears. Options
    4819             :                  * are set here for tables, views and indexes; for historical
    4820             :                  * reasons these can all be used with ALTER TABLE, so we can't
    4821             :                  * decide between them using the basic grammar.
    4822             :                  */
    4823         770 :             case AT_SetRelOptions:  /* Uses MVCC in getIndexes() and
    4824             :                                      * getTables() */
    4825             :             case AT_ResetRelOptions:    /* Uses MVCC in getIndexes() and
    4826             :                                          * getTables() */
    4827         770 :                 cmd_lockmode = AlterTableGetRelOptionsLockLevel((List *) cmd->def);
    4828         770 :                 break;
    4829             : 
    4830        2962 :             case AT_AttachPartition:
    4831        2962 :                 cmd_lockmode = ShareUpdateExclusiveLock;
    4832        2962 :                 break;
    4833             : 
    4834         602 :             case AT_DetachPartition:
    4835         602 :                 if (((PartitionCmd *) cmd->def)->concurrent)
    4836         164 :                     cmd_lockmode = ShareUpdateExclusiveLock;
    4837             :                 else
    4838         438 :                     cmd_lockmode = AccessExclusiveLock;
    4839         602 :                 break;
    4840             : 
    4841          20 :             case AT_DetachPartitionFinalize:
    4842          20 :                 cmd_lockmode = ShareUpdateExclusiveLock;
    4843          20 :                 break;
    4844             : 
    4845         660 :             case AT_MergePartitions:
    4846             :             case AT_SplitPartition:
    4847         660 :                 cmd_lockmode = AccessExclusiveLock;
    4848         660 :                 break;
    4849             : 
    4850           0 :             default:            /* oops */
    4851           0 :                 elog(ERROR, "unrecognized alter table type: %d",
    4852             :                      (int) cmd->subtype);
    4853             :                 break;
    4854             :         }
    4855             : 
    4856             :         /*
    4857             :          * Take the greatest lockmode from any subcommand
    4858             :          */
    4859       35166 :         if (cmd_lockmode > lockmode)
    4860       29526 :             lockmode = cmd_lockmode;
    4861             :     }
    4862             : 
    4863       33970 :     return lockmode;
    4864             : }
    4865             : 
    4866             : /*
    4867             :  * ATController provides top level control over the phases.
    4868             :  *
    4869             :  * parsetree is passed in to allow it to be passed to event triggers
    4870             :  * when requested.
    4871             :  */
    4872             : static void
    4873       33714 : ATController(AlterTableStmt *parsetree,
    4874             :              Relation rel, List *cmds, bool recurse, LOCKMODE lockmode,
    4875             :              AlterTableUtilityContext *context)
    4876             : {
    4877       33714 :     List       *wqueue = NIL;
    4878             :     ListCell   *lcmd;
    4879             : 
    4880             :     /* Phase 1: preliminary examination of commands, create work queue */
    4881       68206 :     foreach(lcmd, cmds)
    4882             :     {
    4883       34904 :         AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);
    4884             : 
    4885       34904 :         ATPrepCmd(&wqueue, rel, cmd, recurse, false, lockmode, context);
    4886             :     }
    4887             : 
    4888             :     /* Close the relation, but keep lock until commit */
    4889       33302 :     relation_close(rel, NoLock);
    4890             : 
    4891             :     /* Phase 2: update system catalogs */
    4892       33302 :     ATRewriteCatalogs(&wqueue, lockmode, context);
    4893             : 
    4894             :     /* Phase 3: scan/rewrite tables as needed, and run afterStmts */
    4895       30156 :     ATRewriteTables(parsetree, &wqueue, lockmode, context);
    4896       29670 : }
    4897             : 
    4898             : /*
    4899             :  * ATPrepCmd
    4900             :  *
    4901             :  * Traffic cop for ALTER TABLE Phase 1 operations, including simple
    4902             :  * recursion and permission checks.
    4903             :  *
    4904             :  * Caller must have acquired appropriate lock type on relation already.
    4905             :  * This lock should be held until commit.
    4906             :  */
    4907             : static void
    4908       35844 : ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
    4909             :           bool recurse, bool recursing, LOCKMODE lockmode,
    4910             :           AlterTableUtilityContext *context)
    4911             : {
    4912             :     AlteredTableInfo *tab;
    4913       35844 :     AlterTablePass pass = AT_PASS_UNSET;
    4914             : 
    4915             :     /* Find or create work queue entry for this table */
    4916       35844 :     tab = ATGetQueueEntry(wqueue, rel);
    4917             : 
    4918             :     /*
    4919             :      * Disallow any ALTER TABLE other than ALTER TABLE DETACH FINALIZE on
    4920             :      * partitions that are pending detach.
    4921             :      */
    4922       35844 :     if (rel->rd_rel->relispartition &&
    4923        2800 :         cmd->subtype != AT_DetachPartitionFinalize &&
    4924        1400 :         PartitionHasPendingDetach(RelationGetRelid(rel)))
    4925           2 :         ereport(ERROR,
    4926             :                 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    4927             :                 errmsg("cannot alter partition \"%s\" with an incomplete detach",
    4928             :                        RelationGetRelationName(rel)),
    4929             :                 errhint("Use ALTER TABLE ... DETACH PARTITION ... FINALIZE to complete the pending detach operation."));
    4930             : 
    4931             :     /*
    4932             :      * Copy the original subcommand for each table, so we can scribble on it.
    4933             :      * This avoids conflicts when different child tables need to make
    4934             :      * different parse transformations (for example, the same column may have
    4935             :      * different column numbers in different children).
    4936             :      */
    4937       35842 :     cmd = copyObject(cmd);
    4938             : 
    4939             :     /*
    4940             :      * Do permissions and relkind checking, recursion to child tables if
    4941             :      * needed, and any additional phase-1 processing needed.  (But beware of
    4942             :      * adding any processing that looks at table details that another
    4943             :      * subcommand could change.  In some cases we reject multiple subcommands
    4944             :      * that could try to change the same state in contrary ways.)
    4945             :      */
    4946       35842 :     switch (cmd->subtype)
    4947             :     {
    4948        2190 :         case AT_AddColumn:      /* ADD COLUMN */
    4949        2190 :             ATSimplePermissions(cmd->subtype, rel,
    4950             :                                 ATT_TABLE | ATT_PARTITIONED_TABLE |
    4951             :                                 ATT_COMPOSITE_TYPE | ATT_FOREIGN_TABLE);
    4952        2190 :             ATPrepAddColumn(wqueue, rel, recurse, recursing, false, cmd,
    4953             :                             lockmode, context);
    4954             :             /* Recursion occurs during execution phase */
    4955        2178 :             pass = AT_PASS_ADD_COL;
    4956        2178 :             break;
    4957          24 :         case AT_AddColumnToView:    /* add column via CREATE OR REPLACE VIEW */
    4958          24 :             ATSimplePermissions(cmd->subtype, rel, ATT_VIEW);
    4959          24 :             ATPrepAddColumn(wqueue, rel, recurse, recursing, true, cmd,
    4960             :                             lockmode, context);
    4961             :             /* Recursion occurs during execution phase */
    4962          24 :             pass = AT_PASS_ADD_COL;
    4963          24 :             break;
    4964         620 :         case AT_ColumnDefault:  /* ALTER COLUMN DEFAULT */
    4965             : 
    4966             :             /*
    4967             :              * We allow defaults on views so that INSERT into a view can have
    4968             :              * default-ish behavior.  This works because the rewriter
    4969             :              * substitutes default values into INSERTs before it expands
    4970             :              * rules.
    4971             :              */
    4972         620 :             ATSimplePermissions(cmd->subtype, rel,
    4973             :                                 ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_VIEW |
    4974             :                                 ATT_FOREIGN_TABLE);
    4975         620 :             ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
    4976             :             /* No command-specific prep needed */
    4977         620 :             pass = cmd->def ? AT_PASS_ADD_OTHERCONSTR : AT_PASS_DROP;
    4978         620 :             break;
    4979          80 :         case AT_CookedColumnDefault:    /* add a pre-cooked default */
    4980             :             /* This is currently used only in CREATE TABLE */
    4981             :             /* (so the permission check really isn't necessary) */
    4982          80 :             ATSimplePermissions(cmd->subtype, rel,
    4983             :                                 ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
    4984             :             /* This command never recurses */
    4985          80 :             pass = AT_PASS_ADD_OTHERCONSTR;
    4986          80 :             break;
    4987         172 :         case AT_AddIdentity:
    4988         172 :             ATSimplePermissions(cmd->subtype, rel,
    4989             :                                 ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_VIEW |
    4990             :                                 ATT_FOREIGN_TABLE);
    4991             :             /* Set up recursion for phase 2; no other prep needed */
    4992         172 :             if (recurse)
    4993         166 :                 cmd->recurse = true;
    4994         172 :             pass = AT_PASS_ADD_OTHERCONSTR;
    4995         172 :             break;
    4996          62 :         case AT_SetIdentity:
    4997          62 :             ATSimplePermissions(cmd->subtype, rel,
    4998             :                                 ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_VIEW |
    4999             :                                 ATT_FOREIGN_TABLE);
    5000             :             /* Set up recursion for phase 2; no other prep needed */
    5001          62 :             if (recurse)
    5002          56 :                 cmd->recurse = true;
    5003             :             /* This should run after AddIdentity, so do it in MISC pass */
    5004          62 :             pass = AT_PASS_MISC;
    5005          62 :             break;
    5006          56 :         case AT_DropIdentity:
    5007          56 :             ATSimplePermissions(cmd->subtype, rel,
    5008             :                                 ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_VIEW |
    5009             :                                 ATT_FOREIGN_TABLE);
    5010             :             /* Set up recursion for phase 2; no other prep needed */
    5011          56 :             if (recurse)
    5012          50 :                 cmd->recurse = true;
    5013          56 :             pass = AT_PASS_DROP;
    5014          56 :             break;
    5015         274 :         case AT_DropNotNull:    /* ALTER COLUMN DROP NOT NULL */
    5016         274 :             ATSimplePermissions(cmd->subtype, rel,
    5017             :                                 ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
    5018             :             /* Set up recursion for phase 2; no other prep needed */
    5019         268 :             if (recurse)
    5020         250 :                 cmd->recurse = true;
    5021         268 :             pass = AT_PASS_DROP;
    5022         268 :             break;
    5023         420 :         case AT_SetNotNull:     /* ALTER COLUMN SET NOT NULL */
    5024         420 :             ATSimplePermissions(cmd->subtype, rel,
    5025             :                                 ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
    5026             :             /* Set up recursion for phase 2; no other prep needed */
    5027         414 :             if (recurse)
    5028         384 :                 cmd->recurse = true;
    5029         414 :             pass = AT_PASS_COL_ATTRS;
    5030         414 :             break;
    5031         224 :         case AT_SetExpression:  /* ALTER COLUMN SET EXPRESSION */
    5032         224 :             ATSimplePermissions(cmd->subtype, rel,
    5033             :                                 ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
    5034         224 :             ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
    5035         224 :             pass = AT_PASS_SET_EXPRESSION;
    5036         224 :             break;
    5037          86 :         case AT_DropExpression: /* ALTER COLUMN DROP EXPRESSION */
    5038          86 :             ATSimplePermissions(cmd->subtype, rel,
    5039             :                                 ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
    5040          86 :             ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
    5041          86 :             ATPrepDropExpression(rel, cmd, recurse, recursing, lockmode);
    5042          62 :             pass = AT_PASS_DROP;
    5043          62 :             break;
    5044         164 :         case AT_SetStatistics:  /* ALTER COLUMN SET STATISTICS */
    5045         164 :             ATSimplePermissions(cmd->subtype, rel,
    5046             :                                 ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_MATVIEW |
    5047             :                                 ATT_INDEX | ATT_PARTITIONED_INDEX | ATT_FOREIGN_TABLE);
    5048         164 :             ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
    5049             :             /* No command-specific prep needed */
    5050         164 :             pass = AT_PASS_MISC;
    5051         164 :             break;
    5052          44 :         case AT_SetOptions:     /* ALTER COLUMN SET ( options ) */
    5053             :         case AT_ResetOptions:   /* ALTER COLUMN RESET ( options ) */
    5054          44 :             ATSimplePermissions(cmd->subtype, rel,
    5055             :                                 ATT_TABLE | ATT_PARTITIONED_TABLE |
    5056             :                                 ATT_MATVIEW | ATT_FOREIGN_TABLE);
    5057             :             /* This command never recurses */
    5058          32 :             pass = AT_PASS_MISC;
    5059          32 :             break;
    5060         260 :         case AT_SetStorage:     /* ALTER COLUMN SET STORAGE */
    5061         260 :             ATSimplePermissions(cmd->subtype, rel,
    5062             :                                 ATT_TABLE | ATT_PARTITIONED_TABLE |
    5063             :                                 ATT_MATVIEW | ATT_FOREIGN_TABLE);
    5064         260 :             ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
    5065             :             /* No command-specific prep needed */
    5066         260 :             pass = AT_PASS_MISC;
    5067         260 :             break;
    5068          78 :         case AT_SetCompression: /* ALTER COLUMN SET COMPRESSION */
    5069          78 :             ATSimplePermissions(cmd->subtype, rel,
    5070             :                                 ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_MATVIEW);
    5071             :             /* This command never recurses */
    5072             :             /* No command-specific prep needed */
    5073          78 :             pass = AT_PASS_MISC;
    5074          78 :             break;
    5075        1700 :         case AT_DropColumn:     /* DROP COLUMN */
    5076        1700 :             ATSimplePermissions(cmd->subtype, rel,
    5077             :                                 ATT_TABLE | ATT_PARTITIONED_TABLE |
    5078             :                                 ATT_COMPOSITE_TYPE | ATT_FOREIGN_TABLE);
    5079        1694 :             ATPrepDropColumn(wqueue, rel, recurse, recursing, cmd,
    5080             :                              lockmode, context);
    5081             :             /* Recursion occurs during execution phase */
    5082        1682 :             pass = AT_PASS_DROP;
    5083        1682 :             break;
    5084           0 :         case AT_AddIndex:       /* ADD INDEX */
    5085           0 :             ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_PARTITIONED_TABLE);
    5086             :             /* This command never recurses */
    5087             :             /* No command-specific prep needed */
    5088           0 :             pass = AT_PASS_ADD_INDEX;
    5089           0 :             break;
    5090       16420 :         case AT_AddConstraint:  /* ADD CONSTRAINT */
    5091       16420 :             ATSimplePermissions(cmd->subtype, rel,
    5092             :                                 ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
    5093       16420 :             ATPrepAddPrimaryKey(wqueue, rel, cmd, recurse, lockmode, context);
    5094       16390 :             if (recurse)
    5095             :             {
    5096             :                 /* recurses at exec time; lock descendants and set flag */
    5097       15996 :                 (void) find_all_inheritors(RelationGetRelid(rel), lockmode, NULL);
    5098       15996 :                 cmd->recurse = true;
    5099             :             }
    5100       16390 :             pass = AT_PASS_ADD_CONSTR;
    5101       16390 :             break;
    5102           0 :         case AT_AddIndexConstraint: /* ADD CONSTRAINT USING INDEX */
    5103           0 :             ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_PARTITIONED_TABLE);
    5104             :             /* This command never recurses */
    5105             :             /* No command-specific prep needed */
    5106           0 :             pass = AT_PASS_ADD_INDEXCONSTR;
    5107           0 :             break;
    5108         818 :         case AT_DropConstraint: /* DROP CONSTRAINT */
    5109         818 :             ATSimplePermissions(cmd->subtype, rel,
    5110             :                                 ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
    5111         818 :             ATCheckPartitionsNotInUse(rel, lockmode);
    5112             :             /* Other recursion occurs during execution phase */
    5113             :             /* No command-specific prep needed except saving recurse flag */
    5114         812 :             if (recurse)
    5115         776 :                 cmd->recurse = true;
    5116         812 :             pass = AT_PASS_DROP;
    5117         812 :             break;
    5118        1432 :         case AT_AlterColumnType:    /* ALTER COLUMN TYPE */
    5119        1432 :             ATSimplePermissions(cmd->subtype, rel,
    5120             :                                 ATT_TABLE | ATT_PARTITIONED_TABLE |
    5121             :                                 ATT_COMPOSITE_TYPE | ATT_FOREIGN_TABLE);
    5122             :             /* See comments for ATPrepAlterColumnType */
    5123        1432 :             cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, recurse, lockmode,
    5124             :                                       AT_PASS_UNSET, context);
    5125             :             Assert(cmd != NULL);
    5126             :             /* Performs own recursion */
    5127        1426 :             ATPrepAlterColumnType(wqueue, tab, rel, recurse, recursing, cmd,
    5128             :                                   lockmode, context);
    5129        1228 :             pass = AT_PASS_ALTER_TYPE;
    5130        1228 :             break;
    5131         172 :         case AT_AlterColumnGenericOptions:
    5132         172 :             ATSimplePermissions(cmd->subtype, rel, ATT_FOREIGN_TABLE);
    5133             :             /* This command never recurses */
    5134             :             /* No command-specific prep needed */
    5135         172 :             pass = AT_PASS_MISC;
    5136         172 :             break;
    5137        2058 :         case AT_ChangeOwner:    /* ALTER OWNER */
    5138             :             /* This command never recurses */
    5139             :             /* No command-specific prep needed */
    5140        2058 :             pass = AT_PASS_MISC;
    5141        2058 :             break;
    5142          64 :         case AT_ClusterOn:      /* CLUSTER ON */
    5143             :         case AT_DropCluster:    /* SET WITHOUT CLUSTER */
    5144          64 :             ATSimplePermissions(cmd->subtype, rel,
    5145             :                                 ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_MATVIEW);
    5146             :             /* These commands never recurse */
    5147             :             /* No command-specific prep needed */
    5148          64 :             pass = AT_PASS_MISC;
    5149          64 :             break;
    5150         112 :         case AT_SetLogged:      /* SET LOGGED */
    5151             :         case AT_SetUnLogged:    /* SET UNLOGGED */
    5152         112 :             ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_SEQUENCE);
    5153         100 :             if (tab->chgPersistence)
    5154           0 :                 ereport(ERROR,
    5155             :                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    5156             :                          errmsg("cannot change persistence setting twice")));
    5157         100 :             ATPrepChangePersistence(tab, rel, cmd->subtype == AT_SetLogged);
    5158          88 :             pass = AT_PASS_MISC;
    5159          88 :             break;
    5160           6 :         case AT_DropOids:       /* SET WITHOUT OIDS */
    5161           6 :             ATSimplePermissions(cmd->subtype, rel,
    5162             :                                 ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
    5163           6 :             pass = AT_PASS_DROP;
    5164           6 :             break;
    5165         128 :         case AT_SetAccessMethod:    /* SET ACCESS METHOD */
    5166         128 :             ATSimplePermissions(cmd->subtype, rel,
    5167             :                                 ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_MATVIEW);
    5168             : 
    5169             :             /* check if another access method change was already requested */
    5170         128 :             if (tab->chgAccessMethod)
    5171          18 :                 ereport(ERROR,
    5172             :                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    5173             :                          errmsg("cannot have multiple SET ACCESS METHOD subcommands")));
    5174             : 
    5175         110 :             ATPrepSetAccessMethod(tab, rel, cmd->name);
    5176         110 :             pass = AT_PASS_MISC;    /* does not matter; no work in Phase 2 */
    5177         110 :             break;
    5178         164 :         case AT_SetTableSpace:  /* SET TABLESPACE */
    5179         164 :             ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_PARTITIONED_TABLE |
    5180             :                                 ATT_MATVIEW | ATT_INDEX | ATT_PARTITIONED_INDEX);
    5181             :             /* This command never recurses */
    5182         164 :             ATPrepSetTableSpace(tab, rel, cmd->name, lockmode);
    5183         164 :             pass = AT_PASS_MISC;    /* doesn't actually matter */
    5184         164 :             break;
    5185         962 :         case AT_SetRelOptions:  /* SET (...) */
    5186             :         case AT_ResetRelOptions:    /* RESET (...) */
    5187             :         case AT_ReplaceRelOptions:  /* reset them all, then set just these */
    5188         962 :             ATSimplePermissions(cmd->subtype, rel,
    5189             :                                 ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_VIEW |
    5190             :                                 ATT_MATVIEW | ATT_INDEX);
    5191             :             /* This command never recurses */
    5192             :             /* No command-specific prep needed */
    5193         960 :             pass = AT_PASS_MISC;
    5194         960 :             break;
    5195         464 :         case AT_AddInherit:     /* INHERIT */
    5196         464 :             ATSimplePermissions(cmd->subtype, rel,
    5197             :                                 ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
    5198             :             /* This command never recurses */
    5199         464 :             ATPrepAddInherit(rel);
    5200         446 :             pass = AT_PASS_MISC;
    5201         446 :             break;
    5202          94 :         case AT_DropInherit:    /* NO INHERIT */
    5203          94 :             ATSimplePermissions(cmd->subtype, rel,
    5204             :                                 ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
    5205             :             /* This command never recurses */
    5206             :             /* No command-specific prep needed */
    5207          94 :             pass = AT_PASS_MISC;
    5208          94 :             break;
    5209         300 :         case AT_AlterConstraint:    /* ALTER CONSTRAINT */
    5210         300 :             ATSimplePermissions(cmd->subtype, rel,
    5211             :                                 ATT_TABLE | ATT_PARTITIONED_TABLE);
    5212             :             /* Recursion occurs during execution phase */
    5213         294 :             if (recurse)
    5214         294 :                 cmd->recurse = true;
    5215         294 :             pass = AT_PASS_MISC;
    5216         294 :             break;
    5217         482 :         case AT_ValidateConstraint: /* VALIDATE CONSTRAINT */
    5218         482 :             ATSimplePermissions(cmd->subtype, rel,
    5219             :                                 ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
    5220             :             /* Recursion occurs during execution phase */
    5221             :             /* No command-specific prep needed except saving recurse flag */
    5222         482 :             if (recurse)
    5223         482 :                 cmd->recurse = true;
    5224         482 :             pass = AT_PASS_MISC;
    5225         482 :             break;
    5226         494 :         case AT_ReplicaIdentity:    /* REPLICA IDENTITY ... */
    5227         494 :             ATSimplePermissions(cmd->subtype, rel,
    5228             :                                 ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_MATVIEW);
    5229         494 :             pass = AT_PASS_MISC;
    5230             :             /* This command never recurses */
    5231             :             /* No command-specific prep needed */
    5232         494 :             break;
    5233         342 :         case AT_EnableTrig:     /* ENABLE TRIGGER variants */
    5234             :         case AT_EnableAlwaysTrig:
    5235             :         case AT_EnableReplicaTrig:
    5236             :         case AT_EnableTrigAll:
    5237             :         case AT_EnableTrigUser:
    5238             :         case AT_DisableTrig:    /* DISABLE TRIGGER variants */
    5239             :         case AT_DisableTrigAll:
    5240             :         case AT_DisableTrigUser:
    5241         342 :             ATSimplePermissions(cmd->subtype, rel,
    5242             :                                 ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
    5243             :             /* Set up recursion for phase 2; no other prep needed */
    5244         342 :             if (recurse)
    5245         314 :                 cmd->recurse = true;
    5246         342 :             pass = AT_PASS_MISC;
    5247         342 :             break;
    5248         598 :         case AT_EnableRule:     /* ENABLE/DISABLE RULE variants */
    5249             :         case AT_EnableAlwaysRule:
    5250             :         case AT_EnableReplicaRule:
    5251             :         case AT_DisableRule:
    5252             :         case AT_AddOf:          /* OF */
    5253             :         case AT_DropOf:         /* NOT OF */
    5254             :         case AT_EnableRowSecurity:
    5255             :         case AT_DisableRowSecurity:
    5256             :         case AT_ForceRowSecurity:
    5257             :         case AT_NoForceRowSecurity:
    5258         598 :             ATSimplePermissions(cmd->subtype, rel,
    5259             :                                 ATT_TABLE | ATT_PARTITIONED_TABLE);
    5260             :             /* These commands never recurse */
    5261             :             /* No command-specific prep needed */
    5262         598 :             pass = AT_PASS_MISC;
    5263         598 :             break;
    5264          58 :         case AT_GenericOptions:
    5265          58 :             ATSimplePermissions(cmd->subtype, rel, ATT_FOREIGN_TABLE);
    5266             :             /* No command-specific prep needed */
    5267          58 :             pass = AT_PASS_MISC;
    5268          58 :             break;
    5269        2950 :         case AT_AttachPartition:
    5270        2950 :             ATSimplePermissions(cmd->subtype, rel,
    5271             :                                 ATT_PARTITIONED_TABLE | ATT_PARTITIONED_INDEX);
    5272             :             /* No command-specific prep needed */
    5273        2944 :             pass = AT_PASS_MISC;
    5274        2944 :             break;
    5275         602 :         case AT_DetachPartition:
    5276         602 :             ATSimplePermissions(cmd->subtype, rel, ATT_PARTITIONED_TABLE);
    5277             :             /* No command-specific prep needed */
    5278         584 :             pass = AT_PASS_MISC;
    5279         584 :             break;
    5280          20 :         case AT_DetachPartitionFinalize:
    5281          20 :             ATSimplePermissions(cmd->subtype, rel, ATT_PARTITIONED_TABLE);
    5282             :             /* No command-specific prep needed */
    5283          14 :             pass = AT_PASS_MISC;
    5284          14 :             break;
    5285         648 :         case AT_MergePartitions:
    5286             :         case AT_SplitPartition:
    5287         648 :             ATSimplePermissions(cmd->subtype, rel, ATT_PARTITIONED_TABLE);
    5288             :             /* No command-specific prep needed */
    5289         642 :             pass = AT_PASS_MISC;
    5290         642 :             break;
    5291           0 :         default:                /* oops */
    5292           0 :             elog(ERROR, "unrecognized alter table type: %d",
    5293             :                  (int) cmd->subtype);
    5294             :             pass = AT_PASS_UNSET;   /* keep compiler quiet */
    5295             :             break;
    5296             :     }
    5297             :     Assert(pass > AT_PASS_UNSET);
    5298             : 
    5299             :     /* Add the subcommand to the appropriate list for phase 2 */
    5300       35420 :     tab->subcmds[pass] = lappend(tab->subcmds[pass], cmd);
    5301       35420 : }
    5302             : 
    5303             : /*
    5304             :  * ATRewriteCatalogs
    5305             :  *
    5306             :  * Traffic cop for ALTER TABLE Phase 2 operations.  Subcommands are
    5307             :  * dispatched in a "safe" execution order (designed to avoid unnecessary
    5308             :  * conflicts).
    5309             :  */
    5310             : static void
    5311       33302 : ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode,
    5312             :                   AlterTableUtilityContext *context)
    5313             : {
    5314             :     ListCell   *ltab;
    5315             : 
    5316             :     /*
    5317             :      * We process all the tables "in parallel", one pass at a time.  This is
    5318             :      * needed because we may have to propagate work from one table to another
    5319             :      * (specifically, ALTER TYPE on a foreign key's PK has to dispatch the
    5320             :      * re-adding of the foreign key constraint to the other table).  Work can
    5321             :      * only be propagated into later passes, however.
    5322             :      */
    5323      420000 :     for (AlterTablePass pass = 0; pass < AT_NUM_PASSES; pass++)
    5324             :     {
    5325             :         /* Go through each table that needs to be processed */
    5326      792950 :         foreach(ltab, *wqueue)
    5327             :         {
    5328      406252 :             AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
    5329      406252 :             List       *subcmds = tab->subcmds[pass];
    5330             :             ListCell   *lcmd;
    5331             : 
    5332      406252 :             if (subcmds == NIL)
    5333      348886 :                 continue;
    5334             : 
    5335             :             /*
    5336             :              * Open the relation and store it in tab.  This allows subroutines
    5337             :              * close and reopen, if necessary.  Appropriate lock was obtained
    5338             :              * by phase 1, needn't get it again.
    5339             :              */
    5340       57366 :             tab->rel = relation_open(tab->relid, NoLock);
    5341             : 
    5342      115368 :             foreach(lcmd, subcmds)
    5343       61148 :                 ATExecCmd(wqueue, tab,
    5344       61148 :                           lfirst_node(AlterTableCmd, lcmd),
    5345             :                           lockmode, pass, context);
    5346             : 
    5347             :             /*
    5348             :              * After the ALTER TYPE or SET EXPRESSION pass, do cleanup work
    5349             :              * (this is not done in ATExecAlterColumnType since it should be
    5350             :              * done only once if multiple columns of a table are altered).
    5351             :              */
    5352       54220 :             if (pass == AT_PASS_ALTER_TYPE || pass == AT_PASS_SET_EXPRESSION)
    5353        1302 :                 ATPostAlterTypeCleanup(wqueue, tab, lockmode);
    5354             : 
    5355       54220 :             if (tab->rel)
    5356             :             {
    5357       54220 :                 relation_close(tab->rel, NoLock);
    5358       54220 :                 tab->rel = NULL;
    5359             :             }
    5360             :         }
    5361             :     }
    5362             : 
    5363             :     /* Check to see if a toast table must be added. */
    5364       64706 :     foreach(ltab, *wqueue)
    5365             :     {
    5366       34550 :         AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
    5367             : 
    5368             :         /*
    5369             :          * If the table is source table of ATTACH PARTITION command, we did
    5370             :          * not modify anything about it that will change its toasting
    5371             :          * requirement, so no need to check.
    5372             :          */
    5373       34550 :         if (((tab->relkind == RELKIND_RELATION ||
    5374        6690 :               tab->relkind == RELKIND_PARTITIONED_TABLE) &&
    5375       32632 :              tab->partition_constraint == NULL) ||
    5376        4122 :             tab->relkind == RELKIND_MATVIEW)
    5377       30478 :             AlterTableCreateToastTable(tab->relid, (Datum) 0, lockmode);
    5378             :     }
    5379       30156 : }
    5380             : 
    5381             : /*
    5382             :  * ATExecCmd: dispatch a subcommand to appropriate execution routine
    5383             :  */
    5384             : static void
    5385       61148 : ATExecCmd(List **wqueue, AlteredTableInfo *tab,
    5386             :           AlterTableCmd *cmd, LOCKMODE lockmode, AlterTablePass cur_pass,
    5387             :           AlterTableUtilityContext *context)
    5388             : {
    5389       61148 :     ObjectAddress address = InvalidObjectAddress;
    5390       61148 :     Relation    rel = tab->rel;
    5391             : 
    5392       61148 :     switch (cmd->subtype)
    5393             :     {
    5394        2196 :         case AT_AddColumn:      /* ADD COLUMN */
    5395             :         case AT_AddColumnToView:    /* add column via CREATE OR REPLACE VIEW */
    5396        2196 :             address = ATExecAddColumn(wqueue, tab, rel, &cmd,
    5397        2196 :                                       cmd->recurse, false,
    5398             :                                       lockmode, cur_pass, context);
    5399        2058 :             break;
    5400         584 :         case AT_ColumnDefault:  /* ALTER COLUMN DEFAULT */
    5401         584 :             address = ATExecColumnDefault(rel, cmd->name, cmd->def, lockmode);
    5402         518 :             break;
    5403          80 :         case AT_CookedColumnDefault:    /* add a pre-cooked default */
    5404          80 :             address = ATExecCookedColumnDefault(rel, cmd->num, cmd->def);
    5405          80 :             break;
    5406         172 :         case AT_AddIdentity:
    5407         172 :             cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
    5408             :                                       cur_pass, context);
    5409             :             Assert(cmd != NULL);
    5410         160 :             address = ATExecAddIdentity(rel, cmd->name, cmd->def, lockmode, cmd->recurse, false);
    5411         106 :             break;
    5412          62 :         case AT_SetIdentity:
    5413          62 :             cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
    5414             :                                       cur_pass, context);
    5415             :             Assert(cmd != NULL);
    5416          62 :             address = ATExecSetIdentity(rel, cmd->name, cmd->def, lockmode, cmd->recurse, false);
    5417          38 :             break;
    5418          56 :         case AT_DropIdentity:
    5419          56 :             address = ATExecDropIdentity(rel, cmd->name, cmd->missing_ok, lockmode, cmd->recurse, false);
    5420          38 :             break;
    5421         268 :         case AT_DropNotNull:    /* ALTER COLUMN DROP NOT NULL */
    5422         268 :             address = ATExecDropNotNull(rel, cmd->name, cmd->recurse, lockmode);
    5423         166 :             break;
    5424         414 :         case AT_SetNotNull:     /* ALTER COLUMN SET NOT NULL */
    5425         414 :             address = ATExecSetNotNull(wqueue, rel, NULL, cmd->name,
    5426         414 :                                        cmd->recurse, false, lockmode);
    5427         384 :             break;
    5428         224 :         case AT_SetExpression:
    5429         224 :             address = ATExecSetExpression(tab, rel, cmd->name, cmd->def, lockmode);
    5430         194 :             break;
    5431          56 :         case AT_DropExpression:
    5432          56 :             address = ATExecDropExpression(rel, cmd->name, cmd->missing_ok, lockmode);
    5433          32 :             break;
    5434         164 :         case AT_SetStatistics:  /* ALTER COLUMN SET STATISTICS */
    5435         164 :             address = ATExecSetStatistics(rel, cmd->name, cmd->num, cmd->def, lockmode);
    5436         116 :             break;
    5437          26 :         case AT_SetOptions:     /* ALTER COLUMN SET ( options ) */
    5438          26 :             address = ATExecSetOptions(rel, cmd->name, cmd->def, false, lockmode);
    5439          26 :             break;
    5440           6 :         case AT_ResetOptions:   /* ALTER COLUMN RESET ( options ) */
    5441           6 :             address = ATExecSetOptions(rel, cmd->name, cmd->def, true, lockmode);
    5442           6 :             break;
    5443         260 :         case AT_SetStorage:     /* ALTER COLUMN SET STORAGE */
    5444         260 :             address = ATExecSetStorage(rel, cmd->name, cmd->def, lockmode);
    5445         248 :             break;
    5446          78 :         case AT_SetCompression: /* ALTER COLUMN SET COMPRESSION */
    5447          78 :             address = ATExecSetCompression(rel, cmd->name, cmd->def,
    5448             :                                            lockmode);
    5449          72 :             break;
    5450        1682 :         case AT_DropColumn:     /* DROP COLUMN */
    5451        1682 :             address = ATExecDropColumn(wqueue, rel, cmd->name,
    5452        1682 :                                        cmd->behavior, cmd->recurse, false,
    5453        1682 :                                        cmd->missing_ok, lockmode,
    5454             :                                        NULL);
    5455        1502 :             break;
    5456        1204 :         case AT_AddIndex:       /* ADD INDEX */
    5457        1204 :             address = ATExecAddIndex(tab, rel, (IndexStmt *) cmd->def, false,
    5458             :                                      lockmode);
    5459        1034 :             break;
    5460         456 :         case AT_ReAddIndex:     /* ADD INDEX */
    5461         456 :             address = ATExecAddIndex(tab, rel, (IndexStmt *) cmd->def, true,
    5462             :                                      lockmode);
    5463         456 :             break;
    5464          74 :         case AT_ReAddStatistics:    /* ADD STATISTICS */
    5465          74 :             address = ATExecAddStatistics(tab, rel, (CreateStatsStmt *) cmd->def,
    5466             :                                           true, lockmode);
    5467          74 :             break;
    5468       29160 :         case AT_AddConstraint:  /* ADD CONSTRAINT */
    5469             :             /* Transform the command only during initial examination */
    5470       29160 :             if (cur_pass == AT_PASS_ADD_CONSTR)
    5471       16360 :                 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd,
    5472       16390 :                                           cmd->recurse, lockmode,
    5473             :                                           cur_pass, context);
    5474             :             /* Depending on constraint type, might be no more work to do now */
    5475       29130 :             if (cmd != NULL)
    5476             :                 address =
    5477       12770 :                     ATExecAddConstraint(wqueue, tab, rel,
    5478       12770 :                                         (Constraint *) cmd->def,
    5479       12770 :                                         cmd->recurse, false, lockmode);
    5480       28450 :             break;
    5481         338 :         case AT_ReAddConstraint:    /* Re-add pre-existing check constraint */
    5482             :             address =
    5483         338 :                 ATExecAddConstraint(wqueue, tab, rel, (Constraint *) cmd->def,
    5484             :                                     true, true, lockmode);
    5485         326 :             break;
    5486          14 :         case AT_ReAddDomainConstraint:  /* Re-add pre-existing domain check
    5487             :                                          * constraint */
    5488             :             address =
    5489          14 :                 AlterDomainAddConstraint(((AlterDomainStmt *) cmd->def)->typeName,
    5490          14 :                                          ((AlterDomainStmt *) cmd->def)->def,
    5491             :                                          NULL);
    5492           8 :             break;
    5493          78 :         case AT_ReAddComment:   /* Re-add existing comment */
    5494          78 :             address = CommentObject((CommentStmt *) cmd->def);
    5495          78 :             break;
    5496       10860 :         case AT_AddIndexConstraint: /* ADD CONSTRAINT USING INDEX */
    5497       10860 :             address = ATExecAddIndexConstraint(tab, rel, (IndexStmt *) cmd->def,
    5498             :                                                lockmode);
    5499       10848 :             break;
    5500         294 :         case AT_AlterConstraint:    /* ALTER CONSTRAINT */
    5501         294 :             address = ATExecAlterConstraint(wqueue, rel,
    5502         294 :                                             castNode(ATAlterConstraint, cmd->def),
    5503         294 :                                             cmd->recurse, lockmode);
    5504         228 :             break;
    5505         482 :         case AT_ValidateConstraint: /* VALIDATE CONSTRAINT */
    5506         482 :             address = ATExecValidateConstraint(wqueue, rel, cmd->name, cmd->recurse,
    5507             :                                                false, lockmode);
    5508         476 :             break;
    5509         812 :         case AT_DropConstraint: /* DROP CONSTRAINT */
    5510         812 :             ATExecDropConstraint(rel, cmd->name, cmd->behavior,
    5511         812 :                                  cmd->recurse,
    5512         812 :                                  cmd->missing_ok, lockmode);
    5513         602 :             break;
    5514        1192 :         case AT_AlterColumnType:    /* ALTER COLUMN TYPE */
    5515             :             /* parse transformation was done earlier */
    5516        1192 :             address = ATExecAlterColumnType(tab, rel, cmd, lockmode);
    5517        1150 :             break;
    5518         172 :         case AT_AlterColumnGenericOptions:  /* ALTER COLUMN OPTIONS */
    5519             :             address =
    5520         172 :                 ATExecAlterColumnGenericOptions(rel, cmd->name,
    5521         172 :                                                 (List *) cmd->def, lockmode);
    5522         166 :             break;
    5523        2058 :         case AT_ChangeOwner:    /* ALTER OWNER */
    5524        2052 :             ATExecChangeOwner(RelationGetRelid(rel),
    5525        2058 :                               get_rolespec_oid(cmd->newowner, false),
    5526             :                               false, lockmode);
    5527        2040 :             break;
    5528          64 :         case AT_ClusterOn:      /* CLUSTER ON */
    5529          64 :             address = ATExecClusterOn(rel, cmd->name, lockmode);
    5530          58 :             break;
    5531          18 :         case AT_DropCluster:    /* SET WITHOUT CLUSTER */
    5532          18 :             ATExecDropCluster(rel, lockmode);
    5533          12 :             break;
    5534          88 :         case AT_SetLogged:      /* SET LOGGED */
    5535             :         case AT_SetUnLogged:    /* SET UNLOGGED */
    5536          88 :             break;
    5537           6 :         case AT_DropOids:       /* SET WITHOUT OIDS */
    5538             :             /* nothing to do here, oid columns don't exist anymore */
    5539           6 :             break;
    5540          92 :         case AT_SetAccessMethod:    /* SET ACCESS METHOD */
    5541             : 
    5542             :             /*
    5543             :              * Only do this for partitioned tables, for which this is just a
    5544             :              * catalog change.  Tables with storage are handled by Phase 3.
    5545             :              */
    5546          92 :             if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&
    5547          50 :                 tab->chgAccessMethod)
    5548          44 :                 ATExecSetAccessMethodNoStorage(rel, tab->newAccessMethod);
    5549          92 :             break;
    5550         164 :         case AT_SetTableSpace:  /* SET TABLESPACE */
    5551             : 
    5552             :             /*
    5553             :              * Only do this for partitioned tables and indexes, for which this
    5554             :              * is just a catalog change.  Other relation types which have
    5555             :              * storage are handled by Phase 3.
    5556             :              */
    5557         164 :             if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
    5558         152 :                 rel->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
    5559          36 :                 ATExecSetTableSpaceNoStorage(rel, tab->newTableSpace);
    5560             : 
    5561         158 :             break;
    5562         960 :         case AT_SetRelOptions:  /* SET (...) */
    5563             :         case AT_ResetRelOptions:    /* RESET (...) */
    5564             :         case AT_ReplaceRelOptions:  /* replace entire option list */
    5565         960 :             ATExecSetRelOptions(rel, (List *) cmd->def, cmd->subtype, lockmode);
    5566         908 :             break;
    5567         122 :         case AT_EnableTrig:     /* ENABLE TRIGGER name */
    5568         122 :             ATExecEnableDisableTrigger(rel, cmd->name,
    5569             :                                        TRIGGER_FIRES_ON_ORIGIN, false,
    5570         122 :                                        cmd->recurse,
    5571             :                                        lockmode);
    5572         122 :             break;
    5573          42 :         case AT_EnableAlwaysTrig:   /* ENABLE ALWAYS TRIGGER name */
    5574          42 :             ATExecEnableDisableTrigger(rel, cmd->name,
    5575             :                                        TRIGGER_FIRES_ALWAYS, false,
    5576          42 :                                        cmd->recurse,
    5577             :                                        lockmode);
    5578          42 :             break;
    5579          16 :         case AT_EnableReplicaTrig:  /* ENABLE REPLICA TRIGGER name */
    5580          16 :             ATExecEnableDisableTrigger(rel, cmd->name,
    5581             :                                        TRIGGER_FIRES_ON_REPLICA, false,
    5582          16 :                                        cmd->recurse,
    5583             :                                        lockmode);
    5584          16 :             break;
    5585         138 :         case AT_DisableTrig:    /* DISABLE TRIGGER name */
    5586         138 :             ATExecEnableDisableTrigger(rel, cmd->name,
    5587             :                                        TRIGGER_DISABLED, false,
    5588         138 :                                        cmd->recurse,
    5589             :                                        lockmode);
    5590         138 :             break;
    5591           0 :         case AT_EnableTrigAll:  /* ENABLE TRIGGER ALL */
    5592           0 :             ATExecEnableDisableTrigger(rel, NULL,
    5593             :                                        TRIGGER_FIRES_ON_ORIGIN, false,
    5594           0 :                                        cmd->recurse,
    5595             :                                        lockmode);
    5596           0 :             break;
    5597          12 :         case AT_DisableTrigAll: /* DISABLE TRIGGER ALL */
    5598          12 :             ATExecEnableDisableTrigger(rel, NULL,
    5599             :                                        TRIGGER_DISABLED, false,
    5600          12 :                                        cmd->recurse,
    5601             :                                        lockmode);
    5602          12 :             break;
    5603           0 :         case AT_EnableTrigUser: /* ENABLE TRIGGER USER */
    5604           0 :             ATExecEnableDisableTrigger(rel, NULL,
    5605             :                                        TRIGGER_FIRES_ON_ORIGIN, true,
    5606           0 :                                        cmd->recurse,
    5607             :                                        lockmode);
    5608           0 :             break;
    5609          12 :         case AT_DisableTrigUser:    /* DISABLE TRIGGER USER */
    5610          12 :             ATExecEnableDisableTrigger(rel, NULL,
    5611             :                                        TRIGGER_DISABLED, true,
    5612          12 :                                        cmd->recurse,
    5613             :                                        lockmode);
    5614          12 :             break;
    5615             : 
    5616           8 :         case AT_EnableRule:     /* ENABLE RULE name */
    5617           8 :             ATExecEnableDisableRule(rel, cmd->name,
    5618             :                                     RULE_FIRES_ON_ORIGIN, lockmode);
    5619           8 :             break;
    5620           0 :         case AT_EnableAlwaysRule:   /* ENABLE ALWAYS RULE name */
    5621           0 :             ATExecEnableDisableRule(rel, cmd->name,
    5622             :                                     RULE_FIRES_ALWAYS, lockmode);
    5623           0 :             break;
    5624           6 :         case AT_EnableReplicaRule:  /* ENABLE REPLICA RULE name */
    5625           6 :             ATExecEnableDisableRule(rel, cmd->name,
    5626             :                                     RULE_FIRES_ON_REPLICA, lockmode);
    5627           6 :             break;
    5628          32 :         case AT_DisableRule:    /* DISABLE RULE name */
    5629          32 :             ATExecEnableDisableRule(rel, cmd->name,
    5630             :                                     RULE_DISABLED, lockmode);
    5631          32 :             break;
    5632             : 
    5633         446 :         case AT_AddInherit:
    5634         446 :             address = ATExecAddInherit(rel, (RangeVar *) cmd->def, lockmode);
    5635         326 :             break;
    5636          94 :         case AT_DropInherit:
    5637          94 :             address = ATExecDropInherit(rel, (RangeVar *) cmd->def, lockmode);
    5638          88 :             break;
    5639          66 :         case AT_AddOf:
    5640          66 :             address = ATExecAddOf(rel, (TypeName *) cmd->def, lockmode);
    5641          30 :             break;
    5642           6 :         case AT_DropOf:
    5643           6 :             ATExecDropOf(rel, lockmode);
    5644           6 :             break;
    5645         512 :         case AT_ReplicaIdentity:
    5646         512 :             ATExecReplicaIdentity(rel, (ReplicaIdentityStmt *) cmd->def, lockmode);
    5647         464 :             break;
    5648         338 :         case AT_EnableRowSecurity:
    5649         338 :             ATExecSetRowSecurity(rel, true);
    5650         338 :             break;
    5651          10 :         case AT_DisableRowSecurity:
    5652          10 :             ATExecSetRowSecurity(rel, false);
    5653          10 :             break;
    5654         100 :         case AT_ForceRowSecurity:
    5655         100 :             ATExecForceNoForceRowSecurity(rel, true);
    5656         100 :             break;
    5657          32 :         case AT_NoForceRowSecurity:
    5658          32 :             ATExecForceNoForceRowSecurity(rel, false);
    5659          32 :             break;
    5660          58 :         case AT_GenericOptions:
    5661          58 :             ATExecGenericOptions(rel, (List *) cmd->def);
    5662          56 :             break;
    5663        2944 :         case AT_AttachPartition:
    5664        2944 :             cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
    5665             :                                       cur_pass, context);
    5666             :             Assert(cmd != NULL);
    5667        2920 :             if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
    5668        2524 :                 address = ATExecAttachPartition(wqueue, rel, (PartitionCmd *) cmd->def,
    5669             :                                                 context);
    5670             :             else
    5671         396 :                 address = ATExecAttachPartitionIdx(wqueue, rel,
    5672         396 :                                                    ((PartitionCmd *) cmd->def)->name);
    5673        2530 :             break;
    5674         584 :         case AT_DetachPartition:
    5675         584 :             cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
    5676             :                                       cur_pass, context);
    5677             :             Assert(cmd != NULL);
    5678             :             /* ATPrepCmd ensures it must be a table */
    5679             :             Assert(rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
    5680         584 :             address = ATExecDetachPartition(wqueue, tab, rel,
    5681         584 :                                             ((PartitionCmd *) cmd->def)->name,
    5682         584 :                                             ((PartitionCmd *) cmd->def)->concurrent);
    5683         454 :             break;
    5684          14 :         case AT_DetachPartitionFinalize:
    5685          14 :             address = ATExecDetachPartitionFinalize(rel, ((PartitionCmd *) cmd->def)->name);
    5686          14 :             break;
    5687         264 :         case AT_MergePartitions:
    5688         264 :             cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
    5689             :                                       cur_pass, context);
    5690             :             Assert(cmd != NULL);
    5691             :             Assert(rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
    5692         180 :             ATExecMergePartitions(wqueue, tab, rel, (PartitionCmd *) cmd->def,
    5693             :                                   context);
    5694         138 :             break;
    5695         378 :         case AT_SplitPartition:
    5696         378 :             cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
    5697             :                                       cur_pass, context);
    5698             :             Assert(cmd != NULL);
    5699             :             Assert(rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
    5700         198 :             ATExecSplitPartition(wqueue, tab, rel, (PartitionCmd *) cmd->def,
    5701             :                                  context);
    5702         186 :             break;
    5703           0 :         default:                /* oops */
    5704           0 :             elog(ERROR, "unrecognized alter table type: %d",
    5705             :                  (int) cmd->subtype);
    5706             :             break;
    5707             :     }
    5708             : 
    5709             :     /*
    5710             :      * Report the subcommand to interested event triggers.
    5711             :      */
    5712       58002 :     if (cmd)
    5713       41642 :         EventTriggerCollectAlterTableSubcmd((Node *) cmd, address);
    5714             : 
    5715             :     /*
    5716             :      * Bump the command counter to ensure the next subcommand in the sequence
    5717             :      * can see the changes so far
    5718             :      */
    5719       58002 :     CommandCounterIncrement();
    5720       58002 : }
    5721             : 
    5722             : /*
    5723             :  * ATParseTransformCmd: perform parse transformation for one subcommand
    5724             :  *
    5725             :  * Returns the transformed subcommand tree, if there is one, else NULL.
    5726             :  *
    5727             :  * The parser may hand back additional AlterTableCmd(s) and/or other
    5728             :  * utility statements, either before or after the original subcommand.
    5729             :  * Other AlterTableCmds are scheduled into the appropriate slot of the
    5730             :  * AlteredTableInfo (they had better be for later passes than the current one).
    5731             :  * Utility statements that are supposed to happen before the AlterTableCmd
    5732             :  * are executed immediately.  Those that are supposed to happen afterwards
    5733             :  * are added to the tab->afterStmts list to be done at the very end.
    5734             :  */
    5735             : static AlterTableCmd *
    5736       24302 : ATParseTransformCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
    5737             :                     AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode,
    5738             :                     AlterTablePass cur_pass, AlterTableUtilityContext *context)
    5739             : {
    5740       24302 :     AlterTableCmd *newcmd = NULL;
    5741       24302 :     AlterTableStmt *atstmt = makeNode(AlterTableStmt);
    5742             :     List       *beforeStmts;
    5743             :     List       *afterStmts;
    5744             :     ListCell   *lc;
    5745             : 
    5746             :     /* Gin up an AlterTableStmt with just this subcommand and this table */
    5747       24302 :     atstmt->relation =
    5748       24302 :         makeRangeVar(get_namespace_name(RelationGetNamespace(rel)),
    5749       24302 :                      pstrdup(RelationGetRelationName(rel)),
    5750             :                      -1);
    5751       24302 :     atstmt->relation->inh = recurse;
    5752       24302 :     atstmt->cmds = list_make1(cmd);
    5753       24302 :     atstmt->objtype = OBJECT_TABLE; /* needn't be picky here */
    5754       24302 :     atstmt->missing_ok = false;
    5755             : 
    5756             :     /* Transform the AlterTableStmt */
    5757       24302 :     atstmt = transformAlterTableStmt(RelationGetRelid(rel),
    5758             :                                      atstmt,
    5759             :                                      context->queryString,
    5760             :                                      &beforeStmts,
    5761             :                                      &afterStmts);
    5762             : 
    5763             :     /* Execute any statements that should happen before these subcommand(s) */
    5764       24458 :     foreach(lc, beforeStmts)
    5765             :     {
    5766         498 :         Node       *stmt = (Node *) lfirst(lc);
    5767             : 
    5768         498 :         ProcessUtilityForAlterTable(stmt, context);
    5769         486 :         CommandCounterIncrement();
    5770             :     }
    5771             : 
    5772             :     /* Examine the transformed subcommands and schedule them appropriately */
    5773       56430 :     foreach(lc, atstmt->cmds)
    5774             :     {
    5775       32470 :         AlterTableCmd *cmd2 = lfirst_node(AlterTableCmd, lc);
    5776             :         AlterTablePass pass;
    5777             : 
    5778             :         /*
    5779             :          * This switch need only cover the subcommand types that can be added
    5780             :          * by parse_utilcmd.c; otherwise, we'll use the default strategy of
    5781             :          * executing the subcommand immediately, as a substitute for the
    5782             :          * original subcommand.  (Note, however, that this does cause
    5783             :          * AT_AddConstraint subcommands to be rescheduled into later passes,
    5784             :          * which is important for index and foreign key constraints.)
    5785             :          *
    5786             :          * We assume we needn't do any phase-1 checks for added subcommands.
    5787             :          */
    5788       32470 :         switch (cmd2->subtype)
    5789             :         {
    5790        1228 :             case AT_AddIndex:
    5791        1228 :                 pass = AT_PASS_ADD_INDEX;
    5792        1228 :                 break;
    5793       10860 :             case AT_AddIndexConstraint:
    5794       10860 :                 pass = AT_PASS_ADD_INDEXCONSTR;
    5795       10860 :                 break;
    5796       12782 :             case AT_AddConstraint:
    5797             :                 /* Recursion occurs during execution phase */
    5798       12782 :                 if (recurse)
    5799       12734 :                     cmd2->recurse = true;
    5800       12782 :                 switch (castNode(Constraint, cmd2->def)->contype)
    5801             :                 {
    5802        9160 :                     case CONSTR_NOTNULL:
    5803        9160 :                         pass = AT_PASS_COL_ATTRS;
    5804        9160 :                         break;
    5805           0 :                     case CONSTR_PRIMARY:
    5806             :                     case CONSTR_UNIQUE:
    5807             :                     case CONSTR_EXCLUSION:
    5808           0 :                         pass = AT_PASS_ADD_INDEXCONSTR;
    5809           0 :                         break;
    5810        3622 :                     default:
    5811        3622 :                         pass = AT_PASS_ADD_OTHERCONSTR;
    5812        3622 :                         break;
    5813             :                 }
    5814       12782 :                 break;
    5815           0 :             case AT_AlterColumnGenericOptions:
    5816             :                 /* This command never recurses */
    5817             :                 /* No command-specific prep needed */
    5818           0 :                 pass = AT_PASS_MISC;
    5819           0 :                 break;
    5820        7600 :             default:
    5821        7600 :                 pass = cur_pass;
    5822        7600 :                 break;
    5823             :         }
    5824             : 
    5825       32470 :         if (pass < cur_pass)
    5826             :         {
    5827             :             /* Cannot schedule into a pass we already finished */
    5828           0 :             elog(ERROR, "ALTER TABLE scheduling failure: too late for pass %d",
    5829             :                  pass);
    5830             :         }
    5831       32470 :         else if (pass > cur_pass)
    5832             :         {
    5833             :             /* OK, queue it up for later */
    5834       24870 :             tab->subcmds[pass] = lappend(tab->subcmds[pass], cmd2);
    5835             :         }
    5836             :         else
    5837             :         {
    5838             :             /*
    5839             :              * We should see at most one subcommand for the current pass,
    5840             :              * which is the transformed version of the original subcommand.
    5841             :              */
    5842        7600 :             if (newcmd == NULL && cmd->subtype == cmd2->subtype)
    5843             :             {
    5844             :                 /* Found the transformed version of our subcommand */
    5845        7600 :                 newcmd = cmd2;
    5846             :             }
    5847             :             else
    5848           0 :                 elog(ERROR, "ALTER TABLE scheduling failure: bogus item for pass %d",
    5849             :                      pass);
    5850             :         }
    5851             :     }
    5852             : 
    5853             :     /* Queue up any after-statements to happen at the end */
    5854       23960 :     tab->afterStmts = list_concat(tab->afterStmts, afterStmts);
    5855             : 
    5856       23960 :     return newcmd;
    5857             : }
    5858             : 
    5859             : /*
    5860             :  * ATRewriteTables: ALTER TABLE phase 3
    5861             :  */
    5862             : static void
    5863       30156 : ATRewriteTables(AlterTableStmt *parsetree, List **wqueue, LOCKMODE lockmode,
    5864             :                 AlterTableUtilityContext *context)
    5865             : {
    5866             :     ListCell   *ltab;
    5867             : 
    5868             :     /* Go through each table that needs to be checked or rewritten */
    5869       64258 :     foreach(ltab, *wqueue)
    5870             :     {
    5871       34484 :         AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
    5872             : 
    5873             :         /* Relations without storage may be ignored here */
    5874       34484 :         if (!RELKIND_HAS_STORAGE(tab->relkind))
    5875        6382 :             continue;
    5876             : 
    5877             :         /*
    5878             :          * If we change column data types, the operation has to be propagated
    5879             :          * to tables that use this table's rowtype as a column type.
    5880             :          * tab->newvals will also be non-NULL in the case where we're adding a
    5881             :          * column with a default.  We choose to forbid that case as well,
    5882             :          * since composite types might eventually support defaults.
    5883             :          *
    5884             :          * (Eventually we'll probably need to check for composite type
    5885             :          * dependencies even when we're just scanning the table without a
    5886             :          * rewrite, but at the moment a composite type does not enforce any
    5887             :          * constraints, so it's not necessary/appropriate to enforce them just
    5888             :          * during ALTER.)
    5889             :          */
    5890       28102 :         if (tab->newvals != NIL || tab->rewrite > 0)
    5891             :         {
    5892             :             Relation    rel;
    5893             : 
    5894        1870 :             rel = table_open(tab->relid, NoLock);
    5895        1870 :             find_composite_type_dependencies(rel->rd_rel->reltype, rel, NULL);
    5896        1816 :             table_close(rel, NoLock);
    5897             :         }
    5898             : 
    5899             :         /*
    5900             :          * We only need to rewrite the table if at least one column needs to
    5901             :          * be recomputed, or we are changing its persistence or access method.
    5902             :          *
    5903             :          * There are two reasons for requiring a rewrite when changing
    5904             :          * persistence: on one hand, we need to ensure that the buffers
    5905             :          * belonging to each of the two relations are marked with or without
    5906             :          * BM_PERMANENT properly.  On the other hand, since rewriting creates
    5907             :          * and assigns a new relfilenumber, we automatically create or drop an
    5908             :          * init fork for the relation as appropriate.
    5909             :          */
    5910       28048 :         if (tab->rewrite > 0 && tab->relkind != RELKIND_SEQUENCE)
    5911        1050 :         {
    5912             :             /* Build a temporary relation and copy data */
    5913             :             Relation    OldHeap;
    5914             :             Oid         OIDNewHeap;
    5915             :             Oid         NewAccessMethod;
    5916             :             Oid         NewTableSpace;
    5917             :             char        persistence;
    5918             : 
    5919        1106 :             OldHeap = table_open(tab->relid, NoLock);
    5920             : 
    5921             :             /*
    5922             :              * We don't support rewriting of system catalogs; there are too
    5923             :              * many corner cases and too little benefit.  In particular this
    5924             :              * is certainly not going to work for mapped catalogs.
    5925             :              */
    5926        1106 :             if (IsSystemRelation(OldHeap))
    5927           0 :                 ereport(ERROR,
    5928             :                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    5929             :                          errmsg("cannot rewrite system relation \"%s\"",
    5930             :                                 RelationGetRelationName(OldHeap))));
    5931             : 
    5932        1106 :             if (RelationIsUsedAsCatalogTable(OldHeap))
    5933           2 :                 ereport(ERROR,
    5934             :                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    5935             :                          errmsg("cannot rewrite table \"%s\" used as a catalog table",
    5936             :                                 RelationGetRelationName(OldHeap))));
    5937             : 
    5938             :             /*
    5939             :              * Don't allow rewrite on temp tables of other backends ... their
    5940             :              * local buffer manager is not going to cope.  (This is redundant
    5941             :              * with the check in CheckAlterTableIsSafe, but for safety we'll
    5942             :              * check here too.)
    5943             :              */
    5944        1104 :             if (RELATION_IS_OTHER_TEMP(OldHeap))
    5945           0 :                 ereport(ERROR,
    5946             :                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    5947             :                          errmsg("cannot rewrite temporary tables of other sessions")));
    5948             : 
    5949             :             /*
    5950             :              * Select destination tablespace (same as original unless user
    5951             :              * requested a change)
    5952             :              */
    5953        1104 :             if (tab->newTableSpace)
    5954           0 :                 NewTableSpace = tab->newTableSpace;
    5955             :             else
    5956        1104 :                 NewTableSpace = OldHeap->rd_rel->reltablespace;
    5957             : 
    5958             :             /*
    5959             :              * Select destination access method (same as original unless user
    5960             :              * requested a change)
    5961             :              */
    5962        1104 :             if (tab->chgAccessMethod)
    5963          36 :                 NewAccessMethod = tab->newAccessMethod;
    5964             :             else
    5965        1068 :                 NewAccessMethod = OldHeap->rd_rel->relam;
    5966             : 
    5967             :             /*
    5968             :              * Select persistence of transient table (same as original unless
    5969             :              * user requested a change)
    5970             :              */
    5971        1104 :             persistence = tab->chgPersistence ?
    5972        1052 :                 tab->newrelpersistence : OldHeap->rd_rel->relpersistence;
    5973             : 
    5974        1104 :             table_close(OldHeap, NoLock);
    5975             : 
    5976             :             /*
    5977             :              * Fire off an Event Trigger now, before actually rewriting the
    5978             :              * table.
    5979             :              *
    5980             :              * We don't support Event Trigger for nested commands anywhere,
    5981             :              * here included, and parsetree is given NULL when coming from
    5982             :              * AlterTableInternal.
    5983             :              *
    5984             :              * And fire it only once.
    5985             :              */
    5986        1104 :             if (parsetree)
    5987        1104 :                 EventTriggerTableRewrite((Node *) parsetree,
    5988             :                                          tab->relid,
    5989             :                                          tab->rewrite);
    5990             : 
    5991             :             /*
    5992             :              * Create transient table that will receive the modified data.
    5993             :              *
    5994             :              * Ensure it is marked correctly as logged or unlogged.  We have
    5995             :              * to do this here so that buffers for the new relfilenumber will
    5996             :              * have the right persistence set, and at the same time ensure
    5997             :              * that the original filenumbers's buffers will get read in with
    5998             :              * the correct setting (i.e. the original one).  Otherwise a
    5999             :              * rollback after the rewrite would possibly result with buffers
    6000             :              * for the original filenumbers having the wrong persistence
    6001             :              * setting.
    6002             :              *
    6003             :              * NB: This relies on swap_relation_files() also swapping the
    6004             :              * persistence. That wouldn't work for pg_class, but that can't be
    6005             :              * unlogged anyway.
    6006             :              */
    6007        1098 :             OIDNewHeap = make_new_heap(tab->relid, NewTableSpace, NewAccessMethod,
    6008             :                                        persistence, lockmode);
    6009             : 
    6010             :             /*
    6011             :              * Copy the heap data into the new table with the desired
    6012             :              * modifications, and test the current data within the table
    6013             :              * against new constraints generated by ALTER TABLE commands.
    6014             :              */
    6015        1098 :             ATRewriteTable(tab, OIDNewHeap);
    6016             : 
    6017             :             /*
    6018             :              * Swap the physical files of the old and new heaps, then rebuild
    6019             :              * indexes and discard the old heap.  We can use RecentXmin for
    6020             :              * the table's new relfrozenxid because we rewrote all the tuples
    6021             :              * in ATRewriteTable, so no older Xid remains in the table.  Also,
    6022             :              * we never try to swap toast tables by content, since we have no
    6023             :              * interest in letting this code work on system catalogs.
    6024             :              */
    6025        1056 :             finish_heap_swap(tab->relid, OIDNewHeap,
    6026             :                              false, false, true,
    6027        1056 :                              !OidIsValid(tab->newTableSpace),
    6028             :                              RecentXmin,
    6029             :                              ReadNextMultiXactId(),
    6030             :                              persistence);
    6031             : 
    6032        1050 :             InvokeObjectPostAlterHook(RelationRelationId, tab->relid, 0);
    6033             :         }
    6034       26942 :         else if (tab->rewrite > 0 && tab->relkind == RELKIND_SEQUENCE)
    6035             :         {
    6036          24 :             if (tab->chgPersistence)
    6037          24 :                 SequenceChangePersistence(tab->relid, tab->newrelpersistence);
    6038             :         }
    6039             :         else
    6040             :         {
    6041             :             /*
    6042             :              * If required, test the current data within the table against new
    6043             :              * constraints generated by ALTER TABLE commands, but don't
    6044             :              * rebuild data.
    6045             :              */
    6046       26918 :             if (tab->constraints != NIL || tab->verify_new_notnull ||
    6047       23960 :                 tab->partition_constraint != NULL)
    6048        5008 :                 ATRewriteTable(tab, InvalidOid);
    6049             : 
    6050             :             /*
    6051             :              * If we had SET TABLESPACE but no reason to reconstruct tuples,
    6052             :              * just do a block-by-block copy.
    6053             :              */
    6054       26646 :             if (tab->newTableSpace)
    6055         128 :                 ATExecSetTableSpace(tab->relid, tab->newTableSpace, lockmode);
    6056             :         }
    6057             : 
    6058             :         /*
    6059             :          * Also change persistence of owned sequences, so that it matches the
    6060             :          * table persistence.
    6061             :          */
    6062       27720 :         if (tab->chgPersistence)
    6063             :         {
    6064          76 :             List       *seqlist = getOwnedSequences(tab->relid);
    6065             :             ListCell   *lc;
    6066             : 
    6067         124 :             foreach(lc, seqlist)
    6068             :             {
    6069          48 :                 Oid         seq_relid = lfirst_oid(lc);
    6070             : 
    6071          48 :                 SequenceChangePersistence(seq_relid, tab->newrelpersistence);
    6072             :             }
    6073             :         }
    6074             :     }
    6075             : 
    6076             :     /*
    6077             :      * Foreign key constraints are checked in a final pass, since (a) it's
    6078             :      * generally best to examine each one separately, and (b) it's at least
    6079             :      * theoretically possible that we have changed both relations of the
    6080             :      * foreign key, and we'd better have finished both rewrites before we try
    6081             :      * to read the tables.
    6082             :      */
    6083       63606 :     foreach(ltab, *wqueue)
    6084             :     {
    6085       33936 :         AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
    6086       33936 :         Relation    rel = NULL;
    6087             :         ListCell   *lcon;
    6088             : 
    6089             :         /* Relations without storage may be ignored here too */
    6090       33936 :         if (!RELKIND_HAS_STORAGE(tab->relkind))
    6091        6278 :             continue;
    6092             : 
    6093       29522 :         foreach(lcon, tab->constraints)
    6094             :         {
    6095        1968 :             NewConstraint *con = lfirst(lcon);
    6096             : 
    6097        1968 :             if (con->contype == CONSTR_FOREIGN)
    6098             :             {
    6099        1178 :                 Constraint *fkconstraint = (Constraint *) con->qual;
    6100             :                 Relation    refrel;
    6101             : 
    6102        1178 :                 if (rel == NULL)
    6103             :                 {
    6104             :                     /* Long since locked, no need for another */
    6105        1166 :                     rel = table_open(tab->relid, NoLock);
    6106             :                 }
    6107             : 
    6108        1178 :                 refrel = table_open(con->refrelid, RowShareLock);
    6109             : 
    6110        1178 :                 validateForeignKeyConstraint(fkconstraint->conname, rel, refrel,
    6111             :                                              con->refindid,
    6112             :                                              con->conid,
    6113        1178 :                                              con->conwithperiod);
    6114             : 
    6115             :                 /*
    6116             :                  * No need to mark the constraint row as validated, we did
    6117             :                  * that when we inserted the row earlier.
    6118             :                  */
    6119             : 
    6120        1074 :                 table_close(refrel, NoLock);
    6121             :             }
    6122             :         }
    6123             : 
    6124       27554 :         if (rel)
    6125        1062 :             table_close(rel, NoLock);
    6126             :     }
    6127             : 
    6128             :     /* Finally, run any afterStmts that were queued up */
    6129       63458 :     foreach(ltab, *wqueue)
    6130             :     {
    6131       33788 :         AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
    6132             :         ListCell   *lc;
    6133             : 
    6134       33874 :         foreach(lc, tab->afterStmts)
    6135             :         {
    6136          86 :             Node       *stmt = (Node *) lfirst(lc);
    6137             : 
    6138          86 :             ProcessUtilityForAlterTable(stmt, context);
    6139          86 :             CommandCounterIncrement();
    6140             :         }
    6141             :     }
    6142       29670 : }
    6143             : 
    6144             : /*
    6145             :  * ATRewriteTable: scan or rewrite one table
    6146             :  *
    6147             :  * A rewrite is requested by passing a valid OIDNewHeap; in that case, caller
    6148             :  * must already hold AccessExclusiveLock on it.
    6149             :  */
    6150             : static void
    6151        6106 : ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap)
    6152             : {
    6153             :     Relation    oldrel;
    6154             :     Relation    newrel;
    6155             :     TupleDesc   oldTupDesc;
    6156             :     TupleDesc   newTupDesc;
    6157        6106 :     bool        needscan = false;
    6158             :     List       *notnull_attrs;
    6159             :     List       *notnull_virtual_attrs;
    6160             :     int         i;
    6161             :     ListCell   *l;
    6162             :     EState     *estate;
    6163             :     CommandId   mycid;
    6164             :     BulkInsertState bistate;
    6165             :     int         ti_options;
    6166        6106 :     ExprState  *partqualstate = NULL;
    6167             : 
    6168             :     /*
    6169             :      * Open the relation(s).  We have surely already locked the existing
    6170             :      * table.
    6171             :      */
    6172        6106 :     oldrel = table_open(tab->relid, NoLock);
    6173        6106 :     oldTupDesc = tab->oldDesc;
    6174        6106 :     newTupDesc = RelationGetDescr(oldrel);  /* includes all mods */
    6175             : 
    6176        6106 :     if (OidIsValid(OIDNewHeap))
    6177             :     {
    6178             :         Assert(CheckRelationOidLockedByMe(OIDNewHeap, AccessExclusiveLock,
    6179             :                                           false));
    6180        1098 :         newrel = table_open(OIDNewHeap, NoLock);
    6181             :     }
    6182             :     else
    6183        5008 :         newrel = NULL;
    6184             : 
    6185             :     /*
    6186             :      * Prepare a BulkInsertState and options for table_tuple_insert.  The FSM
    6187             :      * is empty, so don't bother using it.
    6188             :      */
    6189        6106 :     if (newrel)
    6190             :     {
    6191        1098 :         mycid = GetCurrentCommandId(true);
    6192        1098 :         bistate = GetBulkInsertState();
    6193        1098 :         ti_options = TABLE_INSERT_SKIP_FSM;
    6194             :     }
    6195             :     else
    6196             :     {
    6197             :         /* keep compiler quiet about using these uninitialized */
    6198        5008 :         mycid = 0;
    6199        5008 :         bistate = NULL;
    6200        5008 :         ti_options = 0;
    6201             :     }
    6202             : 
    6203             :     /*
    6204             :      * Generate the constraint and default execution states
    6205             :      */
    6206             : 
    6207        6106 :     estate = CreateExecutorState();
    6208             : 
    6209             :     /* Build the needed expression execution states */
    6210        8194 :     foreach(l, tab->constraints)
    6211             :     {
    6212        2088 :         NewConstraint *con = lfirst(l);
    6213             : 
    6214        2088 :         switch (con->contype)
    6215             :         {
    6216         904 :             case CONSTR_CHECK:
    6217         904 :                 needscan = true;
    6218         904 :                 con->qualstate = ExecPrepareExpr((Expr *) expand_generated_columns_in_expr(con->qual, oldrel, 1), estate);
    6219         904 :                 break;
    6220        1184 :             case CONSTR_FOREIGN:
    6221             :                 /* Nothing to do here */
    6222        1184 :                 break;
    6223           0 :             default:
    6224           0 :                 elog(ERROR, "unrecognized constraint type: %d",
    6225             :                      (int) con->contype);
    6226             :         }
    6227             :     }
    6228             : 
    6229             :     /* Build expression execution states for partition check quals */
    6230        6106 :     if (tab->partition_constraint)
    6231             :     {
    6232        2198 :         needscan = true;
    6233        2198 :         partqualstate = ExecPrepareExpr(tab->partition_constraint, estate);
    6234             :     }
    6235             : 
    6236        7266 :     foreach(l, tab->newvals)
    6237             :     {
    6238        1160 :         NewColumnValue *ex = lfirst(l);
    6239             : 
    6240             :         /* expr already planned */
    6241        1160 :         ex->exprstate = ExecInitExpr(ex->expr, NULL);
    6242             :     }
    6243             : 
    6244        6106 :     notnull_attrs = notnull_virtual_attrs = NIL;
    6245        6106 :     if (newrel || tab->verify_new_notnull)
    6246             :     {
    6247             :         /*
    6248             :          * If we are rebuilding the tuples OR if we added any new but not
    6249             :          * verified not-null constraints, check all *valid* not-null
    6250             :          * constraints. This is a bit of overkill but it minimizes risk of
    6251             :          * bugs.
    6252             :          *
    6253             :          * notnull_attrs does *not* collect attribute numbers for valid
    6254             :          * not-null constraints over virtual generated columns; instead, they
    6255             :          * are collected in notnull_virtual_attrs for verification elsewhere.
    6256             :          */
    6257        7782 :         for (i = 0; i < newTupDesc->natts; i++)
    6258             :         {
    6259        5644 :             CompactAttribute *attr = TupleDescCompactAttr(newTupDesc, i);
    6260             : 
    6261        5644 :             if (attr->attnullability == ATTNULLABLE_VALID &&
    6262        2120 :                 !attr->attisdropped)
    6263             :             {
    6264        2120 :                 Form_pg_attribute wholeatt = TupleDescAttr(newTupDesc, i);
    6265             : 
    6266        2120 :                 if (wholeatt->attgenerated != ATTRIBUTE_GENERATED_VIRTUAL)
    6267        2030 :                     notnull_attrs = lappend_int(notnull_attrs, wholeatt->attnum);
    6268             :                 else
    6269          90 :                     notnull_virtual_attrs = lappend_int(notnull_virtual_attrs,
    6270          90 :                                                         wholeatt->attnum);
    6271             :             }
    6272             :         }
    6273        2138 :         if (notnull_attrs || notnull_virtual_attrs)
    6274        1558 :             needscan = true;
    6275             :     }
    6276             : 
    6277        6106 :     if (newrel || needscan)
    6278             :     {
    6279             :         ExprContext *econtext;
    6280             :         TupleTableSlot *oldslot;
    6281             :         TupleTableSlot *newslot;
    6282             :         TableScanDesc scan;
    6283             :         MemoryContext oldCxt;
    6284        5124 :         List       *dropped_attrs = NIL;
    6285             :         ListCell   *lc;
    6286             :         Snapshot    snapshot;
    6287        5124 :         ResultRelInfo *rInfo = NULL;
    6288             : 
    6289             :         /*
    6290             :          * When adding or changing a virtual generated column with a not-null
    6291             :          * constraint, we need to evaluate whether the generation expression
    6292             :          * is null.  For that, we borrow ExecRelGenVirtualNotNull().  Here, we
    6293             :          * prepare a dummy ResultRelInfo.
    6294             :          */
    6295        5124 :         if (notnull_virtual_attrs != NIL)
    6296             :         {
    6297             :             MemoryContext oldcontext;
    6298             : 
    6299             :             Assert(newTupDesc->constr->has_generated_virtual);
    6300             :             Assert(newTupDesc->constr->has_not_null);
    6301          60 :             oldcontext = MemoryContextSwitchTo(estate->es_query_cxt);
    6302          60 :             rInfo = makeNode(ResultRelInfo);
    6303          60 :             InitResultRelInfo(rInfo,
    6304             :                               oldrel,
    6305             :                               0,    /* dummy rangetable index */
    6306             :                               NULL,
    6307             :                               estate->es_instrument);
    6308          60 :             MemoryContextSwitchTo(oldcontext);
    6309             :         }
    6310             : 
    6311        5124 :         if (newrel)
    6312        1098 :             ereport(DEBUG1,
    6313             :                     (errmsg_internal("rewriting table \"%s\"",
    6314             :                                      RelationGetRelationName(oldrel))));
    6315             :         else
    6316        4026 :             ereport(DEBUG1,
    6317             :                     (errmsg_internal("verifying table \"%s\"",
    6318             :                                      RelationGetRelationName(oldrel))));
    6319             : 
    6320        5124 :         if (newrel)
    6321             :         {
    6322             :             /*
    6323             :              * All predicate locks on the tuples or pages are about to be made
    6324             :              * invalid, because we move tuples around.  Promote them to
    6325             :              * relation locks.
    6326             :              */
    6327        1098 :             TransferPredicateLocksToHeapRelation(oldrel);
    6328             :         }
    6329             : 
    6330        5124 :         econtext = GetPerTupleExprContext(estate);
    6331             : 
    6332             :         /*
    6333             :          * Create necessary tuple slots. When rewriting, two slots are needed,
    6334             :          * otherwise one suffices. In the case where one slot suffices, we
    6335             :          * need to use the new tuple descriptor, otherwise some constraints
    6336             :          * can't be evaluated.  Note that even when the tuple layout is the
    6337             :          * same and no rewrite is required, the tupDescs might not be
    6338             :          * (consider ADD COLUMN without a default).
    6339             :          */
    6340        5124 :         if (tab->rewrite)
    6341             :         {
    6342             :             Assert(newrel != NULL);
    6343        1098 :             oldslot = MakeSingleTupleTableSlot(oldTupDesc,
    6344             :                                                table_slot_callbacks(oldrel));
    6345        1098 :             newslot = MakeSingleTupleTableSlot(newTupDesc,
    6346             :                                                table_slot_callbacks(newrel));
    6347             : 
    6348             :             /*
    6349             :              * Set all columns in the new slot to NULL initially, to ensure
    6350             :              * columns added as part of the rewrite are initialized to NULL.
    6351             :              * That is necessary as tab->newvals will not contain an
    6352             :              * expression for columns with a NULL default, e.g. when adding a
    6353             :              * column without a default together with a column with a default
    6354             :              * requiring an actual rewrite.
    6355             :              */
    6356        1098 :             ExecStoreAllNullTuple(newslot);
    6357             :         }
    6358             :         else
    6359             :         {
    6360        4026 :             oldslot = MakeSingleTupleTableSlot(newTupDesc,
    6361             :                                                table_slot_callbacks(oldrel));
    6362        4026 :             newslot = NULL;
    6363             :         }
    6364             : 
    6365             :         /*
    6366             :          * Any attributes that are dropped according to the new tuple
    6367             :          * descriptor can be set to NULL. We precompute the list of dropped
    6368             :          * attributes to avoid needing to do so in the per-tuple loop.
    6369             :          */
    6370       18010 :         for (i = 0; i < newTupDesc->natts; i++)
    6371             :         {
    6372       12886 :             if (TupleDescAttr(newTupDesc, i)->attisdropped)
    6373         826 :                 dropped_attrs = lappend_int(dropped_attrs, i);
    6374             :         }
    6375             : 
    6376             :         /*
    6377             :          * Scan through the rows, generating a new row if needed and then
    6378             :          * checking all the constraints.
    6379             :          */
    6380        5124 :         snapshot = RegisterSnapshot(GetLatestSnapshot());
    6381        5124 :         scan = table_beginscan(oldrel, snapshot, 0, NULL);
    6382             : 
    6383             :         /*
    6384             :          * Switch to per-tuple memory context and reset it for each tuple
    6385             :          * produced, so we don't leak memory.
    6386             :          */
    6387        5124 :         oldCxt = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
    6388             : 
    6389      775480 :         while (table_scan_getnextslot(scan, ForwardScanDirection, oldslot))
    6390             :         {
    6391             :             TupleTableSlot *insertslot;
    6392             : 
    6393      765546 :             if (tab->rewrite > 0)
    6394             :             {
    6395             :                 /* Extract data from old tuple */
    6396      100122 :                 slot_getallattrs(oldslot);
    6397      100122 :                 ExecClearTuple(newslot);
    6398             : 
    6399             :                 /* copy attributes */
    6400      100122 :                 memcpy(newslot->tts_values, oldslot->tts_values,
    6401      100122 :                        sizeof(Datum) * oldslot->tts_nvalid);
    6402      100122 :                 memcpy(newslot->tts_isnull, oldslot->tts_isnull,
    6403      100122 :                        sizeof(bool) * oldslot->tts_nvalid);
    6404             : 
    6405             :                 /* Set dropped attributes to null in new tuple */
    6406      100238 :                 foreach(lc, dropped_attrs)
    6407         116 :                     newslot->tts_isnull[lfirst_int(lc)] = true;
    6408             : 
    6409             :                 /*
    6410             :                  * Constraints and GENERATED expressions might reference the
    6411             :                  * tableoid column, so fill tts_tableOid with the desired
    6412             :                  * value.  (We must do this each time, because it gets
    6413             :                  * overwritten with newrel's OID during storing.)
    6414             :                  */
    6415      100122 :                 newslot->tts_tableOid = RelationGetRelid(oldrel);
    6416             : 
    6417             :                 /*
    6418             :                  * Process supplied expressions to replace selected columns.
    6419             :                  *
    6420             :                  * First, evaluate expressions whose inputs come from the old
    6421             :                  * tuple.
    6422             :                  */
    6423      100122 :                 econtext->ecxt_scantuple = oldslot;
    6424             : 
    6425      206196 :                 foreach(l, tab->newvals)
    6426             :                 {
    6427      106086 :                     NewColumnValue *ex = lfirst(l);
    6428             : 
    6429      106086 :                     if (ex->is_generated)
    6430         312 :                         continue;
    6431             : 
    6432      105774 :                     newslot->tts_values[ex->attnum - 1]
    6433      105762 :                         = ExecEvalExpr(ex->exprstate,
    6434             :                                        econtext,
    6435      105774 :                                        &newslot->tts_isnull[ex->attnum - 1]);
    6436             :                 }
    6437             : 
    6438      100110 :                 ExecStoreVirtualTuple(newslot);
    6439             : 
    6440             :                 /*
    6441             :                  * Now, evaluate any expressions whose inputs come from the
    6442             :                  * new tuple.  We assume these columns won't reference each
    6443             :                  * other, so that there's no ordering dependency.
    6444             :                  */
    6445      100110 :                 econtext->ecxt_scantuple = newslot;
    6446             : 
    6447      206184 :                 foreach(l, tab->newvals)
    6448             :                 {
    6449      106074 :                     NewColumnValue *ex = lfirst(l);
    6450             : 
    6451      106074 :                     if (!ex->is_generated)
    6452      105762 :                         continue;
    6453             : 
    6454         312 :                     newslot->tts_values[ex->attnum - 1]
    6455         312 :                         = ExecEvalExpr(ex->exprstate,
    6456             :                                        econtext,
    6457         312 :                                        &newslot->tts_isnull[ex->attnum - 1]);
    6458             :                 }
    6459             : 
    6460      100110 :                 insertslot = newslot;
    6461             :             }
    6462             :             else
    6463             :             {
    6464             :                 /*
    6465             :                  * If there's no rewrite, old and new table are guaranteed to
    6466             :                  * have the same AM, so we can just use the old slot to verify
    6467             :                  * new constraints etc.
    6468             :                  */
    6469      665424 :                 insertslot = oldslot;
    6470             :             }
    6471             : 
    6472             :             /* Now check any constraints on the possibly-changed tuple */
    6473      765534 :             econtext->ecxt_scantuple = insertslot;
    6474             : 
    6475     4107338 :             foreach_int(attn, notnull_attrs)
    6476             :             {
    6477     2576474 :                 if (slot_attisnull(insertslot, attn))
    6478             :                 {
    6479         102 :                     Form_pg_attribute attr = TupleDescAttr(newTupDesc, attn - 1);
    6480             : 
    6481         102 :                     ereport(ERROR,
    6482             :                             (errcode(ERRCODE_NOT_NULL_VIOLATION),
    6483             :                              errmsg("column \"%s\" of relation \"%s\" contains null values",
    6484             :                                     NameStr(attr->attname),
    6485             :                                     RelationGetRelationName(oldrel)),
    6486             :                              errtablecol(oldrel, attn)));
    6487             :                 }
    6488             :             }
    6489             : 
    6490      765432 :             if (notnull_virtual_attrs != NIL)
    6491             :             {
    6492             :                 AttrNumber  attnum;
    6493             : 
    6494          84 :                 attnum = ExecRelGenVirtualNotNull(rInfo, insertslot,
    6495             :                                                   estate,
    6496             :                                                   notnull_virtual_attrs);
    6497          84 :                 if (attnum != InvalidAttrNumber)
    6498             :                 {
    6499          30 :                     Form_pg_attribute attr = TupleDescAttr(newTupDesc, attnum - 1);
    6500             : 
    6501          30 :                     ereport(ERROR,
    6502             :                             errcode(ERRCODE_NOT_NULL_VIOLATION),
    6503             :                             errmsg("column \"%s\" of relation \"%s\" contains null values",
    6504             :                                    NameStr(attr->attname),
    6505             :                                    RelationGetRelationName(oldrel)),
    6506             :                             errtablecol(oldrel, attnum));
    6507             :                 }
    6508             :             }
    6509             : 
    6510      773562 :             foreach(l, tab->constraints)
    6511             :             {
    6512        8256 :                 NewConstraint *con = lfirst(l);
    6513             : 
    6514        8256 :                 switch (con->contype)
    6515             :                 {
    6516        8150 :                     case CONSTR_CHECK:
    6517        8150 :                         if (!ExecCheck(con->qualstate, econtext))
    6518          96 :                             ereport(ERROR,
    6519             :                                     (errcode(ERRCODE_CHECK_VIOLATION),
    6520             :                                      errmsg("check constraint \"%s\" of relation \"%s\" is violated by some row",
    6521             :                                             con->name,
    6522             :                                             RelationGetRelationName(oldrel)),
    6523             :                                      errtableconstraint(oldrel, con->name)));
    6524        8054 :                         break;
    6525         106 :                     case CONSTR_NOTNULL:
    6526             :                     case CONSTR_FOREIGN:
    6527             :                         /* Nothing to do here */
    6528         106 :                         break;
    6529           0 :                     default:
    6530           0 :                         elog(ERROR, "unrecognized constraint type: %d",
    6531             :                              (int) con->contype);
    6532             :                 }
    6533             :             }
    6534             : 
    6535      765306 :             if (partqualstate && !ExecCheck(partqualstate, econtext))
    6536             :             {
    6537          74 :                 if (tab->validate_default)
    6538          26 :                     ereport(ERROR,
    6539             :                             (errcode(ERRCODE_CHECK_VIOLATION),
    6540             :                              errmsg("updated partition constraint for default partition \"%s\" would be violated by some row",
    6541             :                                     RelationGetRelationName(oldrel)),
    6542             :                              errtable(oldrel)));
    6543             :                 else
    6544          48 :                     ereport(ERROR,
    6545             :                             (errcode(ERRCODE_CHECK_VIOLATION),
    6546             :                              errmsg("partition constraint of relation \"%s\" is violated by some row",
    6547             :                                     RelationGetRelationName(oldrel)),
    6548             :                              errtable(oldrel)));
    6549             :             }
    6550             : 
    6551             :             /* Write the tuple out to the new relation */
    6552      765232 :             if (newrel)
    6553      100080 :                 table_tuple_insert(newrel, insertslot, mycid,
    6554             :                                    ti_options, bistate);
    6555             : 
    6556      765232 :             ResetExprContext(econtext);
    6557             : 
    6558      765232 :             CHECK_FOR_INTERRUPTS();
    6559             :         }
    6560             : 
    6561        4810 :         MemoryContextSwitchTo(oldCxt);
    6562        4810 :         table_endscan(scan);
    6563        4810 :         UnregisterSnapshot(snapshot);
    6564             : 
    6565        4810 :         ExecDropSingleTupleTableSlot(oldslot);
    6566        4810 :         if (newslot)
    6567        1056 :             ExecDropSingleTupleTableSlot(newslot);
    6568             :     }
    6569             : 
    6570        5792 :     FreeExecutorState(estate);
    6571             : 
    6572        5792 :     table_close(oldrel, NoLock);
    6573        5792 :     if (newrel)
    6574             :     {
    6575        1056 :         FreeBulkInsertState(bistate);
    6576             : 
    6577        1056 :         table_finish_bulk_insert(newrel, ti_options);
    6578             : 
    6579        1056 :         table_close(newrel, NoLock);
    6580             :     }
    6581        5792 : }
    6582             : 
    6583             : /*
    6584             :  * ATGetQueueEntry: find or create an entry in the ALTER TABLE work queue
    6585             :  */
    6586             : static AlteredTableInfo *
    6587       44488 : ATGetQueueEntry(List **wqueue, Relation rel)
    6588             : {
    6589       44488 :     Oid         relid = RelationGetRelid(rel);
    6590             :     AlteredTableInfo *tab;
    6591             :     ListCell   *ltab;
    6592             : 
    6593       56732 :     foreach(ltab, *wqueue)
    6594             :     {
    6595       17832 :         tab = (AlteredTableInfo *) lfirst(ltab);
    6596       17832 :         if (tab->relid == relid)
    6597        5588 :             return tab;
    6598             :     }
    6599             : 
    6600             :     /*
    6601             :      * Not there, so add it.  Note that we make a copy of the relation's
    6602             :      * existing descriptor before anything interesting can happen to it.
    6603             :      */
    6604       38900 :     tab = palloc0_object(AlteredTableInfo);
    6605       38900 :     tab->relid = relid;
    6606       38900 :     tab->rel = NULL;         /* set later */
    6607       38900 :     tab->relkind = rel->rd_rel->relkind;
    6608       38900 :     tab->oldDesc = CreateTupleDescCopyConstr(RelationGetDescr(rel));
    6609       38900 :     tab->newAccessMethod = InvalidOid;
    6610       38900 :     tab->chgAccessMethod = false;
    6611       38900 :     tab->newTableSpace = InvalidOid;
    6612       38900 :     tab->newrelpersistence = RELPERSISTENCE_PERMANENT;
    6613       38900 :     tab->chgPersistence = false;
    6614             : 
    6615       38900 :     *wqueue = lappend(*wqueue, tab);
    6616             : 
    6617       38900 :     return tab;
    6618             : }
    6619             : 
    6620             : static const char *
    6621          86 : alter_table_type_to_string(AlterTableType cmdtype)
    6622             : {
    6623          86 :     switch (cmdtype)
    6624             :     {
    6625           0 :         case AT_AddColumn:
    6626             :         case AT_AddColumnToView:
    6627           0 :             return "ADD COLUMN";
    6628           0 :         case AT_ColumnDefault:
    6629             :         case AT_CookedColumnDefault:
    6630           0 :             return "ALTER COLUMN ... SET DEFAULT";
    6631           6 :         case AT_DropNotNull:
    6632           6 :             return "ALTER COLUMN ... DROP NOT NULL";
    6633           6 :         case AT_SetNotNull:
    6634           6 :             return "ALTER COLUMN ... SET NOT NULL";
    6635           0 :         case AT_SetExpression:
    6636           0 :             return "ALTER COLUMN ... SET EXPRESSION";
    6637           0 :         case AT_DropExpression:
    6638           0 :             return "ALTER COLUMN ... DROP EXPRESSION";
    6639           0 :         case AT_SetStatistics:
    6640           0 :             return "ALTER COLUMN ... SET STATISTICS";
    6641          12 :         case AT_SetOptions:
    6642          12 :             return "ALTER COLUMN ... SET";
    6643           0 :         case AT_ResetOptions:
    6644           0 :             return "ALTER COLUMN ... RESET";
    6645           0 :         case AT_SetStorage:
    6646           0 :             return "ALTER COLUMN ... SET STORAGE";
    6647           0 :         case AT_SetCompression:
    6648           0 :             return "ALTER COLUMN ... SET COMPRESSION";
    6649           6 :         case AT_DropColumn:
    6650           6 :             return "DROP COLUMN";
    6651           0 :         case AT_AddIndex:
    6652             :         case AT_ReAddIndex:
    6653           0 :             return NULL;        /* not real grammar */
    6654           0 :         case AT_AddConstraint:
    6655             :         case AT_ReAddConstraint:
    6656             :         case AT_ReAddDomainConstraint:
    6657             :         case AT_AddIndexConstraint:
    6658           0 :             return "ADD CONSTRAINT";
    6659           6 :         case AT_AlterConstraint:
    6660           6 :             return "ALTER CONSTRAINT";
    6661           0 :         case AT_ValidateConstraint:
    6662           0 :             return "VALIDATE CONSTRAINT";
    6663           0 :         case AT_DropConstraint:
    6664           0 :             return "DROP CONSTRAINT";
    6665           0 :         case AT_ReAddComment:
    6666           0 :             return NULL;        /* not real grammar */
    6667           0 :         case AT_AlterColumnType:
    6668           0 :             return "ALTER COLUMN ... SET DATA TYPE";
    6669           0 :         case AT_AlterColumnGenericOptions:
    6670           0 :             return "ALTER COLUMN ... OPTIONS";
    6671           0 :         case AT_ChangeOwner:
    6672           0 :             return "OWNER TO";
    6673           0 :         case AT_ClusterOn:
    6674           0 :             return "CLUSTER ON";
    6675           0 :         case AT_DropCluster:
    6676           0 :             return "SET WITHOUT CLUSTER";
    6677           0 :         case AT_SetAccessMethod:
    6678           0 :             return "SET ACCESS METHOD";
    6679           6 :         case AT_SetLogged:
    6680           6 :             return "SET LOGGED";
    6681           6 :         case AT_SetUnLogged:
    6682           6 :             return "SET UNLOGGED";
    6683           0 :         case AT_DropOids:
    6684           0 :             return "SET WITHOUT OIDS";
    6685           0 :         case AT_SetTableSpace:
    6686           0 :             return "SET TABLESPACE";
    6687           2 :         case AT_SetRelOptions:
    6688           2 :             return "SET";
    6689           0 :         case AT_ResetRelOptions:
    6690           0 :             return "RESET";
    6691           0 :         case AT_ReplaceRelOptions:
    6692           0 :             return NULL;        /* not real grammar */
    6693           0 :         case AT_EnableTrig:
    6694           0 :             return "ENABLE TRIGGER";
    6695           0 :         case AT_EnableAlwaysTrig:
    6696           0 :             return "ENABLE ALWAYS TRIGGER";
    6697           0 :         case AT_EnableReplicaTrig:
    6698           0 :             return "ENABLE REPLICA TRIGGER";
    6699           0 :         case AT_DisableTrig:
    6700           0 :             return "DISABLE TRIGGER";
    6701           0 :         case AT_EnableTrigAll:
    6702           0 :             return "ENABLE TRIGGER ALL";
    6703           0 :         case AT_DisableTrigAll:
    6704           0 :             return "DISABLE TRIGGER ALL";
    6705           0 :         case AT_EnableTrigUser:
    6706           0 :             return "ENABLE TRIGGER USER";
    6707           0 :         case AT_DisableTrigUser:
    6708           0 :             return "DISABLE TRIGGER USER";
    6709           0 :         case AT_EnableRule:
    6710           0 :             return "ENABLE RULE";
    6711           0 :         case AT_EnableAlwaysRule:
    6712           0 :             return "ENABLE ALWAYS RULE";
    6713           0 :         case AT_EnableReplicaRule:
    6714           0 :             return "ENABLE REPLICA RULE";
    6715           0 :         case AT_DisableRule:
    6716           0 :             return "DISABLE RULE";
    6717           0 :         case AT_AddInherit:
    6718           0 :             return "INHERIT";
    6719           0 :         case AT_DropInherit:
    6720           0 :             return "NO INHERIT";
    6721           0 :         case AT_AddOf:
    6722           0 :             return "OF";
    6723           0 :         case AT_DropOf:
    6724           0 :             return "NOT OF";
    6725           0 :         case AT_ReplicaIdentity:
    6726           0 :             return "REPLICA IDENTITY";
    6727           0 :         case AT_EnableRowSecurity:
    6728           0 :             return "ENABLE ROW SECURITY";
    6729           0 :         case AT_DisableRowSecurity:
    6730           0 :             return "DISABLE ROW SECURITY";
    6731           0 :         case AT_ForceRowSecurity:
    6732           0 :             return "FORCE ROW SECURITY";
    6733           0 :         case AT_NoForceRowSecurity:
    6734           0 :             return "NO FORCE ROW SECURITY";
    6735           0 :         case AT_GenericOptions:
    6736           0 :             return "OPTIONS";
    6737           6 :         case AT_AttachPartition:
    6738           6 :             return "ATTACH PARTITION";
    6739          18 :         case AT_DetachPartition:
    6740          18 :             return "DETACH PARTITION";
    6741           6 :         case AT_DetachPartitionFinalize:
    6742           6 :             return "DETACH PARTITION ... FINALIZE";
    6743           0 :         case AT_MergePartitions:
    6744           0 :             return "MERGE PARTITIONS";
    6745           6 :         case AT_SplitPartition:
    6746           6 :             return "SPLIT PARTITION";
    6747           0 :         case AT_AddIdentity:
    6748           0 :             return "ALTER COLUMN ... ADD IDENTITY";
    6749           0 :         case AT_SetIdentity:
    6750           0 :             return "ALTER COLUMN ... SET";
    6751           0 :         case AT_DropIdentity:
    6752           0 :             return "ALTER COLUMN ... DROP IDENTITY";
    6753           0 :         case AT_ReAddStatistics:
    6754           0 :             return NULL;        /* not real grammar */
    6755             :     }
    6756             : 
    6757           0 :     return NULL;
    6758             : }
    6759             : 
    6760             : /*
    6761             :  * ATSimplePermissions
    6762             :  *
    6763             :  * - Ensure that it is a relation (or possibly a view)
    6764             :  * - Ensure this user is the owner
    6765             :  * - Ensure that it is not a system table
    6766             :  */
    6767             : static void
    6768       39462 : ATSimplePermissions(AlterTableType cmdtype, Relation rel, int allowed_targets)
    6769             : {
    6770             :     int         actual_target;
    6771             : 
    6772       39462 :     switch (rel->rd_rel->relkind)
    6773             :     {
    6774       30650 :         case RELKIND_RELATION:
    6775       30650 :             actual_target = ATT_TABLE;
    6776       30650 :             break;
    6777        6524 :         case RELKIND_PARTITIONED_TABLE:
    6778        6524 :             actual_target = ATT_PARTITIONED_TABLE;
    6779        6524 :             break;
    6780         402 :         case RELKIND_VIEW:
    6781         402 :             actual_target = ATT_VIEW;
    6782         402 :             break;
    6783          46 :         case RELKIND_MATVIEW:
    6784          46 :             actual_target = ATT_MATVIEW;
    6785          46 :             break;
    6786         228 :         case RELKIND_INDEX:
    6787         228 :             actual_target = ATT_INDEX;
    6788         228 :             break;
    6789         438 :         case RELKIND_PARTITIONED_INDEX:
    6790         438 :             actual_target = ATT_PARTITIONED_INDEX;
    6791         438 :             break;
    6792         216 :         case RELKIND_COMPOSITE_TYPE:
    6793         216 :             actual_target = ATT_COMPOSITE_TYPE;
    6794         216 :             break;
    6795         932 :         case RELKIND_FOREIGN_TABLE:
    6796         932 :             actual_target = ATT_FOREIGN_TABLE;
    6797         932 :             break;
    6798          24 :         case RELKIND_SEQUENCE:
    6799          24 :             actual_target = ATT_SEQUENCE;
    6800          24 :             break;
    6801           2 :         default:
    6802           2 :             actual_target = 0;
    6803           2 :             break;
    6804             :     }
    6805             : 
    6806             :     /* Wrong target type? */
    6807       39462 :     if ((actual_target & allowed_targets) == 0)
    6808             :     {
    6809          86 :         const char *action_str = alter_table_type_to_string(cmdtype);
    6810             : 
    6811          86 :         if (action_str)
    6812          86 :             ereport(ERROR,
    6813             :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    6814             :             /* translator: %s is a group of some SQL keywords */
    6815             :                      errmsg("ALTER action %s cannot be performed on relation \"%s\"",
    6816             :                             action_str, RelationGetRelationName(rel)),
    6817             :                      errdetail_relkind_not_supported(rel->rd_rel->relkind)));
    6818             :         else
    6819             :             /* internal error? */
    6820           0 :             elog(ERROR, "invalid ALTER action attempted on relation \"%s\"",
    6821             :                  RelationGetRelationName(rel));
    6822             :     }
    6823             : 
    6824             :     /* Permissions checks */
    6825       39376 :     if (!object_ownercheck(RelationRelationId, RelationGetRelid(rel), GetUserId()))
    6826          12 :         aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(rel->rd_rel->relkind),
    6827          12 :                        RelationGetRelationName(rel));
    6828             : 
    6829       39364 :     if (!allowSystemTableMods && IsSystemRelation(rel))
    6830           0 :         ereport(ERROR,
    6831             :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
    6832             :                  errmsg("permission denied: \"%s\" is a system catalog",
    6833             :                         RelationGetRelationName(rel))));
    6834       39364 : }
    6835             : 
    6836             : /*
    6837             :  * ATSimpleRecursion
    6838             :  *
    6839             :  * Simple table recursion sufficient for most ALTER TABLE operations.
    6840             :  * All direct and indirect children are processed in an unspecified order.
    6841             :  * Note that if a child inherits from the original table via multiple
    6842             :  * inheritance paths, it will be visited just once.
    6843             :  */
    6844             : static void
    6845        1354 : ATSimpleRecursion(List **wqueue, Relation rel,
    6846             :                   AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode,
    6847             :                   AlterTableUtilityContext *context)
    6848             : {
    6849             :     /*
    6850             :      * Propagate to children, if desired and if there are (or might be) any
    6851             :      * children.
    6852             :      */
    6853        1354 :     if (recurse && rel->rd_rel->relhassubclass)
    6854             :     {
    6855          84 :         Oid         relid = RelationGetRelid(rel);
    6856             :         ListCell   *child;
    6857             :         List       *children;
    6858             : 
    6859          84 :         children = find_all_inheritors(relid, lockmode, NULL);
    6860             : 
    6861             :         /*
    6862             :          * find_all_inheritors does the recursive search of the inheritance
    6863             :          * hierarchy, so all we have to do is process all of the relids in the
    6864             :          * list that it returns.
    6865             :          */
    6866         366 :         foreach(child, children)
    6867             :         {
    6868         282 :             Oid         childrelid = lfirst_oid(child);
    6869             :             Relation    childrel;
    6870             : 
    6871         282 :             if (childrelid == relid)
    6872          84 :                 continue;
    6873             :             /* find_all_inheritors already got lock */
    6874         198 :             childrel = relation_open(childrelid, NoLock);
    6875         198 :             CheckAlterTableIsSafe(childrel);
    6876         198 :             ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode, context);
    6877         198 :             relation_close(childrel, NoLock);
    6878             :         }
    6879             :     }
    6880        1354 : }
    6881             : 
    6882             : /*
    6883             :  * Obtain list of partitions of the given table, locking them all at the given
    6884             :  * lockmode and ensuring that they all pass CheckAlterTableIsSafe.
    6885             :  *
    6886             :  * This function is a no-op if the given relation is not a partitioned table;
    6887             :  * in particular, nothing is done if it's a legacy inheritance parent.
    6888             :  */
    6889             : static void
    6890         818 : ATCheckPartitionsNotInUse(Relation rel, LOCKMODE lockmode)
    6891             : {
    6892         818 :     if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
    6893             :     {
    6894             :         List       *inh;
    6895             :         ListCell   *cell;
    6896             : 
    6897         176 :         inh = find_all_inheritors(RelationGetRelid(rel), lockmode, NULL);
    6898             :         /* first element is the parent rel; must ignore it */
    6899         574 :         for_each_from(cell, inh, 1)
    6900             :         {
    6901             :             Relation    childrel;
    6902             : 
    6903             :             /* find_all_inheritors already got lock */
    6904         404 :             childrel = table_open(lfirst_oid(cell), NoLock);
    6905         404 :             CheckAlterTableIsSafe(childrel);
    6906         398 :             table_close(childrel, NoLock);
    6907             :         }
    6908         170 :         list_free(inh);
    6909             :     }
    6910         812 : }
    6911             : 
    6912             : /*
    6913             :  * ATTypedTableRecursion
    6914             :  *
    6915             :  * Propagate ALTER TYPE operations to the typed tables of that type.
    6916             :  * Also check the RESTRICT/CASCADE behavior.  Given CASCADE, also permit
    6917             :  * recursion to inheritance children of the typed tables.
    6918             :  */
    6919             : static void
    6920         192 : ATTypedTableRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd,
    6921             :                       LOCKMODE lockmode, AlterTableUtilityContext *context)
    6922             : {
    6923             :     ListCell   *child;
    6924             :     List       *children;
    6925             : 
    6926             :     Assert(rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE);
    6927             : 
    6928         192 :     children = find_typed_table_dependencies(rel->rd_rel->reltype,
    6929         192 :                                              RelationGetRelationName(rel),
    6930             :                                              cmd->behavior);
    6931             : 
    6932         204 :     foreach(child, children)
    6933             :     {
    6934          30 :         Oid         childrelid = lfirst_oid(child);
    6935             :         Relation    childrel;
    6936             : 
    6937          30 :         childrel = relation_open(childrelid, lockmode);
    6938          30 :         CheckAlterTableIsSafe(childrel);
    6939          30 :         ATPrepCmd(wqueue, childrel, cmd, true, true, lockmode, context);
    6940          30 :         relation_close(childrel, NoLock);
    6941             :     }
    6942         174 : }
    6943             : 
    6944             : 
    6945             : /*
    6946             :  * find_composite_type_dependencies
    6947             :  *
    6948             :  * Check to see if the type "typeOid" is being used as a column in some table
    6949             :  * (possibly nested several levels deep in composite types, arrays, etc!).
    6950             :  * Eventually, we'd like to propagate the check or rewrite operation
    6951             :  * into such tables, but for now, just error out if we find any.
    6952             :  *
    6953             :  * Caller should provide either the associated relation of a rowtype,
    6954             :  * or a type name (not both) for use in the error message, if any.
    6955             :  *
    6956             :  * Note that "typeOid" is not necessarily a composite type; it could also be
    6957             :  * another container type such as an array or range, or a domain over one of
    6958             :  * these things.  The name of this function is therefore somewhat historical,
    6959             :  * but it's not worth changing.
    6960             :  *
    6961             :  * We assume that functions and views depending on the type are not reasons
    6962             :  * to reject the ALTER.  (How safe is this really?)
    6963             :  */
    6964             : void
    6965        4794 : find_composite_type_dependencies(Oid typeOid, Relation origRelation,
    6966             :                                  const char *origTypeName)
    6967             : {
    6968             :     Relation    depRel;
    6969             :     ScanKeyData key[2];
    6970             :     SysScanDesc depScan;
    6971             :     HeapTuple   depTup;
    6972             : 
    6973             :     /* since this function recurses, it could be driven to stack overflow */
    6974        4794 :     check_stack_depth();
    6975             : 
    6976             :     /*
    6977             :      * We scan pg_depend to find those things that depend on the given type.
    6978             :      * (We assume we can ignore refobjsubid for a type.)
    6979             :      */
    6980        4794 :     depRel = table_open(DependRelationId, AccessShareLock);
    6981             : 
    6982        4794 :     ScanKeyInit(&key[0],
    6983             :                 Anum_pg_depend_refclassid,
    6984             :                 BTEqualStrategyNumber, F_OIDEQ,
    6985             :                 ObjectIdGetDatum(TypeRelationId));
    6986        4794 :     ScanKeyInit(&key[1],
    6987             :                 Anum_pg_depend_refobjid,
    6988             :                 BTEqualStrategyNumber, F_OIDEQ,
    6989             :                 ObjectIdGetDatum(typeOid));
    6990             : 
    6991        4794 :     depScan = systable_beginscan(depRel, DependReferenceIndexId, true,
    6992             :                                  NULL, 2, key);
    6993             : 
    6994        7358 :     while (HeapTupleIsValid(depTup = systable_getnext(depScan)))
    6995             :     {
    6996        2720 :         Form_pg_depend pg_depend = (Form_pg_depend) GETSTRUCT(depTup);
    6997             :         Relation    rel;
    6998             :         TupleDesc   tupleDesc;
    6999             :         Form_pg_attribute att;
    7000             : 
    7001             :         /* Check for directly dependent types */
    7002        2720 :         if (pg_depend->classid == TypeRelationId)
    7003             :         {
    7004             :             /*
    7005             :              * This must be an array, domain, or range containing the given
    7006             :              * type, so recursively check for uses of this type.  Note that
    7007             :              * any error message will mention the original type not the
    7008             :              * container; this is intentional.
    7009             :              */
    7010        2314 :             find_composite_type_dependencies(pg_depend->objid,
    7011             :                                              origRelation, origTypeName);
    7012        2290 :             continue;
    7013             :         }
    7014             : 
    7015             :         /* Else, ignore dependees that aren't relations */
    7016         406 :         if (pg_depend->classid != RelationRelationId)
    7017         122 :             continue;
    7018             : 
    7019         284 :         rel = relation_open(pg_depend->objid, AccessShareLock);
    7020         284 :         tupleDesc = RelationGetDescr(rel);
    7021             : 
    7022             :         /*
    7023             :          * If objsubid identifies a specific column, refer to that in error
    7024             :          * messages.  Otherwise, search to see if there's a user column of the
    7025             :          * type.  (We assume system columns are never of interesting types.)
    7026             :          * The search is needed because an index containing an expression
    7027             :          * column of the target type will just be recorded as a whole-relation
    7028             :          * dependency.  If we do not find a column of the type, the dependency
    7029             :          * must indicate that the type is transiently referenced in an index
    7030             :          * expression but not stored on disk, which we assume is OK, just as
    7031             :          * we do for references in views.  (It could also be that the target
    7032             :          * type is embedded in some container type that is stored in an index
    7033             :          * column, but the previous recursion should catch such cases.)
    7034             :          */
    7035         284 :         if (pg_depend->objsubid > 0 && pg_depend->objsubid <= tupleDesc->natts)
    7036         126 :             att = TupleDescAttr(tupleDesc, pg_depend->objsubid - 1);
    7037             :         else
    7038             :         {
    7039         158 :             att = NULL;
    7040         406 :             for (int attno = 1; attno <= tupleDesc->natts; attno++)
    7041             :             {
    7042         254 :                 att = TupleDescAttr(tupleDesc, attno - 1);
    7043         254 :                 if (att->atttypid == typeOid && !att->attisdropped)
    7044           6 :                     break;
    7045         248 :                 att = NULL;
    7046             :             }
    7047         158 :             if (att == NULL)
    7048             :             {
    7049             :                 /* No such column, so assume OK */
    7050         152 :                 relation_close(rel, AccessShareLock);
    7051         152 :                 continue;
    7052             :             }
    7053             :         }
    7054             : 
    7055             :         /*
    7056             :          * We definitely should reject if the relation has storage.  If it's
    7057             :          * partitioned, then perhaps we don't have to reject: if there are
    7058             :          * partitions then we'll fail when we find one, else there is no
    7059             :          * stored data to worry about.  However, it's possible that the type
    7060             :          * change would affect conclusions about whether the type is sortable
    7061             :          * or hashable and thus (if it's a partitioning column) break the
    7062             :          * partitioning rule.  For now, reject for partitioned rels too.
    7063             :          */
    7064         132 :         if (RELKIND_HAS_STORAGE(rel->rd_rel->relkind) ||
    7065           0 :             RELKIND_HAS_PARTITIONS(rel->rd_rel->relkind))
    7066             :         {
    7067         132 :             if (origTypeName)
    7068          30 :                 ereport(ERROR,
    7069             :                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    7070             :                          errmsg("cannot alter type \"%s\" because column \"%s.%s\" uses it",
    7071             :                                 origTypeName,
    7072             :                                 RelationGetRelationName(rel),
    7073             :                                 NameStr(att->attname))));
    7074         102 :             else if (origRelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
    7075          18 :                 ereport(ERROR,
    7076             :                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    7077             :                          errmsg("cannot alter type \"%s\" because column \"%s.%s\" uses it",
    7078             :                                 RelationGetRelationName(origRelation),
    7079             :                                 RelationGetRelationName(rel),
    7080             :                                 NameStr(att->attname))));
    7081          84 :             else if (origRelation->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
    7082           6 :                 ereport(ERROR,
    7083             :                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    7084             :                          errmsg("cannot alter foreign table \"%s\" because column \"%s.%s\" uses its row type",
    7085             :                                 RelationGetRelationName(origRelation),
    7086             :                                 RelationGetRelationName(rel),
    7087             :                                 NameStr(att->attname))));
    7088             :             else
    7089          78 :                 ereport(ERROR,
    7090             :                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    7091             :                          errmsg("cannot alter table \"%s\" because column \"%s.%s\" uses its row type",
    7092             :                                 RelationGetRelationName(origRelation),
    7093             :                                 RelationGetRelationName(rel),
    7094             :                                 NameStr(att->attname))));
    7095             :         }
    7096           0 :         else if (OidIsValid(rel->rd_rel->reltype))
    7097             :         {
    7098             :             /*
    7099             :              * A view or composite type itself isn't a problem, but we must
    7100             :              * recursively check for indirect dependencies via its rowtype.
    7101             :              */
    7102           0 :             find_composite_type_dependencies(rel->rd_rel->reltype,
    7103             :                                              origRelation, origTypeName);
    7104             :         }
    7105             : 
    7106           0 :         relation_close(rel, AccessShareLock);
    7107             :     }
    7108             : 
    7109        4638 :     systable_endscan(depScan);
    7110             : 
    7111        4638 :     relation_close(depRel, AccessShareLock);
    7112        4638 : }
    7113             : 
    7114             : 
    7115             : /*
    7116             :  * find_typed_table_dependencies
    7117             :  *
    7118             :  * Check to see if a composite type is being used as the type of a
    7119             :  * typed table.  Abort if any are found and behavior is RESTRICT.
    7120             :  * Else return the list of tables.
    7121             :  */
    7122             : static List *
    7123         216 : find_typed_table_dependencies(Oid typeOid, const char *typeName, DropBehavior behavior)
    7124             : {
    7125             :     Relation    classRel;
    7126             :     ScanKeyData key[1];
    7127             :     TableScanDesc scan;
    7128             :     HeapTuple   tuple;
    7129         216 :     List       *result = NIL;
    7130             : 
    7131         216 :     classRel = table_open(RelationRelationId, AccessShareLock);
    7132             : 
    7133         216 :     ScanKeyInit(&key[0],
    7134             :                 Anum_pg_class_reloftype,
    7135             :                 BTEqualStrategyNumber, F_OIDEQ,
    7136             :                 ObjectIdGetDatum(typeOid));
    7137             : 
    7138         216 :     scan = table_beginscan_catalog(classRel, 1, key);
    7139             : 
    7140         252 :     while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
    7141             :     {
    7142          60 :         Form_pg_class classform = (Form_pg_class) GETSTRUCT(tuple);
    7143             : 
    7144          60 :         if (behavior == DROP_RESTRICT)
    7145          24 :             ereport(ERROR,
    7146             :                     (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
    7147             :                      errmsg("cannot alter type \"%s\" because it is the type of a typed table",
    7148             :                             typeName),
    7149             :                      errhint("Use ALTER ... CASCADE to alter the typed tables too.")));
    7150             :         else
    7151          36 :             result = lappend_oid(result, classform->oid);
    7152             :     }
    7153             : 
    7154         192 :     table_endscan(scan);
    7155         192 :     table_close(classRel, AccessShareLock);
    7156             : 
    7157         192 :     return result;
    7158             : }
    7159             : 
    7160             : 
    7161             : /*
    7162             :  * check_of_type
    7163             :  *
    7164             :  * Check whether a type is suitable for CREATE TABLE OF/ALTER TABLE OF.  If it
    7165             :  * isn't suitable, throw an error.  Currently, we require that the type
    7166             :  * originated with CREATE TYPE AS.  We could support any row type, but doing so
    7167             :  * would require handling a number of extra corner cases in the DDL commands.
    7168             :  * (Also, allowing domain-over-composite would open up a can of worms about
    7169             :  * whether and how the domain's constraints should apply to derived tables.)
    7170             :  */
    7171             : void
    7172         182 : check_of_type(HeapTuple typetuple)
    7173             : {
    7174         182 :     Form_pg_type typ = (Form_pg_type) GETSTRUCT(typetuple);
    7175         182 :     bool        typeOk = false;
    7176             : 
    7177         182 :     if (typ->typtype == TYPTYPE_COMPOSITE)
    7178             :     {
    7179             :         Relation    typeRelation;
    7180             : 
    7181             :         Assert(OidIsValid(typ->typrelid));
    7182         176 :         typeRelation = relation_open(typ->typrelid, AccessShareLock);
    7183         176 :         typeOk = (typeRelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE);
    7184             : 
    7185             :         /*
    7186             :          * Close the parent rel, but keep our AccessShareLock on it until xact
    7187             :          * commit.  That will prevent someone else from deleting or ALTERing
    7188             :          * the type before the typed table creation/conversion commits.
    7189             :          */
    7190         176 :         relation_close(typeRelation, NoLock);
    7191             : 
    7192         176 :         if (!typeOk)
    7193           6 :             ereport(ERROR,
    7194             :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    7195             :                      errmsg("type %s is the row type of another table",
    7196             :                             format_type_be(typ->oid)),
    7197             :                      errdetail("A typed table must use a stand-alone composite type created with CREATE TYPE.")));
    7198             :     }
    7199             :     else
    7200           6 :         ereport(ERROR,
    7201             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    7202             :                  errmsg("type %s is not a composite type",
    7203             :                         format_type_be(typ->oid))));
    7204         170 : }
    7205             : 
    7206             : 
    7207             : /*
    7208             :  * ALTER TABLE ADD COLUMN
    7209             :  *
    7210             :  * Adds an additional attribute to a relation making the assumption that
    7211             :  * CHECK, NOT NULL, and FOREIGN KEY constraints will be removed from the
    7212             :  * AT_AddColumn AlterTableCmd by parse_utilcmd.c and added as independent
    7213             :  * AlterTableCmd's.
    7214             :  *
    7215             :  * ADD COLUMN cannot use the normal ALTER TABLE recursion mechanism, because we
    7216             :  * have to decide at runtime whether to recurse or not depending on whether we
    7217             :  * actually add a column or merely merge with an existing column.  (We can't
    7218             :  * check this in a static pre-pass because it won't handle multiple inheritance
    7219             :  * situations correctly.)
    7220             :  */
    7221             : static void
    7222        2214 : ATPrepAddColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
    7223             :                 bool is_view, AlterTableCmd *cmd, LOCKMODE lockmode,
    7224             :                 AlterTableUtilityContext *context)
    7225             : {
    7226        2214 :     if (rel->rd_rel->reloftype && !recursing)
    7227           6 :         ereport(ERROR,
    7228             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    7229             :                  errmsg("cannot add column to typed table")));
    7230             : 
    7231        2208 :     if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
    7232          58 :         ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
    7233             : 
    7234        2202 :     if (recurse && !is_view)
    7235        2102 :         cmd->recurse = true;
    7236        2202 : }
    7237             : 
    7238             : /*
    7239             :  * Add a column to a table.  The return value is the address of the
    7240             :  * new column in the parent relation.
    7241             :  *
    7242             :  * cmd is pass-by-ref so that we can replace it with the parse-transformed
    7243             :  * copy (but that happens only after we check for IF NOT EXISTS).
    7244             :  */
    7245             : static ObjectAddress
    7246        2934 : ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
    7247             :                 AlterTableCmd **cmd, bool recurse, bool recursing,
    7248             :                 LOCKMODE lockmode, AlterTablePass cur_pass,
    7249             :                 AlterTableUtilityContext *context)
    7250             : {
    7251        2934 :     Oid         myrelid = RelationGetRelid(rel);
    7252        2934 :     ColumnDef  *colDef = castNode(ColumnDef, (*cmd)->def);
    7253        2934 :     bool        if_not_exists = (*cmd)->missing_ok;
    7254             :     Relation    pgclass,
    7255             :                 attrdesc;
    7256             :     HeapTuple   reltup;
    7257             :     Form_pg_class relform;
    7258             :     Form_pg_attribute attribute;
    7259             :     int         newattnum;
    7260             :     char        relkind;
    7261             :     Expr       *defval;
    7262             :     List       *children;
    7263             :     ListCell   *child;
    7264             :     AlterTableCmd *childcmd;
    7265             :     ObjectAddress address;
    7266             :     TupleDesc   tupdesc;
    7267             : 
    7268             :     /* since this function recurses, it could be driven to stack overflow */
    7269        2934 :     check_stack_depth();
    7270             : 
    7271             :     /* At top level, permission check was done in ATPrepCmd, else do it */
    7272        2934 :     if (recursing)
    7273         738 :         ATSimplePermissions((*cmd)->subtype, rel,
    7274             :                             ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
    7275             : 
    7276        2934 :     if (rel->rd_rel->relispartition && !recursing)
    7277          12 :         ereport(ERROR,
    7278             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    7279             :                  errmsg("cannot add column to a partition")));
    7280             : 
    7281        2922 :     attrdesc = table_open(AttributeRelationId, RowExclusiveLock);
    7282             : 
    7283             :     /*
    7284             :      * Are we adding the column to a recursion child?  If so, check whether to
    7285             :      * merge with an existing definition for the column.  If we do merge, we
    7286             :      * must not recurse.  Children will already have the column, and recursing
    7287             :      * into them would mess up attinhcount.
    7288             :      */
    7289        2922 :     if (colDef->inhcount > 0)
    7290             :     {
    7291             :         HeapTuple   tuple;
    7292             : 
    7293             :         /* Does child already have a column by this name? */
    7294         738 :         tuple = SearchSysCacheCopyAttName(myrelid, colDef->colname);
    7295         738 :         if (HeapTupleIsValid(tuple))
    7296             :         {
    7297          60 :             Form_pg_attribute childatt = (Form_pg_attribute) GETSTRUCT(tuple);
    7298             :             Oid         ctypeId;
    7299             :             int32       ctypmod;
    7300             :             Oid         ccollid;
    7301             : 
    7302             :             /* Child column must match on type, typmod, and collation */
    7303          60 :             typenameTypeIdAndMod(NULL, colDef->typeName, &ctypeId, &ctypmod);
    7304          60 :             if (ctypeId != childatt->atttypid ||
    7305          60 :                 ctypmod != childatt->atttypmod)
    7306           0 :                 ereport(ERROR,
    7307             :                         (errcode(ERRCODE_DATATYPE_MISMATCH),
    7308             :                          errmsg("child table \"%s\" has different type for column \"%s\"",
    7309             :                                 RelationGetRelationName(rel), colDef->colname)));
    7310          60 :             ccollid = GetColumnDefCollation(NULL, colDef, ctypeId);
    7311          60 :             if (ccollid != childatt->attcollation)
    7312           0 :                 ereport(ERROR,
    7313             :                         (errcode(ERRCODE_COLLATION_MISMATCH),
    7314             :                          errmsg("child table \"%s\" has different collation for column \"%s\"",
    7315             :                                 RelationGetRelationName(rel), colDef->colname),
    7316             :                          errdetail("\"%s\" versus \"%s\"",
    7317             :                                    get_collation_name(ccollid),
    7318             :                                    get_collation_name(childatt->attcollation))));
    7319             : 
    7320             :             /* Bump the existing child att's inhcount */
    7321          60 :             if (pg_add_s16_overflow(childatt->attinhcount, 1,
    7322             :                                     &childatt->attinhcount))
    7323           0 :                 ereport(ERROR,
    7324             :                         errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
    7325             :                         errmsg("too many inheritance parents"));
    7326          60 :             CatalogTupleUpdate(attrdesc, &tuple->t_self, tuple);
    7327             : 
    7328          60 :             heap_freetuple(tuple);
    7329             : 
    7330             :             /* Inform the user about the merge */
    7331          60 :             ereport(NOTICE,
    7332             :                     (errmsg("merging definition of column \"%s\" for child \"%s\"",
    7333             :                             colDef->colname, RelationGetRelationName(rel))));
    7334             : 
    7335          60 :             table_close(attrdesc, RowExclusiveLock);
    7336             : 
    7337             :             /* Make the child column change visible */
    7338          60 :             CommandCounterIncrement();
    7339             : 
    7340          60 :             return InvalidObjectAddress;
    7341             :         }
    7342             :     }
    7343             : 
    7344             :     /* skip if the name already exists and if_not_exists is true */
    7345        2862 :     if (!check_for_column_name_collision(rel, colDef->colname, if_not_exists))
    7346             :     {
    7347          54 :         table_close(attrdesc, RowExclusiveLock);
    7348          54 :         return InvalidObjectAddress;
    7349             :     }
    7350             : 
    7351             :     /*
    7352             :      * Okay, we need to add the column, so go ahead and do parse
    7353             :      * transformation.  This can result in queueing up, or even immediately
    7354             :      * executing, subsidiary operations (such as creation of unique indexes);
    7355             :      * so we mustn't do it until we have made the if_not_exists check.
    7356             :      *
    7357             :      * When recursing, the command was already transformed and we needn't do
    7358             :      * so again.  Also, if context isn't given we can't transform.  (That
    7359             :      * currently happens only for AT_AddColumnToView; we expect that view.c
    7360             :      * passed us a ColumnDef that doesn't need work.)
    7361             :      */
    7362        2778 :     if (context != NULL && !recursing)
    7363             :     {
    7364        2076 :         *cmd = ATParseTransformCmd(wqueue, tab, rel, *cmd, recurse, lockmode,
    7365             :                                    cur_pass, context);
    7366             :         Assert(*cmd != NULL);
    7367        2070 :         colDef = castNode(ColumnDef, (*cmd)->def);
    7368             :     }
    7369             : 
    7370             :     /*
    7371             :      * Regular inheritance children are independent enough not to inherit the
    7372             :      * identity column from parent hence cannot recursively add identity
    7373             :      * column if the table has inheritance children.
    7374             :      *
    7375             :      * Partitions, on the other hand, are integral part of a partitioned table
    7376             :      * and inherit identity column.  Hence propagate identity column down the
    7377             :      * partition hierarchy.
    7378             :      */
    7379        2772 :     if (colDef->identity &&
    7380          54 :         recurse &&
    7381         102 :         rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE &&
    7382          48 :         find_inheritance_children(myrelid, NoLock) != NIL)
    7383           6 :         ereport(ERROR,
    7384             :                 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    7385             :                  errmsg("cannot recursively add identity column to table that has child tables")));
    7386             : 
    7387        2766 :     pgclass = table_open(RelationRelationId, RowExclusiveLock);
    7388             : 
    7389        2766 :     reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(myrelid));
    7390        2766 :     if (!HeapTupleIsValid(reltup))
    7391           0 :         elog(ERROR, "cache lookup failed for relation %u", myrelid);
    7392        2766 :     relform = (Form_pg_class) GETSTRUCT(reltup);
    7393        2766 :     relkind = relform->relkind;
    7394             : 
    7395             :     /* Determine the new attribute's number */
    7396        2766 :     newattnum = relform->relnatts + 1;
    7397        2766 :     if (newattnum > MaxHeapAttributeNumber)
    7398           0 :         ereport(ERROR,
    7399             :                 (errcode(ERRCODE_TOO_MANY_COLUMNS),
    7400             :                  errmsg("tables can have at most %d columns",
    7401             :                         MaxHeapAttributeNumber)));
    7402             : 
    7403             :     /*
    7404             :      * Construct new attribute's pg_attribute entry.
    7405             :      */
    7406        2766 :     tupdesc = BuildDescForRelation(list_make1(colDef));
    7407             : 
    7408        2754 :     attribute = TupleDescAttr(tupdesc, 0);
    7409             : 
    7410             :     /* Fix up attribute number */
    7411        2754 :     attribute->attnum = newattnum;
    7412             : 
    7413             :     /* make sure datatype is legal for a column */
    7414        5508 :     CheckAttributeType(NameStr(attribute->attname), attribute->atttypid, attribute->attcollation,
    7415        2754 :                        list_make1_oid(rel->rd_rel->reltype),
    7416        2754 :                        (attribute->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL ? CHKATYPE_IS_VIRTUAL : 0));
    7417             : 
    7418        2718 :     InsertPgAttributeTuples(attrdesc, tupdesc, myrelid, NULL, NULL);
    7419             : 
    7420        2718 :     table_close(attrdesc, RowExclusiveLock);
    7421             : 
    7422             :     /*
    7423             :      * Update pg_class tuple as appropriate
    7424             :      */
    7425        2718 :     relform->relnatts = newattnum;
    7426             : 
    7427        2718 :     CatalogTupleUpdate(pgclass, &reltup->t_self, reltup);
    7428             : 
    7429        2718 :     heap_freetuple(reltup);
    7430             : 
    7431             :     /* Post creation hook for new attribute */
    7432        2718 :     InvokeObjectPostCreateHook(RelationRelationId, myrelid, newattnum);
    7433             : 
    7434        2718 :     table_close(pgclass, RowExclusiveLock);
    7435             : 
    7436             :     /* Make the attribute's catalog entry visible */
    7437        2718 :     CommandCounterIncrement();
    7438             : 
    7439             :     /*
    7440             :      * Store the DEFAULT, if any, in the catalogs
    7441             :      */
    7442        2718 :     if (colDef->raw_default)
    7443             :     {
    7444             :         RawColumnDefault *rawEnt;
    7445             : 
    7446         950 :         rawEnt = palloc_object(RawColumnDefault);
    7447         950 :         rawEnt->attnum = attribute->attnum;
    7448         950 :         rawEnt->raw_default = copyObject(colDef->raw_default);
    7449         950 :         rawEnt->generated = colDef->generated;
    7450             : 
    7451             :         /*
    7452             :          * This function is intended for CREATE TABLE, so it processes a
    7453             :          * _list_ of defaults, but we just do one.
    7454             :          */
    7455         950 :         AddRelationNewConstraints(rel, list_make1(rawEnt), NIL,
    7456             :                                   false, true, false, NULL);
    7457             : 
    7458             :         /* Make the additional catalog changes visible */
    7459         926 :         CommandCounterIncrement();
    7460             :     }
    7461             : 
    7462             :     /*
    7463             :      * Tell Phase 3 to fill in the default expression, if there is one.
    7464             :      *
    7465             :      * If there is no default, Phase 3 doesn't have to do anything, because
    7466             :      * that effectively means that the default is NULL.  The heap tuple access
    7467             :      * routines always check for attnum > # of attributes in tuple, and return
    7468             :      * NULL if so, so without any modification of the tuple data we will get
    7469             :      * the effect of NULL values in the new column.
    7470             :      *
    7471             :      * An exception occurs when the new column is of a domain type: the domain
    7472             :      * might have a not-null constraint, or a check constraint that indirectly
    7473             :      * rejects nulls.  If there are any domain constraints then we construct
    7474             :      * an explicit NULL default value that will be passed through
    7475             :      * CoerceToDomain processing.  (This is a tad inefficient, since it causes
    7476             :      * rewriting the table which we really wouldn't have to do; but we do it
    7477             :      * to preserve the historical behavior that such a failure will be raised
    7478             :      * only if the table currently contains some rows.)
    7479             :      *
    7480             :      * Note: we use build_column_default, and not just the cooked default
    7481             :      * returned by AddRelationNewConstraints, so that the right thing happens
    7482             :      * when a datatype's default applies.
    7483             :      *
    7484             :      * Note: it might seem that this should happen at the end of Phase 2, so
    7485             :      * that the effects of subsequent subcommands can be taken into account.
    7486             :      * It's intentional that we do it now, though.  The new column should be
    7487             :      * filled according to what is said in the ADD COLUMN subcommand, so that
    7488             :      * the effects are the same as if this subcommand had been run by itself
    7489             :      * and the later subcommands had been issued in new ALTER TABLE commands.
    7490             :      *
    7491             :      * We can skip this entirely for relations without storage, since Phase 3
    7492             :      * is certainly not going to touch them.
    7493             :      */
    7494        2694 :     if (RELKIND_HAS_STORAGE(relkind))
    7495             :     {
    7496             :         bool        has_domain_constraints;
    7497        2318 :         bool        has_missing = false;
    7498             : 
    7499             :         /*
    7500             :          * For an identity column, we can't use build_column_default(),
    7501             :          * because the sequence ownership isn't set yet.  So do it manually.
    7502             :          */
    7503        2318 :         if (colDef->identity)
    7504             :         {
    7505          42 :             NextValueExpr *nve = makeNode(NextValueExpr);
    7506             : 
    7507          42 :             nve->seqid = RangeVarGetRelid(colDef->identitySequence, NoLock, false);
    7508          42 :             nve->typeId = attribute->atttypid;
    7509             : 
    7510          42 :             defval = (Expr *) nve;
    7511             :         }
    7512             :         else
    7513        2276 :             defval = (Expr *) build_column_default(rel, attribute->attnum);
    7514             : 
    7515             :         /* Build CoerceToDomain(NULL) expression if needed */
    7516        2318 :         has_domain_constraints = DomainHasConstraints(attribute->atttypid);
    7517        2318 :         if (!defval && has_domain_constraints)
    7518             :         {
    7519             :             Oid         baseTypeId;
    7520             :             int32       baseTypeMod;
    7521             :             Oid         baseTypeColl;
    7522             : 
    7523           6 :             baseTypeMod = attribute->atttypmod;
    7524           6 :             baseTypeId = getBaseTypeAndTypmod(attribute->atttypid, &baseTypeMod);
    7525           6 :             baseTypeColl = get_typcollation(baseTypeId);
    7526           6 :             defval = (Expr *) makeNullConst(baseTypeId, baseTypeMod, baseTypeColl);
    7527           6 :             defval = (Expr *) coerce_to_target_type(NULL,
    7528             :                                                     (Node *) defval,
    7529             :                                                     baseTypeId,
    7530             :                                                     attribute->atttypid,
    7531             :                                                     attribute->atttypmod,
    7532             :                                                     COERCION_ASSIGNMENT,
    7533             :                                                     COERCE_IMPLICIT_CAST,
    7534             :                                                     -1);
    7535           6 :             if (defval == NULL) /* should not happen */
    7536           0 :                 elog(ERROR, "failed to coerce base type to domain");
    7537             :         }
    7538             : 
    7539        2318 :         if (defval)
    7540             :         {
    7541             :             NewColumnValue *newval;
    7542             : 
    7543             :             /* Prepare defval for execution, either here or in Phase 3 */
    7544         822 :             defval = expression_planner(defval);
    7545             : 
    7546             :             /* Add the new default to the newvals list */
    7547         822 :             newval = palloc0_object(NewColumnValue);
    7548         822 :             newval->attnum = attribute->attnum;
    7549         822 :             newval->expr = defval;
    7550         822 :             newval->is_generated = (colDef->generated != '\0');
    7551             : 
    7552         822 :             tab->newvals = lappend(tab->newvals, newval);
    7553             : 
    7554             :             /*
    7555             :              * Attempt to skip a complete table rewrite by storing the
    7556             :              * specified DEFAULT value outside of the heap.  This is only
    7557             :              * allowed for plain relations and non-generated columns, and the
    7558             :              * default expression can't be volatile (stable is OK).  Note that
    7559             :              * contain_volatile_functions deems CoerceToDomain immutable, but
    7560             :              * here we consider that coercion to a domain with constraints is
    7561             :              * volatile; else it might fail even when the table is empty.
    7562             :              */
    7563         822 :             if (rel->rd_rel->relkind == RELKIND_RELATION &&
    7564         822 :                 !colDef->generated &&
    7565         694 :                 !has_domain_constraints &&
    7566         682 :                 !contain_volatile_functions((Node *) defval))
    7567         514 :             {
    7568             :                 EState     *estate;
    7569             :                 ExprState  *exprState;
    7570             :                 Datum       missingval;
    7571             :                 bool        missingIsNull;
    7572             : 
    7573             :                 /* Evaluate the default expression */
    7574         514 :                 estate = CreateExecutorState();
    7575         514 :                 exprState = ExecPrepareExpr(defval, estate);
    7576         514 :                 missingval = ExecEvalExpr(exprState,
    7577         514 :                                           GetPerTupleExprContext(estate),
    7578             :                                           &missingIsNull);
    7579             :                 /* If it turns out NULL, nothing to do; else store it */
    7580         514 :                 if (!missingIsNull)
    7581             :                 {
    7582         514 :                     StoreAttrMissingVal(rel, attribute->attnum, missingval);
    7583             :                     /* Make the additional catalog change visible */
    7584         514 :                     CommandCounterIncrement();
    7585         514 :                     has_missing = true;
    7586             :                 }
    7587         514 :                 FreeExecutorState(estate);
    7588             :             }
    7589             :             else
    7590             :             {
    7591             :                 /*
    7592             :                  * Failed to use missing mode.  We have to do a table rewrite
    7593             :                  * to install the value --- unless it's a virtual generated
    7594             :                  * column.
    7595             :                  */
    7596         308 :                 if (colDef->generated != ATTRIBUTE_GENERATED_VIRTUAL)
    7597         216 :                     tab->rewrite |= AT_REWRITE_DEFAULT_VAL;
    7598             :             }
    7599             :         }
    7600             : 
    7601        2318 :         if (!has_missing)
    7602             :         {
    7603             :             /*
    7604             :              * If the new column is NOT NULL, and there is no missing value,
    7605             :              * tell Phase 3 it needs to check for NULLs.
    7606             :              */
    7607        1804 :             tab->verify_new_notnull |= colDef->is_not_null;
    7608             :         }
    7609             :     }
    7610             : 
    7611             :     /*
    7612             :      * Add needed dependency entries for the new column.
    7613             :      */
    7614        2694 :     add_column_datatype_dependency(myrelid, newattnum, attribute->atttypid);
    7615        2694 :     add_column_collation_dependency(myrelid, newattnum, attribute->attcollation);
    7616             : 
    7617             :     /*
    7618             :      * Propagate to children as appropriate.  Unlike most other ALTER
    7619             :      * routines, we have to do this one level of recursion at a time; we can't
    7620             :      * use find_all_inheritors to do it in one pass.
    7621             :      */
    7622             :     children =
    7623        2694 :         find_inheritance_children(RelationGetRelid(rel), lockmode);
    7624             : 
    7625             :     /*
    7626             :      * If we are told not to recurse, there had better not be any child
    7627             :      * tables; else the addition would put them out of step.
    7628             :      */
    7629        2694 :     if (children && !recurse)
    7630          12 :         ereport(ERROR,
    7631             :                 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    7632             :                  errmsg("column must be added to child tables too")));
    7633             : 
    7634             :     /* Children should see column as singly inherited */
    7635        2682 :     if (!recursing)
    7636             :     {
    7637        2004 :         childcmd = copyObject(*cmd);
    7638        2004 :         colDef = castNode(ColumnDef, childcmd->def);
    7639        2004 :         colDef->inhcount = 1;
    7640        2004 :         colDef->is_local = false;
    7641             :     }
    7642             :     else
    7643         678 :         childcmd = *cmd;        /* no need to copy again */
    7644             : 
    7645        3420 :     foreach(child, children)
    7646             :     {
    7647         738 :         Oid         childrelid = lfirst_oid(child);
    7648             :         Relation    childrel;
    7649             :         AlteredTableInfo *childtab;
    7650             : 
    7651             :         /* find_inheritance_children already got lock */
    7652         738 :         childrel = table_open(childrelid, NoLock);
    7653         738 :         CheckAlterTableIsSafe(childrel);
    7654             : 
    7655             :         /* Find or create work queue entry for this table */
    7656         738 :         childtab = ATGetQueueEntry(wqueue, childrel);
    7657             : 
    7658             :         /* Recurse to child; return value is ignored */
    7659         738 :         ATExecAddColumn(wqueue, childtab, childrel,
    7660             :                         &childcmd, recurse, true,
    7661             :                         lockmode, cur_pass, context);
    7662             : 
    7663         738 :         table_close(childrel, NoLock);
    7664             :     }
    7665             : 
    7666        2682 :     ObjectAddressSubSet(address, RelationRelationId, myrelid, newattnum);
    7667        2682 :     return address;
    7668             : }
    7669             : 
    7670             : /*
    7671             :  * If a new or renamed column will collide with the name of an existing
    7672             :  * column and if_not_exists is false then error out, else do nothing.
    7673             :  */
    7674             : static bool
    7675        3312 : check_for_column_name_collision(Relation rel, const char *colname,
    7676             :                                 bool if_not_exists)
    7677             : {
    7678             :     HeapTuple   attTuple;
    7679             :     int         attnum;
    7680             : 
    7681             :     /*
    7682             :      * this test is deliberately not attisdropped-aware, since if one tries to
    7683             :      * add a column matching a dropped column name, it's gonna fail anyway.
    7684             :      */
    7685        3312 :     attTuple = SearchSysCache2(ATTNAME,
    7686             :                                ObjectIdGetDatum(RelationGetRelid(rel)),
    7687             :                                PointerGetDatum(colname));
    7688        3312 :     if (!HeapTupleIsValid(attTuple))
    7689        3216 :         return true;
    7690             : 
    7691          96 :     attnum = ((Form_pg_attribute) GETSTRUCT(attTuple))->attnum;
    7692          96 :     ReleaseSysCache(attTuple);
    7693             : 
    7694             :     /*
    7695             :      * We throw a different error message for conflicts with system column
    7696             :      * names, since they are normally not shown and the user might otherwise
    7697             :      * be confused about the reason for the conflict.
    7698             :      */
    7699          96 :     if (attnum <= 0)
    7700          12 :         ereport(ERROR,
    7701             :                 (errcode(ERRCODE_DUPLICATE_COLUMN),
    7702             :                  errmsg("column name \"%s\" conflicts with a system column name",
    7703             :                         colname)));
    7704             :     else
    7705             :     {
    7706          84 :         if (if_not_exists)
    7707             :         {
    7708          54 :             ereport(NOTICE,
    7709             :                     (errcode(ERRCODE_DUPLICATE_COLUMN),
    7710             :                      errmsg("column \"%s\" of relation \"%s\" already exists, skipping",
    7711             :                             colname, RelationGetRelationName(rel))));
    7712          54 :             return false;
    7713             :         }
    7714             : 
    7715          30 :         ereport(ERROR,
    7716             :                 (errcode(ERRCODE_DUPLICATE_COLUMN),
    7717             :                  errmsg("column \"%s\" of relation \"%s\" already exists",
    7718             :                         colname, RelationGetRelationName(rel))));
    7719             :     }
    7720             : 
    7721             :     return true;
    7722             : }
    7723             : 
    7724             : /*
    7725             :  * Install a column's dependency on its datatype.
    7726             :  */
    7727             : static void
    7728        3844 : add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid)
    7729             : {
    7730             :     ObjectAddress myself,
    7731             :                 referenced;
    7732             : 
    7733        3844 :     myself.classId = RelationRelationId;
    7734        3844 :     myself.objectId = relid;
    7735        3844 :     myself.objectSubId = attnum;
    7736        3844 :     referenced.classId = TypeRelationId;
    7737        3844 :     referenced.objectId = typid;
    7738        3844 :     referenced.objectSubId = 0;
    7739        3844 :     recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
    7740        3844 : }
    7741             : 
    7742             : /*
    7743             :  * Install a column's dependency on its collation.
    7744             :  */
    7745             : static void
    7746        3844 : add_column_collation_dependency(Oid relid, int32 attnum, Oid collid)
    7747             : {
    7748             :     ObjectAddress myself,
    7749             :                 referenced;
    7750             : 
    7751             :     /* We know the default collation is pinned, so don't bother recording it */
    7752        3844 :     if (OidIsValid(collid) && collid != DEFAULT_COLLATION_OID)
    7753             :     {
    7754          18 :         myself.classId = RelationRelationId;
    7755          18 :         myself.objectId = relid;
    7756          18 :         myself.objectSubId = attnum;
    7757          18 :         referenced.classId = CollationRelationId;
    7758          18 :         referenced.objectId = collid;
    7759          18 :         referenced.objectSubId = 0;
    7760          18 :         recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
    7761             :     }
    7762        3844 : }
    7763             : 
    7764             : /*
    7765             :  * ALTER TABLE ALTER COLUMN DROP NOT NULL
    7766             :  *
    7767             :  * Return the address of the modified column.  If the column was already
    7768             :  * nullable, InvalidObjectAddress is returned.
    7769             :  */
    7770             : static ObjectAddress
    7771         268 : ATExecDropNotNull(Relation rel, const char *colName, bool recurse,
    7772             :                   LOCKMODE lockmode)
    7773             : {
    7774             :     HeapTuple   tuple;
    7775             :     HeapTuple   conTup;
    7776             :     Form_pg_attribute attTup;
    7777             :     AttrNumber  attnum;
    7778             :     Relation    attr_rel;
    7779             :     ObjectAddress address;
    7780             : 
    7781             :     /*
    7782             :      * lookup the attribute
    7783             :      */
    7784         268 :     attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
    7785             : 
    7786         268 :     tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
    7787         268 :     if (!HeapTupleIsValid(tuple))
    7788          18 :         ereport(ERROR,
    7789             :                 (errcode(ERRCODE_UNDEFINED_COLUMN),
    7790             :                  errmsg("column \"%s\" of relation \"%s\" does not exist",
    7791             :                         colName, RelationGetRelationName(rel))));
    7792         250 :     attTup = (Form_pg_attribute) GETSTRUCT(tuple);
    7793         250 :     attnum = attTup->attnum;
    7794         250 :     ObjectAddressSubSet(address, RelationRelationId,
    7795             :                         RelationGetRelid(rel), attnum);
    7796             : 
    7797             :     /* If the column is already nullable there's nothing to do. */
    7798         250 :     if (!attTup->attnotnull)
    7799             :     {
    7800           0 :         table_close(attr_rel, RowExclusiveLock);
    7801           0 :         return InvalidObjectAddress;
    7802             :     }
    7803             : 
    7804             :     /* Prevent them from altering a system attribute */
    7805         250 :     if (attnum <= 0)
    7806           0 :         ereport(ERROR,
    7807             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    7808             :                  errmsg("cannot alter system column \"%s\"",
    7809             :                         colName)));
    7810             : 
    7811         250 :     if (attTup->attidentity)
    7812          18 :         ereport(ERROR,
    7813             :                 (errcode(ERRCODE_SYNTAX_ERROR),
    7814             :                  errmsg("column \"%s\" of relation \"%s\" is an identity column",
    7815             :                         colName, RelationGetRelationName(rel))));
    7816             : 
    7817             :     /*
    7818             :      * If rel is partition, shouldn't drop NOT NULL if parent has the same.
    7819             :      */
    7820         232 :     if (rel->rd_rel->relispartition)
    7821             :     {
    7822          12 :         Oid         parentId = get_partition_parent(RelationGetRelid(rel), false);
    7823          12 :         Relation    parent = table_open(parentId, AccessShareLock);
    7824          12 :         TupleDesc   tupDesc = RelationGetDescr(parent);
    7825             :         AttrNumber  parent_attnum;
    7826             : 
    7827          12 :         parent_attnum = get_attnum(parentId, colName);
    7828          12 :         if (TupleDescAttr(tupDesc, parent_attnum - 1)->attnotnull)
    7829          12 :             ereport(ERROR,
    7830             :                     (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    7831             :                      errmsg("column \"%s\" is marked NOT NULL in parent table",
    7832             :                             colName)));
    7833           0 :         table_close(parent, AccessShareLock);
    7834             :     }
    7835             : 
    7836             :     /*
    7837             :      * Find the constraint that makes this column NOT NULL, and drop it.
    7838             :      * dropconstraint_internal() resets attnotnull.
    7839             :      */
    7840         220 :     conTup = findNotNullConstraintAttnum(RelationGetRelid(rel), attnum);
    7841         220 :     if (conTup == NULL)
    7842           0 :         elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation \"%s\"",
    7843             :              colName, RelationGetRelationName(rel));
    7844             : 
    7845             :     /* The normal case: we have a pg_constraint row, remove it */
    7846         220 :     dropconstraint_internal(rel, conTup, DROP_RESTRICT, recurse, false,
    7847             :                             false, lockmode);
    7848         166 :     heap_freetuple(conTup);
    7849             : 
    7850         166 :     InvokeObjectPostAlterHook(RelationRelationId,
    7851             :                               RelationGetRelid(rel), attnum);
    7852             : 
    7853         166 :     table_close(attr_rel, RowExclusiveLock);
    7854             : 
    7855         166 :     return address;
    7856             : }
    7857             : 
    7858             : /*
    7859             :  * set_attnotnull
    7860             :  *      Helper to update/validate the pg_attribute status of a not-null
    7861             :  *      constraint
    7862             :  *
    7863             :  * pg_attribute.attnotnull is set true, if it isn't already.
    7864             :  * If queue_validation is true, also set up wqueue to validate the constraint.
    7865             :  * wqueue may be given as NULL when validation is not needed (e.g., on table
    7866             :  * creation).
    7867             :  */
    7868             : static void
    7869       25980 : set_attnotnull(List **wqueue, Relation rel, AttrNumber attnum,
    7870             :                bool is_valid, bool queue_validation)
    7871             : {
    7872             :     Form_pg_attribute attr;
    7873             :     CompactAttribute *thisatt;
    7874             : 
    7875             :     Assert(!queue_validation || wqueue);
    7876             : 
    7877       25980 :     CheckAlterTableIsSafe(rel);
    7878             : 
    7879             :     /*
    7880             :      * Exit quickly by testing attnotnull from the tupledesc's copy of the
    7881             :      * attribute.
    7882             :      */
    7883       25980 :     attr = TupleDescAttr(RelationGetDescr(rel), attnum - 1);
    7884       25980 :     if (attr->attisdropped)
    7885           0 :         return;
    7886             : 
    7887       25980 :     if (!attr->attnotnull)
    7888             :     {
    7889             :         Relation    attr_rel;
    7890             :         HeapTuple   tuple;
    7891             : 
    7892        1494 :         attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
    7893             : 
    7894        1494 :         tuple = SearchSysCacheCopyAttNum(RelationGetRelid(rel), attnum);
    7895        1494 :         if (!HeapTupleIsValid(tuple))
    7896           0 :             elog(ERROR, "cache lookup failed for attribute %d of relation %u",
    7897             :                  attnum, RelationGetRelid(rel));
    7898             : 
    7899        1494 :         thisatt = TupleDescCompactAttr(RelationGetDescr(rel), attnum - 1);
    7900        1494 :         thisatt->attnullability = ATTNULLABLE_VALID;
    7901             : 
    7902        1494 :         attr = (Form_pg_attribute) GETSTRUCT(tuple);
    7903             : 
    7904        1494 :         attr->attnotnull = true;
    7905        1494 :         CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
    7906             : 
    7907             :         /*
    7908             :          * If the nullness isn't already proven by validated constraints, have
    7909             :          * ALTER TABLE phase 3 test for it.
    7910             :          */
    7911        1494 :         if (queue_validation && wqueue &&
    7912        1246 :             !NotNullImpliedByRelConstraints(rel, attr))
    7913             :         {
    7914             :             AlteredTableInfo *tab;
    7915             : 
    7916        1196 :             tab = ATGetQueueEntry(wqueue, rel);
    7917        1196 :             tab->verify_new_notnull = true;
    7918             :         }
    7919             : 
    7920        1494 :         CommandCounterIncrement();
    7921             : 
    7922        1494 :         table_close(attr_rel, RowExclusiveLock);
    7923        1494 :         heap_freetuple(tuple);
    7924             :     }
    7925             :     else
    7926             :     {
    7927       24486 :         CacheInvalidateRelcache(rel);
    7928             :     }
    7929             : }
    7930             : 
    7931             : /*
    7932             :  * ALTER TABLE ALTER COLUMN SET NOT NULL
    7933             :  *
    7934             :  * Add a not-null constraint to a single table and its children.  Returns
    7935             :  * the address of the constraint added to the parent relation, if one gets
    7936             :  * added, or InvalidObjectAddress otherwise.
    7937             :  *
    7938             :  * We must recurse to child tables during execution, rather than using
    7939             :  * ALTER TABLE's normal prep-time recursion.
    7940             :  */
    7941             : static ObjectAddress
    7942         712 : ATExecSetNotNull(List **wqueue, Relation rel, char *conName, char *colName,
    7943             :                  bool recurse, bool recursing, LOCKMODE lockmode)
    7944             : {
    7945             :     HeapTuple   tuple;
    7946             :     AttrNumber  attnum;
    7947             :     ObjectAddress address;
    7948             :     Constraint *constraint;
    7949             :     CookedConstraint *ccon;
    7950             :     List       *cooked;
    7951         712 :     bool        is_no_inherit = false;
    7952             : 
    7953             :     /* Guard against stack overflow due to overly deep inheritance tree. */
    7954         712 :     check_stack_depth();
    7955             : 
    7956             :     /* At top level, permission check was done in ATPrepCmd, else do it */
    7957         712 :     if (recursing)
    7958             :     {
    7959         298 :         ATSimplePermissions(AT_AddConstraint, rel,
    7960             :                             ATT_PARTITIONED_TABLE | ATT_TABLE | ATT_FOREIGN_TABLE);
    7961             :         Assert(conName != NULL);
    7962             :     }
    7963             : 
    7964         712 :     attnum = get_attnum(RelationGetRelid(rel), colName);
    7965         712 :     if (attnum == InvalidAttrNumber)
    7966          18 :         ereport(ERROR,
    7967             :                 (errcode(ERRCODE_UNDEFINED_COLUMN),
    7968             :                  errmsg("column \"%s\" of relation \"%s\" does not exist",
    7969             :                         colName, RelationGetRelationName(rel))));
    7970             : 
    7971             :     /* Prevent them from altering a system attribute */
    7972         694 :     if (attnum <= 0)
    7973           0 :         ereport(ERROR,
    7974             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    7975             :                  errmsg("cannot alter system column \"%s\"",
    7976             :                         colName)));
    7977             : 
    7978             :     /* See if there's already a constraint */
    7979         694 :     tuple = findNotNullConstraintAttnum(RelationGetRelid(rel), attnum);
    7980         694 :     if (HeapTupleIsValid(tuple))
    7981             :     {
    7982         158 :         Form_pg_constraint conForm = (Form_pg_constraint) GETSTRUCT(tuple);
    7983         158 :         bool        changed = false;
    7984             : 
    7985             :         /*
    7986             :          * Don't let a NO INHERIT constraint be changed into inherit.
    7987             :          */
    7988         158 :         if (conForm->connoinherit && recurse)
    7989          12 :             ereport(ERROR,
    7990             :                     errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    7991             :                     errmsg("cannot change NO INHERIT status of NOT NULL constraint \"%s\" on relation \"%s\"",
    7992             :                            NameStr(conForm->conname),
    7993             :                            RelationGetRelationName(rel)));
    7994             : 
    7995             :         /*
    7996             :          * If we find an appropriate constraint, we're almost done, but just
    7997             :          * need to change some properties on it: if we're recursing, increment
    7998             :          * coninhcount; if not, set conislocal if not already set.
    7999             :          */
    8000         146 :         if (recursing)
    8001             :         {
    8002         102 :             if (pg_add_s16_overflow(conForm->coninhcount, 1,
    8003             :                                     &conForm->coninhcount))
    8004           0 :                 ereport(ERROR,
    8005             :                         errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
    8006             :                         errmsg("too many inheritance parents"));
    8007         102 :             changed = true;
    8008             :         }
    8009          44 :         else if (!conForm->conislocal)
    8010             :         {
    8011           0 :             conForm->conislocal = true;
    8012           0 :             changed = true;
    8013             :         }
    8014          44 :         else if (!conForm->convalidated)
    8015             :         {
    8016             :             /*
    8017             :              * Flip attnotnull and convalidated, and also validate the
    8018             :              * constraint.
    8019             :              */
    8020          24 :             return ATExecValidateConstraint(wqueue, rel, NameStr(conForm->conname),
    8021             :                                             recurse, recursing, lockmode);
    8022             :         }
    8023             : 
    8024         122 :         if (changed)
    8025             :         {
    8026             :             Relation    constr_rel;
    8027             : 
    8028         102 :             constr_rel = table_open(ConstraintRelationId, RowExclusiveLock);
    8029             : 
    8030         102 :             CatalogTupleUpdate(constr_rel, &tuple->t_self, tuple);
    8031         102 :             ObjectAddressSet(address, ConstraintRelationId, conForm->oid);
    8032         102 :             table_close(constr_rel, RowExclusiveLock);
    8033             :         }
    8034             : 
    8035         122 :         if (changed)
    8036         102 :             return address;
    8037             :         else
    8038          20 :             return InvalidObjectAddress;
    8039             :     }
    8040             : 
    8041             :     /*
    8042             :      * If we're asked not to recurse, and children exist, raise an error for
    8043             :      * partitioned tables.  For inheritance, we act as if NO INHERIT had been
    8044             :      * specified.
    8045             :      */
    8046         566 :     if (!recurse &&
    8047          30 :         find_inheritance_children(RelationGetRelid(rel),
    8048             :                                   NoLock) != NIL)
    8049             :     {
    8050          18 :         if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
    8051           6 :             ereport(ERROR,
    8052             :                     errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    8053             :                     errmsg("constraint must be added to child tables too"),
    8054             :                     errhint("Do not specify the ONLY keyword."));
    8055             :         else
    8056          12 :             is_no_inherit = true;
    8057             :     }
    8058             : 
    8059             :     /*
    8060             :      * No constraint exists; we must add one.  First determine a name to use,
    8061             :      * if we haven't already.
    8062             :      */
    8063         530 :     if (!recursing)
    8064             :     {
    8065             :         Assert(conName == NULL);
    8066         340 :         conName = ChooseConstraintName(RelationGetRelationName(rel),
    8067             :                                        colName, "not_null",
    8068         340 :                                        RelationGetNamespace(rel),
    8069             :                                        NIL);
    8070             :     }
    8071             : 
    8072         530 :     constraint = makeNotNullConstraint(makeString(colName));
    8073         530 :     constraint->is_no_inherit = is_no_inherit;
    8074         530 :     constraint->conname = conName;
    8075             : 
    8076             :     /* and do it */
    8077         530 :     cooked = AddRelationNewConstraints(rel, NIL, list_make1(constraint),
    8078         530 :                                        false, !recursing, false, NULL);
    8079         530 :     ccon = linitial(cooked);
    8080         530 :     ObjectAddressSet(address, ConstraintRelationId, ccon->conoid);
    8081             : 
    8082         530 :     InvokeObjectPostAlterHook(RelationRelationId,
    8083             :                               RelationGetRelid(rel), attnum);
    8084             : 
    8085             :     /* Mark pg_attribute.attnotnull for the column and queue validation */
    8086         530 :     set_attnotnull(wqueue, rel, attnum, true, true);
    8087             : 
    8088             :     /*
    8089             :      * Recurse to propagate the constraint to children that don't have one.
    8090             :      */
    8091         530 :     if (recurse)
    8092             :     {
    8093             :         List       *children;
    8094             : 
    8095         506 :         children = find_inheritance_children(RelationGetRelid(rel),
    8096             :                                              lockmode);
    8097             : 
    8098        1244 :         foreach_oid(childoid, children)
    8099             :         {
    8100         244 :             Relation    childrel = table_open(childoid, NoLock);
    8101             : 
    8102         244 :             CommandCounterIncrement();
    8103             : 
    8104         244 :             ATExecSetNotNull(wqueue, childrel, conName, colName,
    8105             :                              recurse, true, lockmode);
    8106         238 :             table_close(childrel, NoLock);
    8107             :         }
    8108             :     }
    8109             : 
    8110         524 :     return address;
    8111             : }
    8112             : 
    8113             : /*
    8114             :  * NotNullImpliedByRelConstraints
    8115             :  *      Does rel's existing constraints imply NOT NULL for the given attribute?
    8116             :  */
    8117             : static bool
    8118        1246 : NotNullImpliedByRelConstraints(Relation rel, Form_pg_attribute attr)
    8119             : {
    8120        1246 :     NullTest   *nnulltest = makeNode(NullTest);
    8121             : 
    8122        2492 :     nnulltest->arg = (Expr *) makeVar(1,
    8123        1246 :                                       attr->attnum,
    8124             :                                       attr->atttypid,
    8125             :                                       attr->atttypmod,
    8126             :                                       attr->attcollation,
    8127             :                                       0);
    8128        1246 :     nnulltest->nulltesttype = IS_NOT_NULL;
    8129             : 
    8130             :     /*
    8131             :      * argisrow = false is correct even for a composite column, because
    8132             :      * attnotnull does not represent a SQL-spec IS NOT NULL test in such a
    8133             :      * case, just IS DISTINCT FROM NULL.
    8134             :      */
    8135        1246 :     nnulltest->argisrow = false;
    8136        1246 :     nnulltest->location = -1;
    8137             : 
    8138        1246 :     if (ConstraintImpliedByRelConstraint(rel, list_make1(nnulltest), NIL))
    8139             :     {
    8140          50 :         ereport(DEBUG1,
    8141             :                 (errmsg_internal("existing constraints on column \"%s.%s\" are sufficient to prove that it does not contain nulls",
    8142             :                                  RelationGetRelationName(rel), NameStr(attr->attname))));
    8143          50 :         return true;
    8144             :     }
    8145             : 
    8146        1196 :     return false;
    8147             : }
    8148             : 
    8149             : /*
    8150             :  * ALTER TABLE ALTER COLUMN SET/DROP DEFAULT
    8151             :  *
    8152             :  * Return the address of the affected column.
    8153             :  */
    8154             : static ObjectAddress
    8155         584 : ATExecColumnDefault(Relation rel, const char *colName,
    8156             :                     Node *newDefault, LOCKMODE lockmode)
    8157             : {
    8158         584 :     TupleDesc   tupdesc = RelationGetDescr(rel);
    8159             :     AttrNumber  attnum;
    8160             :     ObjectAddress address;
    8161             : 
    8162             :     /*
    8163             :      * get the number of the attribute
    8164             :      */
    8165         584 :     attnum = get_attnum(RelationGetRelid(rel), colName);
    8166         584 :     if (attnum == InvalidAttrNumber)
    8167          30 :         ereport(ERROR,
    8168             :                 (errcode(ERRCODE_UNDEFINED_COLUMN),
    8169             :                  errmsg("column \"%s\" of relation \"%s\" does not exist",
    8170             :                         colName, RelationGetRelationName(rel))));
    8171             : 
    8172             :     /* Prevent them from altering a system attribute */
    8173         554 :     if (attnum <= 0)
    8174           0 :         ereport(ERROR,
    8175             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    8176             :                  errmsg("cannot alter system column \"%s\"",
    8177             :                         colName)));
    8178             : 
    8179         554 :     if (TupleDescAttr(tupdesc, attnum - 1)->attidentity)
    8180          18 :         ereport(ERROR,
    8181             :                 (errcode(ERRCODE_SYNTAX_ERROR),
    8182             :                  errmsg("column \"%s\" of relation \"%s\" is an identity column",
    8183             :                         colName, RelationGetRelationName(rel)),
    8184             :         /* translator: %s is an SQL ALTER command */
    8185             :                  newDefault ? 0 : errhint("Use %s instead.",
    8186             :                                           "ALTER TABLE ... ALTER COLUMN ... DROP IDENTITY")));
    8187             : 
    8188         536 :     if (TupleDescAttr(tupdesc, attnum - 1)->attgenerated)
    8189          12 :         ereport(ERROR,
    8190             :                 (errcode(ERRCODE_SYNTAX_ERROR),
    8191             :                  errmsg("column \"%s\" of relation \"%s\" is a generated column",
    8192             :                         colName, RelationGetRelationName(rel)),
    8193             :                  newDefault ?
    8194             :         /* translator: %s is an SQL ALTER command */
    8195             :                  errhint("Use %s instead.", "ALTER TABLE ... ALTER COLUMN ... SET EXPRESSION") :
    8196             :                  (TupleDescAttr(tupdesc, attnum - 1)->attgenerated == ATTRIBUTE_GENERATED_STORED ?
    8197             :                   errhint("Use %s instead.", "ALTER TABLE ... ALTER COLUMN ... DROP EXPRESSION") : 0)));
    8198             : 
    8199             :     /*
    8200             :      * Remove any old default for the column.  We use RESTRICT here for
    8201             :      * safety, but at present we do not expect anything to depend on the
    8202             :      * default.
    8203             :      *
    8204             :      * We treat removing the existing default as an internal operation when it
    8205             :      * is preparatory to adding a new default, but as a user-initiated
    8206             :      * operation when the user asked for a drop.
    8207             :      */
    8208         524 :     RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT, false,
    8209             :                       newDefault != NULL);
    8210             : 
    8211         524 :     if (newDefault)
    8212             :     {
    8213             :         /* SET DEFAULT */
    8214             :         RawColumnDefault *rawEnt;
    8215             : 
    8216         350 :         rawEnt = palloc_object(RawColumnDefault);
    8217         350 :         rawEnt->attnum = attnum;
    8218         350 :         rawEnt->raw_default = newDefault;
    8219         350 :         rawEnt->generated = '\0';
    8220             : 
    8221             :         /*
    8222             :          * This function is intended for CREATE TABLE, so it processes a
    8223             :          * _list_ of defaults, but we just do one.
    8224             :          */
    8225         350 :         AddRelationNewConstraints(rel, list_make1(rawEnt), NIL,
    8226             :                                   false, true, false, NULL);
    8227             :     }
    8228             : 
    8229         518 :     ObjectAddressSubSet(address, RelationRelationId,
    8230             :                         RelationGetRelid(rel), attnum);
    8231         518 :     return address;
    8232             : }
    8233             : 
    8234             : /*
    8235             :  * Add a pre-cooked default expression.
    8236             :  *
    8237             :  * Return the address of the affected column.
    8238             :  */
    8239             : static ObjectAddress
    8240          80 : ATExecCookedColumnDefault(Relation rel, AttrNumber attnum,
    8241             :                           Node *newDefault)
    8242             : {
    8243             :     ObjectAddress address;
    8244             : 
    8245             :     /* We assume no checking is required */
    8246             : 
    8247             :     /*
    8248             :      * Remove any old default for the column.  We use RESTRICT here for
    8249             :      * safety, but at present we do not expect anything to depend on the
    8250             :      * default.  (In ordinary cases, there could not be a default in place
    8251             :      * anyway, but it's possible when combining LIKE with inheritance.)
    8252             :      */
    8253          80 :     RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT, false,
    8254             :                       true);
    8255             : 
    8256          80 :     (void) StoreAttrDefault(rel, attnum, newDefault, true);
    8257             : 
    8258          80 :     ObjectAddressSubSet(address, RelationRelationId,
    8259             :                         RelationGetRelid(rel), attnum);
    8260          80 :     return address;
    8261             : }
    8262             : 
    8263             : /*
    8264             :  * ALTER TABLE ALTER COLUMN ADD IDENTITY
    8265             :  *
    8266             :  * Return the address of the affected column.
    8267             :  */
    8268             : static ObjectAddress
    8269         166 : ATExecAddIdentity(Relation rel, const char *colName,
    8270             :                   Node *def, LOCKMODE lockmode, bool recurse, bool recursing)
    8271             : {
    8272             :     Relation    attrelation;
    8273             :     HeapTuple   tuple;
    8274             :     Form_pg_attribute attTup;
    8275             :     AttrNumber  attnum;
    8276             :     ObjectAddress address;
    8277         166 :     ColumnDef  *cdef = castNode(ColumnDef, def);
    8278             :     bool        ispartitioned;
    8279             : 
    8280         166 :     ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
    8281         166 :     if (ispartitioned && !recurse)
    8282           6 :         ereport(ERROR,
    8283             :                 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    8284             :                  errmsg("cannot add identity to a column of only the partitioned table"),
    8285             :                  errhint("Do not specify the ONLY keyword.")));
    8286             : 
    8287         160 :     if (rel->rd_rel->relispartition && !recursing)
    8288          12 :         ereport(ERROR,
    8289             :                 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    8290             :                 errmsg("cannot add identity to a column of a partition"));
    8291             : 
    8292         148 :     attrelation = table_open(AttributeRelationId, RowExclusiveLock);
    8293             : 
    8294         148 :     tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
    8295         148 :     if (!HeapTupleIsValid(tuple))
    8296           0 :         ereport(ERROR,
    8297             :                 (errcode(ERRCODE_UNDEFINED_COLUMN),
    8298             :                  errmsg("column \"%s\" of relation \"%s\" does not exist",
    8299             :                         colName, RelationGetRelationName(rel))));
    8300         148 :     attTup = (Form_pg_attribute) GETSTRUCT(tuple);
    8301         148 :     attnum = attTup->attnum;
    8302             : 
    8303             :     /* Can't alter a system attribute */
    8304         148 :     if (attnum <= 0)
    8305           0 :         ereport(ERROR,
    8306             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    8307             :                  errmsg("cannot alter system column \"%s\"",
    8308             :                         colName)));
    8309             : 
    8310             :     /*
    8311             :      * Creating a column as identity implies NOT NULL, so adding the identity
    8312             :      * to an existing column that is not NOT NULL would create a state that
    8313             :      * cannot be reproduced without contortions.
    8314             :      */
    8315         148 :     if (!attTup->attnotnull)
    8316           6 :         ereport(ERROR,
    8317             :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    8318             :                  errmsg("column \"%s\" of relation \"%s\" must be declared NOT NULL before identity can be added",
    8319             :                         colName, RelationGetRelationName(rel))));
    8320             : 
    8321             :     /*
    8322             :      * On the other hand, if a not-null constraint exists, then verify that
    8323             :      * it's compatible.
    8324             :      */
    8325         142 :     if (attTup->attnotnull)
    8326             :     {
    8327             :         HeapTuple   contup;
    8328             :         Form_pg_constraint conForm;
    8329             : 
    8330         142 :         contup = findNotNullConstraintAttnum(RelationGetRelid(rel),
    8331             :                                              attnum);
    8332         142 :         if (!HeapTupleIsValid(contup))
    8333           0 :             elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation \"%s\"",
    8334             :                  colName, RelationGetRelationName(rel));
    8335             : 
    8336         142 :         conForm = (Form_pg_constraint) GETSTRUCT(contup);
    8337         142 :         if (!conForm->convalidated)
    8338           6 :             ereport(ERROR,
    8339             :                     errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    8340             :                     errmsg("incompatible NOT VALID constraint \"%s\" on relation \"%s\"",
    8341             :                            NameStr(conForm->conname), RelationGetRelationName(rel)),
    8342             :                     errhint("You might need to validate it using %s.",
    8343             :                             "ALTER TABLE ... VALIDATE CONSTRAINT"));
    8344             :     }
    8345             : 
    8346         136 :     if (attTup->attidentity)
    8347          18 :         ereport(ERROR,
    8348             :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    8349             :                  errmsg("column \"%s\" of relation \"%s\" is already an identity column",
    8350             :                         colName, RelationGetRelationName(rel))));
    8351             : 
    8352         118 :     if (attTup->atthasdef)
    8353           6 :         ereport(ERROR,
    8354             :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    8355             :                  errmsg("column \"%s\" of relation \"%s\" already has a default value",
    8356             :                         colName, RelationGetRelationName(rel))));
    8357             : 
    8358         112 :     attTup->attidentity = cdef->identity;
    8359         112 :     CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
    8360             : 
    8361         112 :     InvokeObjectPostAlterHook(RelationRelationId,
    8362             :                               RelationGetRelid(rel),
    8363             :                               attTup->attnum);
    8364         112 :     ObjectAddressSubSet(address, RelationRelationId,
    8365             :                         RelationGetRelid(rel), attnum);
    8366         112 :     heap_freetuple(tuple);
    8367             : 
    8368         112 :     table_close(attrelation, RowExclusiveLock);
    8369             : 
    8370             :     /*
    8371             :      * Recurse to propagate the identity column to partitions.  Identity is
    8372             :      * not inherited in regular inheritance children.
    8373             :      */
    8374         112 :     if (recurse && ispartitioned)
    8375             :     {
    8376             :         List       *children;
    8377             :         ListCell   *lc;
    8378             : 
    8379          10 :         children = find_inheritance_children(RelationGetRelid(rel), lockmode);
    8380             : 
    8381          16 :         foreach(lc, children)
    8382             :         {
    8383             :             Relation    childrel;
    8384             : 
    8385           6 :             childrel = table_open(lfirst_oid(lc), NoLock);
    8386           6 :             ATExecAddIdentity(childrel, colName, def, lockmode, recurse, true);
    8387           6 :             table_close(childrel, NoLock);
    8388             :         }
    8389             :     }
    8390             : 
    8391         112 :     return address;
    8392             : }
    8393             : 
    8394             : /*
    8395             :  * ALTER TABLE ALTER COLUMN SET { GENERATED or sequence options }
    8396             :  *
    8397             :  * Return the address of the affected column.
    8398             :  */
    8399             : static ObjectAddress
    8400          74 : ATExecSetIdentity(Relation rel, const char *colName, Node *def,
    8401             :                   LOCKMODE lockmode, bool recurse, bool recursing)
    8402             : {
    8403             :     ListCell   *option;
    8404          74 :     DefElem    *generatedEl = NULL;
    8405             :     HeapTuple   tuple;
    8406             :     Form_pg_attribute attTup;
    8407             :     AttrNumber  attnum;
    8408             :     Relation    attrelation;
    8409             :     ObjectAddress address;
    8410             :     bool        ispartitioned;
    8411             : 
    8412          74 :     ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
    8413          74 :     if (ispartitioned && !recurse)
    8414           6 :         ereport(ERROR,
    8415             :                 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    8416             :                  errmsg("cannot change identity column of only the partitioned table"),
    8417             :                  errhint("Do not specify the ONLY keyword.")));
    8418             : 
    8419          68 :     if (rel->rd_rel->relispartition && !recursing)
    8420          12 :         ereport(ERROR,
    8421             :                 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    8422             :                 errmsg("cannot change identity column of a partition"));
    8423             : 
    8424         100 :     foreach(option, castNode(List, def))
    8425             :     {
    8426          44 :         DefElem    *defel = lfirst_node(DefElem, option);
    8427             : 
    8428          44 :         if (strcmp(defel->defname, "generated") == 0)
    8429             :         {
    8430          44 :             if (generatedEl)
    8431           0 :                 ereport(ERROR,
    8432             :                         (errcode(ERRCODE_SYNTAX_ERROR),
    8433             :                          errmsg("conflicting or redundant options")));
    8434          44 :             generatedEl = defel;
    8435             :         }
    8436             :         else
    8437           0 :             elog(ERROR, "option \"%s\" not recognized",
    8438             :                  defel->defname);
    8439             :     }
    8440             : 
    8441             :     /*
    8442             :      * Even if there is nothing to change here, we run all the checks.  There
    8443             :      * will be a subsequent ALTER SEQUENCE that relies on everything being
    8444             :      * there.
    8445             :      */
    8446             : 
    8447          56 :     attrelation = table_open(AttributeRelationId, RowExclusiveLock);
    8448          56 :     tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
    8449          56 :     if (!HeapTupleIsValid(tuple))
    8450           0 :         ereport(ERROR,
    8451             :                 (errcode(ERRCODE_UNDEFINED_COLUMN),
    8452             :                  errmsg("column \"%s\" of relation \"%s\" does not exist",
    8453             :                         colName, RelationGetRelationName(rel))));
    8454             : 
    8455          56 :     attTup = (Form_pg_attribute) GETSTRUCT(tuple);
    8456          56 :     attnum = attTup->attnum;
    8457             : 
    8458          56 :     if (attnum <= 0)
    8459           0 :         ereport(ERROR,
    8460             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    8461             :                  errmsg("cannot alter system column \"%s\"",
    8462             :                         colName)));
    8463             : 
    8464          56 :     if (!attTup->attidentity)
    8465           6 :         ereport(ERROR,
    8466             :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    8467             :                  errmsg("column \"%s\" of relation \"%s\" is not an identity column",
    8468             :                         colName, RelationGetRelationName(rel))));
    8469             : 
    8470          50 :     if (generatedEl)
    8471             :     {
    8472          44 :         attTup->attidentity = defGetInt32(generatedEl);
    8473          44 :         CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
    8474             : 
    8475          44 :         InvokeObjectPostAlterHook(RelationRelationId,
    8476             :                                   RelationGetRelid(rel),
    8477             :                                   attTup->attnum);
    8478          44 :         ObjectAddressSubSet(address, RelationRelationId,
    8479             :                             RelationGetRelid(rel), attnum);
    8480             :     }
    8481             :     else
    8482           6 :         address = InvalidObjectAddress;
    8483             : 
    8484          50 :     heap_freetuple(tuple);
    8485          50 :     table_close(attrelation, RowExclusiveLock);
    8486             : 
    8487             :     /*
    8488             :      * Recurse to propagate the identity change to partitions. Identity is not
    8489             :      * inherited in regular inheritance children.
    8490             :      */
    8491          50 :     if (generatedEl && recurse && ispartitioned)
    8492             :     {
    8493             :         List       *children;
    8494             :         ListCell   *lc;
    8495             : 
    8496           6 :         children = find_inheritance_children(RelationGetRelid(rel), lockmode);
    8497             : 
    8498          18 :         foreach(lc, children)
    8499             :         {
    8500             :             Relation    childrel;
    8501             : 
    8502          12 :             childrel = table_open(lfirst_oid(lc), NoLock);
    8503          12 :             ATExecSetIdentity(childrel, colName, def, lockmode, recurse, true);
    8504          12 :             table_close(childrel, NoLock);
    8505             :         }
    8506             :     }
    8507             : 
    8508          50 :     return address;
    8509             : }
    8510             : 
    8511             : /*
    8512             :  * ALTER TABLE ALTER COLUMN DROP IDENTITY
    8513             :  *
    8514             :  * Return the address of the affected column.
    8515             :  */
    8516             : static ObjectAddress
    8517          92 : ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode,
    8518             :                    bool recurse, bool recursing)
    8519             : {
    8520             :     HeapTuple   tuple;
    8521             :     Form_pg_attribute attTup;
    8522             :     AttrNumber  attnum;
    8523             :     Relation    attrelation;
    8524             :     ObjectAddress address;
    8525             :     Oid         seqid;
    8526             :     ObjectAddress seqaddress;
    8527             :     bool        ispartitioned;
    8528             : 
    8529          92 :     ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
    8530          92 :     if (ispartitioned && !recurse)
    8531           6 :         ereport(ERROR,
    8532             :                 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    8533             :                  errmsg("cannot drop identity from a column of only the partitioned table"),
    8534             :                  errhint("Do not specify the ONLY keyword.")));
    8535             : 
    8536          86 :     if (rel->rd_rel->relispartition && !recursing)
    8537           6 :         ereport(ERROR,
    8538             :                 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    8539             :                 errmsg("cannot drop identity from a column of a partition"));
    8540             : 
    8541          80 :     attrelation = table_open(AttributeRelationId, RowExclusiveLock);
    8542          80 :     tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
    8543          80 :     if (!HeapTupleIsValid(tuple))
    8544           0 :         ereport(ERROR,
    8545             :                 (errcode(ERRCODE_UNDEFINED_COLUMN),
    8546             :                  errmsg("column \"%s\" of relation \"%s\" does not exist",
    8547             :                         colName, RelationGetRelationName(rel))));
    8548             : 
    8549          80 :     attTup = (Form_pg_attribute) GETSTRUCT(tuple);
    8550          80 :     attnum = attTup->attnum;
    8551             : 
    8552          80 :     if (attnum <= 0)
    8553           0 :         ereport(ERROR,
    8554             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    8555             :                  errmsg("cannot alter system column \"%s\"",
    8556             :                         colName)));
    8557             : 
    8558          80 :     if (!attTup->attidentity)
    8559             :     {
    8560          12 :         if (!missing_ok)
    8561           6 :             ereport(ERROR,
    8562             :                     (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    8563             :                      errmsg("column \"%s\" of relation \"%s\" is not an identity column",
    8564             :                             colName, RelationGetRelationName(rel))));
    8565             :         else
    8566             :         {
    8567           6 :             ereport(NOTICE,
    8568             :                     (errmsg("column \"%s\" of relation \"%s\" is not an identity column, skipping",
    8569             :                             colName, RelationGetRelationName(rel))));
    8570           6 :             heap_freetuple(tuple);
    8571           6 :             table_close(attrelation, RowExclusiveLock);
    8572           6 :             return InvalidObjectAddress;
    8573             :         }
    8574             :     }
    8575             : 
    8576          68 :     attTup->attidentity = '\0';
    8577          68 :     CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
    8578             : 
    8579          68 :     InvokeObjectPostAlterHook(RelationRelationId,
    8580             :                               RelationGetRelid(rel),
    8581             :                               attTup->attnum);
    8582          68 :     ObjectAddressSubSet(address, RelationRelationId,
    8583             :                         RelationGetRelid(rel), attnum);
    8584          68 :     heap_freetuple(tuple);
    8585             : 
    8586          68 :     table_close(attrelation, RowExclusiveLock);
    8587             : 
    8588             :     /*
    8589             :      * Recurse to drop the identity from column in partitions.  Identity is
    8590             :      * not inherited in regular inheritance children so ignore them.
    8591             :      */
    8592          68 :     if (recurse && ispartitioned)
    8593             :     {
    8594             :         List       *children;
    8595             :         ListCell   *lc;
    8596             : 
    8597           6 :         children = find_inheritance_children(RelationGetRelid(rel), lockmode);
    8598             : 
    8599          12 :         foreach(lc, children)
    8600             :         {
    8601             :             Relation    childrel;
    8602             : 
    8603           6 :             childrel = table_open(lfirst_oid(lc), NoLock);
    8604           6 :             ATExecDropIdentity(childrel, colName, false, lockmode, recurse, true);
    8605           6 :             table_close(childrel, NoLock);
    8606             :         }
    8607             :     }
    8608             : 
    8609          68 :     if (!recursing)
    8610             :     {
    8611             :         /* drop the internal sequence */
    8612          32 :         seqid = getIdentitySequence(rel, attnum, false);
    8613          32 :         deleteDependencyRecordsForClass(RelationRelationId, seqid,
    8614             :                                         RelationRelationId, DEPENDENCY_INTERNAL);
    8615          32 :         CommandCounterIncrement();
    8616          32 :         seqaddress.classId = RelationRelationId;
    8617          32 :         seqaddress.objectId = seqid;
    8618          32 :         seqaddress.objectSubId = 0;
    8619          32 :         performDeletion(&seqaddress, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
    8620             :     }
    8621             : 
    8622          68 :     return address;
    8623             : }
    8624             : 
    8625             : /*
    8626             :  * ALTER TABLE ALTER COLUMN SET EXPRESSION
    8627             :  *
    8628             :  * Return the address of the affected column.
    8629             :  */
    8630             : static ObjectAddress
    8631         224 : ATExecSetExpression(AlteredTableInfo *tab, Relation rel, const char *colName,
    8632             :                     Node *newExpr, LOCKMODE lockmode)
    8633             : {
    8634             :     HeapTuple   tuple;
    8635             :     Form_pg_attribute attTup;
    8636             :     AttrNumber  attnum;
    8637             :     char        attgenerated;
    8638             :     bool        rewrite;
    8639             :     Oid         attrdefoid;
    8640             :     ObjectAddress address;
    8641             :     Expr       *defval;
    8642             :     NewColumnValue *newval;
    8643             :     RawColumnDefault *rawEnt;
    8644             : 
    8645         224 :     tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
    8646         224 :     if (!HeapTupleIsValid(tuple))
    8647           0 :         ereport(ERROR,
    8648             :                 (errcode(ERRCODE_UNDEFINED_COLUMN),
    8649             :                  errmsg("column \"%s\" of relation \"%s\" does not exist",
    8650             :                         colName, RelationGetRelationName(rel))));
    8651             : 
    8652         224 :     attTup = (Form_pg_attribute) GETSTRUCT(tuple);
    8653             : 
    8654         224 :     attnum = attTup->attnum;
    8655         224 :     if (attnum <= 0)
    8656           0 :         ereport(ERROR,
    8657             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    8658             :                  errmsg("cannot alter system column \"%s\"",
    8659             :                         colName)));
    8660             : 
    8661         224 :     attgenerated = attTup->attgenerated;
    8662         224 :     if (!attgenerated)
    8663          12 :         ereport(ERROR,
    8664             :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    8665             :                  errmsg("column \"%s\" of relation \"%s\" is not a generated column",
    8666             :                         colName, RelationGetRelationName(rel))));
    8667             : 
    8668             :     /*
    8669             :      * TODO: This could be done, just need to recheck any constraints
    8670             :      * afterwards.
    8671             :      */
    8672         212 :     if (attgenerated == ATTRIBUTE_GENERATED_VIRTUAL &&
    8673         108 :         rel->rd_att->constr && rel->rd_att->constr->num_check > 0)
    8674          12 :         ereport(ERROR,
    8675             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    8676             :                  errmsg("ALTER TABLE / SET EXPRESSION is not supported for virtual generated columns in tables with check constraints"),
    8677             :                  errdetail("Column \"%s\" of relation \"%s\" is a virtual generated column.",
    8678             :                            colName, RelationGetRelationName(rel))));
    8679             : 
    8680         200 :     if (attgenerated == ATTRIBUTE_GENERATED_VIRTUAL && attTup->attnotnull)
    8681          24 :         tab->verify_new_notnull = true;
    8682             : 
    8683             :     /*
    8684             :      * We need to prevent this because a change of expression could affect a
    8685             :      * row filter and inject expressions that are not permitted in a row
    8686             :      * filter.  XXX We could try to have a more precise check to catch only
    8687             :      * publications with row filters, or even re-verify the row filter
    8688             :      * expressions.
    8689             :      */
    8690         296 :     if (attgenerated == ATTRIBUTE_GENERATED_VIRTUAL &&
    8691          96 :         GetRelationPublications(RelationGetRelid(rel)) != NIL)
    8692           6 :         ereport(ERROR,
    8693             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    8694             :                  errmsg("ALTER TABLE / SET EXPRESSION is not supported for virtual generated columns in tables that are part of a publication"),
    8695             :                  errdetail("Column \"%s\" of relation \"%s\" is a virtual generated column.",
    8696             :                            colName, RelationGetRelationName(rel))));
    8697             : 
    8698         194 :     rewrite = (attgenerated == ATTRIBUTE_GENERATED_STORED);
    8699             : 
    8700         194 :     ReleaseSysCache(tuple);
    8701             : 
    8702         194 :     if (rewrite)
    8703             :     {
    8704             :         /*
    8705             :          * Clear all the missing values if we're rewriting the table, since
    8706             :          * this renders them pointless.
    8707             :          */
    8708         104 :         RelationClearMissing(rel);
    8709             : 
    8710             :         /* make sure we don't conflict with later attribute modifications */
    8711         104 :         CommandCounterIncrement();
    8712             : 
    8713             :         /*
    8714             :          * Find everything that depends on the column (constraints, indexes,
    8715             :          * etc), and record enough information to let us recreate the objects
    8716             :          * after rewrite.
    8717             :          */
    8718         104 :         RememberAllDependentForRebuilding(tab, AT_SetExpression, rel, attnum, colName);
    8719             :     }
    8720             : 
    8721             :     /*
    8722             :      * Drop the dependency records of the GENERATED expression, in particular
    8723             :      * its INTERNAL dependency on the column, which would otherwise cause
    8724             :      * dependency.c to refuse to perform the deletion.
    8725             :      */
    8726         194 :     attrdefoid = GetAttrDefaultOid(RelationGetRelid(rel), attnum);
    8727         194 :     if (!OidIsValid(attrdefoid))
    8728           0 :         elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
    8729             :              RelationGetRelid(rel), attnum);
    8730         194 :     (void) deleteDependencyRecordsFor(AttrDefaultRelationId, attrdefoid, false);
    8731             : 
    8732             :     /* Make above changes visible */
    8733         194 :     CommandCounterIncrement();
    8734             : 
    8735             :     /*
    8736             :      * Get rid of the GENERATED expression itself.  We use RESTRICT here for
    8737             :      * safety, but at present we do not expect anything to depend on the
    8738             :      * expression.
    8739             :      */
    8740         194 :     RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT,
    8741             :                       false, false);
    8742             : 
    8743             :     /* Prepare to store the new expression, in the catalogs */
    8744         194 :     rawEnt = palloc_object(RawColumnDefault);
    8745         194 :     rawEnt->attnum = attnum;
    8746         194 :     rawEnt->raw_default = newExpr;
    8747         194 :     rawEnt->generated = attgenerated;
    8748             : 
    8749             :     /* Store the generated expression */
    8750         194 :     AddRelationNewConstraints(rel, list_make1(rawEnt), NIL,
    8751             :                               false, true, false, NULL);
    8752             : 
    8753             :     /* Make above new expression visible */
    8754         194 :     CommandCounterIncrement();
    8755             : 
    8756         194 :     if (rewrite)
    8757             :     {
    8758             :         /* Prepare for table rewrite */
    8759         104 :         defval = (Expr *) build_column_default(rel, attnum);
    8760             : 
    8761         104 :         newval = palloc0_object(NewColumnValue);
    8762         104 :         newval->attnum = attnum;
    8763         104 :         newval->expr = expression_planner(defval);
    8764         104 :         newval->is_generated = true;
    8765             : 
    8766         104 :         tab->newvals = lappend(tab->newvals, newval);
    8767         104 :         tab->rewrite |= AT_REWRITE_DEFAULT_VAL;
    8768             :     }
    8769             : 
    8770             :     /* Drop any pg_statistic entry for the column */
    8771         194 :     RemoveStatistics(RelationGetRelid(rel), attnum);
    8772             : 
    8773         194 :     InvokeObjectPostAlterHook(RelationRelationId,
    8774             :                               RelationGetRelid(rel), attnum);
    8775             : 
    8776         194 :     ObjectAddressSubSet(address, RelationRelationId,
    8777             :                         RelationGetRelid(rel), attnum);
    8778         194 :     return address;
    8779             : }
    8780             : 
    8781             : /*
    8782             :  * ALTER TABLE ALTER COLUMN DROP EXPRESSION
    8783             :  */
    8784             : static void
    8785          86 : ATPrepDropExpression(Relation rel, AlterTableCmd *cmd, bool recurse, bool recursing, LOCKMODE lockmode)
    8786             : {
    8787             :     /*
    8788             :      * Reject ONLY if there are child tables.  We could implement this, but it
    8789             :      * is a bit complicated.  GENERATED clauses must be attached to the column
    8790             :      * definition and cannot be added later like DEFAULT, so if a child table
    8791             :      * has a generation expression that the parent does not have, the child
    8792             :      * column will necessarily be an attislocal column.  So to implement ONLY
    8793             :      * here, we'd need extra code to update attislocal of the direct child
    8794             :      * tables, somewhat similar to how DROP COLUMN does it, so that the
    8795             :      * resulting state can be properly dumped and restored.
    8796             :      */
    8797         110 :     if (!recurse &&
    8798          24 :         find_inheritance_children(RelationGetRelid(rel), lockmode))
    8799          12 :         ereport(ERROR,
    8800             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    8801             :                  errmsg("ALTER TABLE / DROP EXPRESSION must be applied to child tables too")));
    8802             : 
    8803             :     /*
    8804             :      * Cannot drop generation expression from inherited columns.
    8805             :      */
    8806          74 :     if (!recursing)
    8807             :     {
    8808             :         HeapTuple   tuple;
    8809             :         Form_pg_attribute attTup;
    8810             : 
    8811          62 :         tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), cmd->name);
    8812          62 :         if (!HeapTupleIsValid(tuple))
    8813           0 :             ereport(ERROR,
    8814             :                     (errcode(ERRCODE_UNDEFINED_COLUMN),
    8815             :                      errmsg("column \"%s\" of relation \"%s\" does not exist",
    8816             :                             cmd->name, RelationGetRelationName(rel))));
    8817             : 
    8818          62 :         attTup = (Form_pg_attribute) GETSTRUCT(tuple);
    8819             : 
    8820          62 :         if (attTup->attinhcount > 0)
    8821          12 :             ereport(ERROR,
    8822             :                     (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    8823             :                      errmsg("cannot drop generation expression from inherited column")));
    8824             :     }
    8825          62 : }
    8826             : 
    8827             : /*
    8828             :  * Return the address of the affected column.
    8829             :  */
    8830             : static ObjectAddress
    8831          56 : ATExecDropExpression(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode)
    8832             : {
    8833             :     HeapTuple   tuple;
    8834             :     Form_pg_attribute attTup;
    8835             :     AttrNumber  attnum;
    8836             :     Relation    attrelation;
    8837             :     Oid         attrdefoid;
    8838             :     ObjectAddress address;
    8839             : 
    8840          56 :     attrelation = table_open(AttributeRelationId, RowExclusiveLock);
    8841          56 :     tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
    8842          56 :     if (!HeapTupleIsValid(tuple))
    8843           0 :         ereport(ERROR,
    8844             :                 (errcode(ERRCODE_UNDEFINED_COLUMN),
    8845             :                  errmsg("column \"%s\" of relation \"%s\" does not exist",
    8846             :                         colName, RelationGetRelationName(rel))));
    8847             : 
    8848          56 :     attTup = (Form_pg_attribute) GETSTRUCT(tuple);
    8849          56 :     attnum = attTup->attnum;
    8850             : 
    8851          56 :     if (attnum <= 0)
    8852           0 :         ereport(ERROR,
    8853             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    8854             :                  errmsg("cannot alter system column \"%s\"",
    8855             :                         colName)));
    8856             : 
    8857             :     /*
    8858             :      * TODO: This could be done, but it would need a table rewrite to
    8859             :      * materialize the generated values.  Note that for the time being, we
    8860             :      * still error with missing_ok, so that we don't silently leave the column
    8861             :      * as generated.
    8862             :      */
    8863          56 :     if (attTup->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
    8864          12 :         ereport(ERROR,
    8865             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    8866             :                  errmsg("ALTER TABLE / DROP EXPRESSION is not supported for virtual generated columns"),
    8867             :                  errdetail("Column \"%s\" of relation \"%s\" is a virtual generated column.",
    8868             :                            colName, RelationGetRelationName(rel))));
    8869             : 
    8870          44 :     if (!attTup->attgenerated)
    8871             :     {
    8872          24 :         if (!missing_ok)
    8873          12 :             ereport(ERROR,
    8874             :                     (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    8875             :                      errmsg("column \"%s\" of relation \"%s\" is not a generated column",
    8876             :                             colName, RelationGetRelationName(rel))));
    8877             :         else
    8878             :         {
    8879          12 :             ereport(NOTICE,
    8880             :                     (errmsg("column \"%s\" of relation \"%s\" is not a generated column, skipping",
    8881             :                             colName, RelationGetRelationName(rel))));
    8882          12 :             heap_freetuple(tuple);
    8883          12 :             table_close(attrelation, RowExclusiveLock);
    8884          12 :             return InvalidObjectAddress;
    8885             :         }
    8886             :     }
    8887             : 
    8888             :     /*
    8889             :      * Mark the column as no longer generated.  (The atthasdef flag needs to
    8890             :      * get cleared too, but RemoveAttrDefault will handle that.)
    8891             :      */
    8892          20 :     attTup->attgenerated = '\0';
    8893          20 :     CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
    8894             : 
    8895          20 :     InvokeObjectPostAlterHook(RelationRelationId,
    8896             :                               RelationGetRelid(rel),
    8897             :                               attnum);
    8898          20 :     heap_freetuple(tuple);
    8899             : 
    8900          20 :     table_close(attrelation, RowExclusiveLock);
    8901             : 
    8902             :     /*
    8903             :      * Drop the dependency records of the GENERATED expression, in particular
    8904             :      * its INTERNAL dependency on the column, which would otherwise cause
    8905             :      * dependency.c to refuse to perform the deletion.
    8906             :      */
    8907          20 :     attrdefoid = GetAttrDefaultOid(RelationGetRelid(rel), attnum);
    8908          20 :     if (!OidIsValid(attrdefoid))
    8909           0 :         elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
    8910             :              RelationGetRelid(rel), attnum);
    8911          20 :     (void) deleteDependencyRecordsFor(AttrDefaultRelationId, attrdefoid, false);
    8912             : 
    8913             :     /* Make above changes visible */
    8914          20 :     CommandCounterIncrement();
    8915             : 
    8916             :     /*
    8917             :      * Get rid of the GENERATED expression itself.  We use RESTRICT here for
    8918             :      * safety, but at present we do not expect anything to depend on the
    8919             :      * default.
    8920             :      */
    8921          20 :     RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT,
    8922             :                       false, false);
    8923             : 
    8924          20 :     ObjectAddressSubSet(address, RelationRelationId,
    8925             :                         RelationGetRelid(rel), attnum);
    8926          20 :     return address;
    8927             : }
    8928             : 
    8929             : /*
    8930             :  * ALTER TABLE ALTER COLUMN SET STATISTICS
    8931             :  *
    8932             :  * Return value is the address of the modified column
    8933             :  */
    8934             : static ObjectAddress
    8935         164 : ATExecSetStatistics(Relation rel, const char *colName, int16 colNum, Node *newValue, LOCKMODE lockmode)
    8936             : {
    8937         164 :     int         newtarget = 0;
    8938             :     bool        newtarget_default;
    8939             :     Relation    attrelation;
    8940             :     HeapTuple   tuple,
    8941             :                 newtuple;
    8942             :     Form_pg_attribute attrtuple;
    8943             :     AttrNumber  attnum;
    8944             :     ObjectAddress address;
    8945             :     Datum       repl_val[Natts_pg_attribute];
    8946             :     bool        repl_null[Natts_pg_attribute];
    8947             :     bool        repl_repl[Natts_pg_attribute];
    8948             : 
    8949             :     /*
    8950             :      * We allow referencing columns by numbers only for indexes, since table
    8951             :      * column numbers could contain gaps if columns are later dropped.
    8952             :      */
    8953         164 :     if (rel->rd_rel->relkind != RELKIND_INDEX &&
    8954         100 :         rel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX &&
    8955             :         !colName)
    8956           0 :         ereport(ERROR,
    8957             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    8958             :                  errmsg("cannot refer to non-index column by number")));
    8959             : 
    8960             :     /* -1 was used in previous versions for the default setting */
    8961         164 :     if (newValue && intVal(newValue) != -1)
    8962             :     {
    8963         120 :         newtarget = intVal(newValue);
    8964         120 :         newtarget_default = false;
    8965             :     }
    8966             :     else
    8967          44 :         newtarget_default = true;
    8968             : 
    8969         164 :     if (!newtarget_default)
    8970             :     {
    8971             :         /*
    8972             :          * Limit target to a sane range
    8973             :          */
    8974         120 :         if (newtarget < 0)
    8975             :         {
    8976           0 :             ereport(ERROR,
    8977             :                     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    8978             :                      errmsg("statistics target %d is too low",
    8979             :                             newtarget)));
    8980             :         }
    8981         120 :         else if (newtarget > MAX_STATISTICS_TARGET)
    8982             :         {
    8983           0 :             newtarget = MAX_STATISTICS_TARGET;
    8984           0 :             ereport(WARNING,
    8985             :                     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    8986             :                      errmsg("lowering statistics target to %d",
    8987             :                             newtarget)));
    8988             :         }
    8989             :     }
    8990             : 
    8991         164 :     attrelation = table_open(AttributeRelationId, RowExclusiveLock);
    8992             : 
    8993         164 :     if (colName)
    8994             :     {
    8995         100 :         tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
    8996             : 
    8997         100 :         if (!HeapTupleIsValid(tuple))
    8998          12 :             ereport(ERROR,
    8999             :                     (errcode(ERRCODE_UNDEFINED_COLUMN),
    9000             :                      errmsg("column \"%s\" of relation \"%s\" does not exist",
    9001             :                             colName, RelationGetRelationName(rel))));
    9002             :     }
    9003             :     else
    9004             :     {
    9005          64 :         tuple = SearchSysCacheAttNum(RelationGetRelid(rel), colNum);
    9006             : 
    9007          64 :         if (!HeapTupleIsValid(tuple))
    9008          12 :             ereport(ERROR,
    9009             :                     (errcode(ERRCODE_UNDEFINED_COLUMN),
    9010             :                      errmsg("column number %d of relation \"%s\" does not exist",
    9011             :                             colNum, RelationGetRelationName(rel))));
    9012             :     }
    9013             : 
    9014         140 :     attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
    9015             : 
    9016         140 :     attnum = attrtuple->attnum;
    9017         140 :     if (attnum <= 0)
    9018           0 :         ereport(ERROR,
    9019             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    9020             :                  errmsg("cannot alter system column \"%s\"",
    9021             :                         colName)));
    9022             : 
    9023             :     /*
    9024             :      * Prevent this as long as the ANALYZE code skips virtual generated
    9025             :      * columns.
    9026             :      */
    9027         140 :     if (attrtuple->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
    9028           0 :         ereport(ERROR,
    9029             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    9030             :                  errmsg("cannot alter statistics on virtual generated column \"%s\"",
    9031             :                         colName)));
    9032             : 
    9033         140 :     if (rel->rd_rel->relkind == RELKIND_INDEX ||
    9034          88 :         rel->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
    9035             :     {
    9036          52 :         if (attnum > rel->rd_index->indnkeyatts)
    9037           6 :             ereport(ERROR,
    9038             :                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    9039             :                      errmsg("cannot alter statistics on included column \"%s\" of index \"%s\"",
    9040             :                             NameStr(attrtuple->attname), RelationGetRelationName(rel))));
    9041          46 :         else if (rel->rd_index->indkey.values[attnum - 1] != 0)
    9042          18 :             ereport(ERROR,
    9043             :                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    9044             :                      errmsg("cannot alter statistics on non-expression column \"%s\" of index \"%s\"",
    9045             :                             NameStr(attrtuple->attname), RelationGetRelationName(rel)),
    9046             :                      errhint("Alter statistics on table column instead.")));
    9047             :     }
    9048             : 
    9049             :     /* Build new tuple. */
    9050         116 :     memset(repl_null, false, sizeof(repl_null));
    9051         116 :     memset(repl_repl, false, sizeof(repl_repl));
    9052         116 :     if (!newtarget_default)
    9053          72 :         repl_val[Anum_pg_attribute_attstattarget - 1] = Int16GetDatum(newtarget);
    9054             :     else
    9055          44 :         repl_null[Anum_pg_attribute_attstattarget - 1] = true;
    9056         116 :     repl_repl[Anum_pg_attribute_attstattarget - 1] = true;
    9057         116 :     newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrelation),
    9058             :                                  repl_val, repl_null, repl_repl);
    9059         116 :     CatalogTupleUpdate(attrelation, &tuple->t_self, newtuple);
    9060             : 
    9061         116 :     InvokeObjectPostAlterHook(RelationRelationId,
    9062             :                               RelationGetRelid(rel),
    9063             :                               attrtuple->attnum);
    9064         116 :     ObjectAddressSubSet(address, RelationRelationId,
    9065             :                         RelationGetRelid(rel), attnum);
    9066             : 
    9067         116 :     heap_freetuple(newtuple);
    9068             : 
    9069         116 :     ReleaseSysCache(tuple);
    9070             : 
    9071         116 :     table_close(attrelation, RowExclusiveLock);
    9072             : 
    9073         116 :     return address;
    9074             : }
    9075             : 
    9076             : /*
    9077             :  * Return value is the address of the modified column
    9078             :  */
    9079             : static ObjectAddress
    9080          32 : ATExecSetOptions(Relation rel, const char *colName, Node *options,
    9081             :                  bool isReset, LOCKMODE lockmode)
    9082             : {
    9083             :     Relation    attrelation;
    9084             :     HeapTuple   tuple,
    9085             :                 newtuple;
    9086             :     Form_pg_attribute attrtuple;
    9087             :     AttrNumber  attnum;
    9088             :     Datum       datum,
    9089             :                 newOptions;
    9090             :     bool        isnull;
    9091             :     ObjectAddress address;
    9092             :     Datum       repl_val[Natts_pg_attribute];
    9093             :     bool        repl_null[Natts_pg_attribute];
    9094             :     bool        repl_repl[Natts_pg_attribute];
    9095             : 
    9096          32 :     attrelation = table_open(AttributeRelationId, RowExclusiveLock);
    9097             : 
    9098          32 :     tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
    9099             : 
    9100          32 :     if (!HeapTupleIsValid(tuple))
    9101           0 :         ereport(ERROR,
    9102             :                 (errcode(ERRCODE_UNDEFINED_COLUMN),
    9103             :                  errmsg("column \"%s\" of relation \"%s\" does not exist",
    9104             :                         colName, RelationGetRelationName(rel))));
    9105          32 :     attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
    9106             : 
    9107          32 :     attnum = attrtuple->attnum;
    9108          32 :     if (attnum <= 0)
    9109           0 :         ereport(ERROR,
    9110             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    9111             :                  errmsg("cannot alter system column \"%s\"",
    9112             :                         colName)));
    9113             : 
    9114             :     /* Generate new proposed attoptions (text array) */
    9115          32 :     datum = SysCacheGetAttr(ATTNAME, tuple, Anum_pg_attribute_attoptions,
    9116             :                             &isnull);
    9117          32 :     newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
    9118             :                                      castNode(List, options), NULL, NULL,
    9119             :                                      false, isReset);
    9120             :     /* Validate new options */
    9121          32 :     (void) attribute_reloptions(newOptions, true);
    9122             : 
    9123             :     /* Build new tuple. */
    9124          32 :     memset(repl_null, false, sizeof(repl_null));
    9125          32 :     memset(repl_repl, false, sizeof(repl_repl));
    9126          32 :     if (newOptions != (Datum) 0)
    9127          32 :         repl_val[Anum_pg_attribute_attoptions - 1] = newOptions;
    9128             :     else
    9129           0 :         repl_null[Anum_pg_attribute_attoptions - 1] = true;
    9130          32 :     repl_repl[Anum_pg_attribute_attoptions - 1] = true;
    9131          32 :     newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrelation),
    9132             :                                  repl_val, repl_null, repl_repl);
    9133             : 
    9134             :     /* Update system catalog. */
    9135          32 :     CatalogTupleUpdate(attrelation, &newtuple->t_self, newtuple);
    9136             : 
    9137          32 :     InvokeObjectPostAlterHook(RelationRelationId,
    9138             :                               RelationGetRelid(rel),
    9139             :                               attrtuple->attnum);
    9140          32 :     ObjectAddressSubSet(address, RelationRelationId,
    9141             :                         RelationGetRelid(rel), attnum);
    9142             : 
    9143          32 :     heap_freetuple(newtuple);
    9144             : 
    9145          32 :     ReleaseSysCache(tuple);
    9146             : 
    9147          32 :     table_close(attrelation, RowExclusiveLock);
    9148             : 
    9149          32 :     return address;
    9150             : }
    9151             : 
    9152             : /*
    9153             :  * Helper function for ATExecSetStorage and ATExecSetCompression
    9154             :  *
    9155             :  * Set the attstorage and/or attcompression fields for index columns
    9156             :  * associated with the specified table column.
    9157             :  */
    9158             : static void
    9159         320 : SetIndexStorageProperties(Relation rel, Relation attrelation,
    9160             :                           AttrNumber attnum,
    9161             :                           bool setstorage, char newstorage,
    9162             :                           bool setcompression, char newcompression,
    9163             :                           LOCKMODE lockmode)
    9164             : {
    9165             :     ListCell   *lc;
    9166             : 
    9167         412 :     foreach(lc, RelationGetIndexList(rel))
    9168             :     {
    9169          92 :         Oid         indexoid = lfirst_oid(lc);
    9170             :         Relation    indrel;
    9171          92 :         AttrNumber  indattnum = 0;
    9172             :         HeapTuple   tuple;
    9173             : 
    9174          92 :         indrel = index_open(indexoid, lockmode);
    9175             : 
    9176         154 :         for (int i = 0; i < indrel->rd_index->indnatts; i++)
    9177             :         {
    9178          98 :             if (indrel->rd_index->indkey.values[i] == attnum)
    9179             :             {
    9180          36 :                 indattnum = i + 1;
    9181          36 :                 break;
    9182             :             }
    9183             :         }
    9184             : 
    9185          92 :         if (indattnum == 0)
    9186             :         {
    9187          56 :             index_close(indrel, lockmode);
    9188          56 :             continue;
    9189             :         }
    9190             : 
    9191          36 :         tuple = SearchSysCacheCopyAttNum(RelationGetRelid(indrel), indattnum);
    9192             : 
    9193          36 :         if (HeapTupleIsValid(tuple))
    9194             :         {
    9195          36 :             Form_pg_attribute attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
    9196             : 
    9197          36 :             if (setstorage)
    9198          24 :                 attrtuple->attstorage = newstorage;
    9199             : 
    9200          36 :             if (setcompression)
    9201          12 :                 attrtuple->attcompression = newcompression;
    9202             : 
    9203          36 :             CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
    9204             : 
    9205          36 :             InvokeObjectPostAlterHook(RelationRelationId,
    9206             :                                       RelationGetRelid(rel),
    9207             :                                       attrtuple->attnum);
    9208             : 
    9209          36 :             heap_freetuple(tuple);
    9210             :         }
    9211             : 
    9212          36 :         index_close(indrel, lockmode);
    9213             :     }
    9214         320 : }
    9215             : 
    9216             : /*
    9217             :  * ALTER TABLE ALTER COLUMN SET STORAGE
    9218             :  *
    9219             :  * Return value is the address of the modified column
    9220             :  */
    9221             : static ObjectAddress
    9222         260 : ATExecSetStorage(Relation rel, const char *colName, Node *newValue, LOCKMODE lockmode)
    9223             : {
    9224             :     Relation    attrelation;
    9225             :     HeapTuple   tuple;
    9226             :     Form_pg_attribute attrtuple;
    9227             :     AttrNumber  attnum;
    9228             :     ObjectAddress address;
    9229             : 
    9230         260 :     attrelation = table_open(AttributeRelationId, RowExclusiveLock);
    9231             : 
    9232         260 :     tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
    9233             : 
    9234         260 :     if (!HeapTupleIsValid(tuple))
    9235          12 :         ereport(ERROR,
    9236             :                 (errcode(ERRCODE_UNDEFINED_COLUMN),
    9237             :                  errmsg("column \"%s\" of relation \"%s\" does not exist",
    9238             :                         colName, RelationGetRelationName(rel))));
    9239         248 :     attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
    9240             : 
    9241         248 :     attnum = attrtuple->attnum;
    9242         248 :     if (attnum <= 0)
    9243           0 :         ereport(ERROR,
    9244             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    9245             :                  errmsg("cannot alter system column \"%s\"",
    9246             :                         colName)));
    9247             : 
    9248         248 :     attrtuple->attstorage = GetAttributeStorage(attrtuple->atttypid, strVal(newValue));
    9249             : 
    9250         248 :     CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
    9251             : 
    9252         248 :     InvokeObjectPostAlterHook(RelationRelationId,
    9253             :                               RelationGetRelid(rel),
    9254             :                               attrtuple->attnum);
    9255             : 
    9256             :     /*
    9257             :      * Apply the change to indexes as well (only for simple index columns,
    9258             :      * matching behavior of index.c ConstructTupleDescriptor()).
    9259             :      */
    9260         248 :     SetIndexStorageProperties(rel, attrelation, attnum,
    9261         248 :                               true, attrtuple->attstorage,
    9262             :                               false, 0,
    9263             :                               lockmode);
    9264             : 
    9265         248 :     heap_freetuple(tuple);
    9266             : 
    9267         248 :     table_close(attrelation, RowExclusiveLock);
    9268             : 
    9269         248 :     ObjectAddressSubSet(address, RelationRelationId,
    9270             :                         RelationGetRelid(rel), attnum);
    9271         248 :     return address;
    9272             : }
    9273             : 
    9274             : 
    9275             : /*
    9276             :  * ALTER TABLE DROP COLUMN
    9277             :  *
    9278             :  * DROP COLUMN cannot use the normal ALTER TABLE recursion mechanism,
    9279             :  * because we have to decide at runtime whether to recurse or not depending
    9280             :  * on whether attinhcount goes to zero or not.  (We can't check this in a
    9281             :  * static pre-pass because it won't handle multiple inheritance situations
    9282             :  * correctly.)
    9283             :  */
    9284             : static void
    9285        1694 : ATPrepDropColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
    9286             :                  AlterTableCmd *cmd, LOCKMODE lockmode,
    9287             :                  AlterTableUtilityContext *context)
    9288             : {
    9289        1694 :     if (rel->rd_rel->reloftype && !recursing)
    9290           6 :         ereport(ERROR,
    9291             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    9292             :                  errmsg("cannot drop column from typed table")));
    9293             : 
    9294        1688 :     if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
    9295          84 :         ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
    9296             : 
    9297        1682 :     if (recurse)
    9298        1402 :         cmd->recurse = true;
    9299        1682 : }
    9300             : 
    9301             : /*
    9302             :  * Drops column 'colName' from relation 'rel' and returns the address of the
    9303             :  * dropped column.  The column is also dropped (or marked as no longer
    9304             :  * inherited from relation) from the relation's inheritance children, if any.
    9305             :  *
    9306             :  * In the recursive invocations for inheritance child relations, instead of
    9307             :  * dropping the column directly (if to be dropped at all), its object address
    9308             :  * is added to 'addrs', which must be non-NULL in such invocations.  All
    9309             :  * columns are dropped at the same time after all the children have been
    9310             :  * checked recursively.
    9311             :  */
    9312             : static ObjectAddress
    9313        2244 : ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
    9314             :                  DropBehavior behavior,
    9315             :                  bool recurse, bool recursing,
    9316             :                  bool missing_ok, LOCKMODE lockmode,
    9317             :                  ObjectAddresses *addrs)
    9318             : {
    9319             :     HeapTuple   tuple;
    9320             :     Form_pg_attribute targetatt;
    9321             :     AttrNumber  attnum;
    9322             :     List       *children;
    9323             :     ObjectAddress object;
    9324             :     bool        is_expr;
    9325             : 
    9326             :     /* At top level, permission check was done in ATPrepCmd, else do it */
    9327        2244 :     if (recursing)
    9328         562 :         ATSimplePermissions(AT_DropColumn, rel,
    9329             :                             ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
    9330             : 
    9331             :     /* Initialize addrs on the first invocation */
    9332             :     Assert(!recursing || addrs != NULL);
    9333             : 
    9334             :     /* since this function recurses, it could be driven to stack overflow */
    9335        2244 :     check_stack_depth();
    9336             : 
    9337        2244 :     if (!recursing)
    9338        1682 :         addrs = new_object_addresses();
    9339             : 
    9340             :     /*
    9341             :      * get the number of the attribute
    9342             :      */
    9343        2244 :     tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
    9344        2244 :     if (!HeapTupleIsValid(tuple))
    9345             :     {
    9346          54 :         if (!missing_ok)
    9347             :         {
    9348          36 :             ereport(ERROR,
    9349             :                     (errcode(ERRCODE_UNDEFINED_COLUMN),
    9350             :                      errmsg("column \"%s\" of relation \"%s\" does not exist",
    9351             :                             colName, RelationGetRelationName(rel))));
    9352             :         }
    9353             :         else
    9354             :         {
    9355          18 :             ereport(NOTICE,
    9356             :                     (errmsg("column \"%s\" of relation \"%s\" does not exist, skipping",
    9357             :                             colName, RelationGetRelationName(rel))));
    9358          18 :             return InvalidObjectAddress;
    9359             :         }
    9360             :     }
    9361        2190 :     targetatt = (Form_pg_attribute) GETSTRUCT(tuple);
    9362             : 
    9363        2190 :     attnum = targetatt->attnum;
    9364             : 
    9365             :     /* Can't drop a system attribute */
    9366        2190 :     if (attnum <= 0)
    9367           6 :         ereport(ERROR,
    9368             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    9369             :                  errmsg("cannot drop system column \"%s\"",
    9370             :                         colName)));
    9371             : 
    9372             :     /*
    9373             :      * Don't drop inherited columns, unless recursing (presumably from a drop
    9374             :      * of the parent column)
    9375             :      */
    9376        2184 :     if (targetatt->attinhcount > 0 && !recursing)
    9377          48 :         ereport(ERROR,
    9378             :                 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    9379             :                  errmsg("cannot drop inherited column \"%s\"",
    9380             :                         colName)));
    9381             : 
    9382             :     /*
    9383             :      * Don't drop columns used in the partition key, either.  (If we let this
    9384             :      * go through, the key column's dependencies would cause a cascaded drop
    9385             :      * of the whole table, which is surely not what the user expected.)
    9386             :      */
    9387        2136 :     if (has_partition_attrs(rel,
    9388             :                             bms_make_singleton(attnum - FirstLowInvalidHeapAttributeNumber),
    9389             :                             &is_expr))
    9390          30 :         ereport(ERROR,
    9391             :                 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    9392             :                  errmsg("cannot drop column \"%s\" because it is part of the partition key of relation \"%s\"",
    9393             :                         colName, RelationGetRelationName(rel))));
    9394             : 
    9395        2106 :     ReleaseSysCache(tuple);
    9396             : 
    9397             :     /*
    9398             :      * Propagate to children as appropriate.  Unlike most other ALTER
    9399             :      * routines, we have to do this one level of recursion at a time; we can't
    9400             :      * use find_all_inheritors to do it in one pass.
    9401             :      */
    9402             :     children =
    9403        2106 :         find_inheritance_children(RelationGetRelid(rel), lockmode);
    9404             : 
    9405        2106 :     if (children)
    9406             :     {
    9407             :         Relation    attr_rel;
    9408             :         ListCell   *child;
    9409             : 
    9410             :         /*
    9411             :          * In case of a partitioned table, the column must be dropped from the
    9412             :          * partitions as well.
    9413             :          */
    9414         308 :         if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && !recurse)
    9415           6 :             ereport(ERROR,
    9416             :                     (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    9417             :                      errmsg("cannot drop column from only the partitioned table when partitions exist"),
    9418             :                      errhint("Do not specify the ONLY keyword.")));
    9419             : 
    9420         302 :         attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
    9421         894 :         foreach(child, children)
    9422             :         {
    9423         598 :             Oid         childrelid = lfirst_oid(child);
    9424             :             Relation    childrel;
    9425             :             Form_pg_attribute childatt;
    9426             : 
    9427             :             /* find_inheritance_children already got lock */
    9428         598 :             childrel = table_open(childrelid, NoLock);
    9429         598 :             CheckAlterTableIsSafe(childrel);
    9430             : 
    9431         598 :             tuple = SearchSysCacheCopyAttName(childrelid, colName);
    9432         598 :             if (!HeapTupleIsValid(tuple))   /* shouldn't happen */
    9433           0 :                 elog(ERROR, "cache lookup failed for attribute \"%s\" of relation %u",
    9434             :                      colName, childrelid);
    9435         598 :             childatt = (Form_pg_attribute) GETSTRUCT(tuple);
    9436             : 
    9437         598 :             if (childatt->attinhcount <= 0) /* shouldn't happen */
    9438           0 :                 elog(ERROR, "relation %u has non-inherited attribute \"%s\"",
    9439             :                      childrelid, colName);
    9440             : 
    9441         598 :             if (recurse)
    9442             :             {
    9443             :                 /*
    9444             :                  * If the child column has other definition sources, just
    9445             :                  * decrement its inheritance count; if not, recurse to delete
    9446             :                  * it.
    9447             :                  */
    9448         574 :                 if (childatt->attinhcount == 1 && !childatt->attislocal)
    9449             :                 {
    9450             :                     /* Time to delete this child column, too */
    9451         562 :                     ATExecDropColumn(wqueue, childrel, colName,
    9452             :                                      behavior, true, true,
    9453             :                                      false, lockmode, addrs);
    9454             :                 }
    9455             :                 else
    9456             :                 {
    9457             :                     /* Child column must survive my deletion */
    9458          12 :                     childatt->attinhcount--;
    9459             : 
    9460          12 :                     CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
    9461             : 
    9462             :                     /* Make update visible */
    9463          12 :                     CommandCounterIncrement();
    9464             :                 }
    9465             :             }
    9466             :             else
    9467             :             {
    9468             :                 /*
    9469             :                  * If we were told to drop ONLY in this table (no recursion),
    9470             :                  * we need to mark the inheritors' attributes as locally
    9471             :                  * defined rather than inherited.
    9472             :                  */
    9473          24 :                 childatt->attinhcount--;
    9474          24 :                 childatt->attislocal = true;
    9475             : 
    9476          24 :                 CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
    9477             : 
    9478             :                 /* Make update visible */
    9479          24 :                 CommandCounterIncrement();
    9480             :             }
    9481             : 
    9482         592 :             heap_freetuple(tuple);
    9483             : 
    9484         592 :             table_close(childrel, NoLock);
    9485             :         }
    9486         296 :         table_close(attr_rel, RowExclusiveLock);
    9487             :     }
    9488             : 
    9489             :     /* Add object to delete */
    9490        2094 :     object.classId = RelationRelationId;
    9491        2094 :     object.objectId = RelationGetRelid(rel);
    9492        2094 :     object.objectSubId = attnum;
    9493        2094 :     add_exact_object_address(&object, addrs);
    9494             : 
    9495        2094 :     if (!recursing)
    9496             :     {
    9497             :         /* Recursion has ended, drop everything that was collected */
    9498        1538 :         performMultipleDeletions(addrs, behavior, 0);
    9499        1484 :         free_object_addresses(addrs);
    9500             :     }
    9501             : 
    9502        2040 :     return object;
    9503             : }
    9504             : 
    9505             : /*
    9506             :  * Prepare to add a primary key on a table, by adding not-null constraints
    9507             :  * on all columns.
    9508             :  *
    9509             :  * The not-null constraints for a primary key must cover the whole inheritance
    9510             :  * hierarchy (failing to ensure that leads to funny corner cases).  For the
    9511             :  * normal case where we're asked to recurse, this routine checks if the
    9512             :  * not-null constraints exist already, and if not queues a requirement for
    9513             :  * them to be created by phase 2.
    9514             :  *
    9515             :  * For the case where we're asked not to recurse, we verify that a not-null
    9516             :  * constraint exists on each column of each (direct) child table, throwing an
    9517             :  * error if not.  Not throwing an error would also work, because a not-null
    9518             :  * constraint would be created anyway, but it'd cause a silent scan of the
    9519             :  * child table to verify absence of nulls.  We prefer to let the user know so
    9520             :  * that they can add the constraint manually without having to hold
    9521             :  * AccessExclusiveLock while at it.
    9522             :  *
    9523             :  * However, it's also important that we do not acquire locks on children if
    9524             :  * the not-null constraints already exist on the parent, to avoid risking
    9525             :  * deadlocks during parallel pg_restore of PKs on partitioned tables.
    9526             :  */
    9527             : static void
    9528       16420 : ATPrepAddPrimaryKey(List **wqueue, Relation rel, AlterTableCmd *cmd,
    9529             :                     bool recurse, LOCKMODE lockmode,
    9530             :                     AlterTableUtilityContext *context)
    9531             : {
    9532             :     Constraint *pkconstr;
    9533       16420 :     List       *children = NIL;
    9534       16420 :     bool        got_children = false;
    9535             : 
    9536       16420 :     pkconstr = castNode(Constraint, cmd->def);
    9537       16420 :     if (pkconstr->contype != CONSTR_PRIMARY)
    9538        9434 :         return;
    9539             : 
    9540             :     /* Verify that columns are not-null, or request that they be made so */
    9541       14948 :     foreach_node(String, column, pkconstr->keys)
    9542             :     {
    9543             :         AlterTableCmd *newcmd;
    9544             :         Constraint *nnconstr;
    9545             :         HeapTuple   tuple;
    9546             : 
    9547             :         /*
    9548             :          * First check if a suitable constraint exists.  If it does, we don't
    9549             :          * need to request another one.  We do need to bail out if it's not
    9550             :          * valid, though.
    9551             :          */
    9552        1036 :         tuple = findNotNullConstraint(RelationGetRelid(rel), strVal(column));
    9553        1036 :         if (tuple != NULL)
    9554             :         {
    9555         526 :             verifyNotNullPKCompatible(tuple, strVal(column));
    9556             : 
    9557             :             /* All good with this one; don't request another */
    9558         514 :             heap_freetuple(tuple);
    9559         514 :             continue;
    9560             :         }
    9561         510 :         else if (!recurse)
    9562             :         {
    9563             :             /*
    9564             :              * No constraint on this column.  Asked not to recurse, we won't
    9565             :              * create one here, but verify that all children have one.
    9566             :              */
    9567          36 :             if (!got_children)
    9568             :             {
    9569          36 :                 children = find_inheritance_children(RelationGetRelid(rel),
    9570             :                                                      lockmode);
    9571             :                 /* only search for children on the first time through */
    9572          36 :                 got_children = true;
    9573             :             }
    9574             : 
    9575          72 :             foreach_oid(childrelid, children)
    9576             :             {
    9577             :                 HeapTuple   tup;
    9578             : 
    9579          36 :                 tup = findNotNullConstraint(childrelid, strVal(column));
    9580          36 :                 if (!tup)
    9581           6 :                     ereport(ERROR,
    9582             :                             errmsg("column \"%s\" of table \"%s\" is not marked NOT NULL",
    9583             :                                    strVal(column), get_rel_name(childrelid)));
    9584             :                 /* verify it's good enough */
    9585          30 :                 verifyNotNullPKCompatible(tup, strVal(column));
    9586             :             }
    9587             :         }
    9588             : 
    9589             :         /* This column is not already not-null, so add it to the queue */
    9590         492 :         nnconstr = makeNotNullConstraint(column);
    9591             : 
    9592         492 :         newcmd = makeNode(AlterTableCmd);
    9593         492 :         newcmd->subtype = AT_AddConstraint;
    9594             :         /* note we force recurse=true here; see above */
    9595         492 :         newcmd->recurse = true;
    9596         492 :         newcmd->def = (Node *) nnconstr;
    9597             : 
    9598         492 :         ATPrepCmd(wqueue, rel, newcmd, true, false, lockmode, context);
    9599             :     }
    9600             : }
    9601             : 
    9602             : /*
    9603             :  * Verify whether the given not-null constraint is compatible with a
    9604             :  * primary key.  If not, an error is thrown.
    9605             :  */
    9606             : static void
    9607         556 : verifyNotNullPKCompatible(HeapTuple tuple, const char *colname)
    9608             : {
    9609         556 :     Form_pg_constraint conForm = (Form_pg_constraint) GETSTRUCT(tuple);
    9610             : 
    9611         556 :     if (conForm->contype != CONSTRAINT_NOTNULL)
    9612           0 :         elog(ERROR, "constraint %u is not a not-null constraint", conForm->oid);
    9613             : 
    9614             :     /* a NO INHERIT constraint is no good */
    9615         556 :     if (conForm->connoinherit)
    9616          12 :         ereport(ERROR,
    9617             :                 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    9618             :                 errmsg("cannot create primary key on column \"%s\"", colname),
    9619             :         /*- translator: fourth %s is a constraint characteristic such as NOT VALID */
    9620             :                 errdetail("The constraint \"%s\" on column \"%s\" of table \"%s\", marked %s, is incompatible with a primary key.",
    9621             :                           NameStr(conForm->conname), colname,
    9622             :                           get_rel_name(conForm->conrelid), "NO INHERIT"),
    9623             :                 errhint("You might need to make the existing constraint inheritable using %s.",
    9624             :                         "ALTER TABLE ... ALTER CONSTRAINT ... INHERIT"));
    9625             : 
    9626             :     /* an unvalidated constraint is no good */
    9627         544 :     if (!conForm->convalidated)
    9628          12 :         ereport(ERROR,
    9629             :                 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    9630             :                 errmsg("cannot create primary key on column \"%s\"", colname),
    9631             :         /*- translator: fourth %s is a constraint characteristic such as NOT VALID */
    9632             :                 errdetail("The constraint \"%s\" on column \"%s\" of table \"%s\", marked %s, is incompatible with a primary key.",
    9633             :                           NameStr(conForm->conname), colname,
    9634             :                           get_rel_name(conForm->conrelid), "NOT VALID"),
    9635             :                 errhint("You might need to validate it using %s.",
    9636             :                         "ALTER TABLE ... VALIDATE CONSTRAINT"));
    9637         532 : }
    9638             : 
    9639             : /*
    9640             :  * ALTER TABLE ADD INDEX
    9641             :  *
    9642             :  * There is no such command in the grammar, but parse_utilcmd.c converts
    9643             :  * UNIQUE and PRIMARY KEY constraints into AT_AddIndex subcommands.  This lets
    9644             :  * us schedule creation of the index at the appropriate time during ALTER.
    9645             :  *
    9646             :  * Return value is the address of the new index.
    9647             :  */
    9648             : static ObjectAddress
    9649        1660 : ATExecAddIndex(AlteredTableInfo *tab, Relation rel,
    9650             :                IndexStmt *stmt, bool is_rebuild, LOCKMODE lockmode)
    9651             : {
    9652             :     bool        check_rights;
    9653             :     bool        skip_build;
    9654             :     bool        quiet;
    9655             :     ObjectAddress address;
    9656             : 
    9657             :     Assert(IsA(stmt, IndexStmt));
    9658             :     Assert(!stmt->concurrent);
    9659             : 
    9660             :     /* The IndexStmt has already been through transformIndexStmt */
    9661             :     Assert(stmt->transformed);
    9662             : 
    9663             :     /* suppress schema rights check when rebuilding existing index */
    9664        1660 :     check_rights = !is_rebuild;
    9665             :     /* skip index build if phase 3 will do it or we're reusing an old one */
    9666        1660 :     skip_build = tab->rewrite > 0 || RelFileNumberIsValid(stmt->oldNumber);
    9667             :     /* suppress notices when rebuilding existing index */
    9668        1660 :     quiet = is_rebuild;
    9669             : 
    9670        1660 :     address = DefineIndex(NULL,
    9671             :                           RelationGetRelid(rel),
    9672             :                           stmt,
    9673             :                           InvalidOid,   /* no predefined OID */
    9674             :                           InvalidOid,   /* no parent index */
    9675             :                           InvalidOid,   /* no parent constraint */
    9676             :                           -1,   /* total_parts unknown */
    9677             :                           true, /* is_alter_table */
    9678             :                           check_rights,
    9679             :                           false,    /* check_not_in_use - we did it already */
    9680             :                           skip_build,
    9681             :                           quiet);
    9682             : 
    9683             :     /*
    9684             :      * If TryReuseIndex() stashed a relfilenumber for us, we used it for the
    9685             :      * new index instead of building from scratch.  Restore associated fields.
    9686             :      * This may store InvalidSubTransactionId in both fields, in which case
    9687             :      * relcache.c will assume it can rebuild the relcache entry.  Hence, do
    9688             :      * this after the CCI that made catalog rows visible to any rebuild.  The
    9689             :      * DROP of the old edition of this index will have scheduled the storage
    9690             :      * for deletion at commit, so cancel that pending deletion.
    9691             :      */
    9692        1490 :     if (RelFileNumberIsValid(stmt->oldNumber))
    9693             :     {
    9694          74 :         Relation    irel = index_open(address.objectId, NoLock);
    9695             : 
    9696          74 :         irel->rd_createSubid = stmt->oldCreateSubid;
    9697          74 :         irel->rd_firstRelfilelocatorSubid = stmt->oldFirstRelfilelocatorSubid;
    9698          74 :         RelationPreserveStorage(irel->rd_locator, true);
    9699          74 :         index_close(irel, NoLock);
    9700             :     }
    9701             : 
    9702        1490 :     return address;
    9703             : }
    9704             : 
    9705             : /*
    9706             :  * ALTER TABLE ADD STATISTICS
    9707             :  *
    9708             :  * This is no such command in the grammar, but we use this internally to add
    9709             :  * AT_ReAddStatistics subcommands to rebuild extended statistics after a table
    9710             :  * column type change.
    9711             :  */
    9712             : static ObjectAddress
    9713          74 : ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
    9714             :                     CreateStatsStmt *stmt, bool is_rebuild, LOCKMODE lockmode)
    9715             : {
    9716             :     ObjectAddress address;
    9717             : 
    9718             :     Assert(IsA(stmt, CreateStatsStmt));
    9719             : 
    9720             :     /* The CreateStatsStmt has already been through transformStatsStmt */
    9721             :     Assert(stmt->transformed);
    9722             : 
    9723          74 :     address = CreateStatistics(stmt, !is_rebuild);
    9724             : 
    9725          74 :     return address;
    9726             : }
    9727             : 
    9728             : /*
    9729             :  * ALTER TABLE ADD CONSTRAINT USING INDEX
    9730             :  *
    9731             :  * Returns the address of the new constraint.
    9732             :  */
    9733             : static ObjectAddress
    9734       10860 : ATExecAddIndexConstraint(AlteredTableInfo *tab, Relation rel,
    9735             :                          IndexStmt *stmt, LOCKMODE lockmode)
    9736             : {
    9737       10860 :     Oid         index_oid = stmt->indexOid;
    9738             :     Relation    indexRel;
    9739             :     char       *indexName;
    9740             :     IndexInfo  *indexInfo;
    9741             :     char       *constraintName;
    9742             :     char        constraintType;
    9743             :     ObjectAddress address;
    9744             :     bits16      flags;
    9745             : 
    9746             :     Assert(IsA(stmt, IndexStmt));
    9747             :     Assert(OidIsValid(index_oid));
    9748             :     Assert(stmt->isconstraint);
    9749             : 
    9750             :     /*
    9751             :      * Doing this on partitioned tables is not a simple feature to implement,
    9752             :      * so let's punt for now.
    9753             :      */
    9754       10860 :     if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
    9755           6 :         ereport(ERROR,
    9756             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    9757             :                  errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX is not supported on partitioned tables")));
    9758             : 
    9759       10854 :     indexRel = index_open(index_oid, AccessShareLock);
    9760             : 
    9761       10854 :     indexName = pstrdup(RelationGetRelationName(indexRel));
    9762             : 
    9763       10854 :     indexInfo = BuildIndexInfo(indexRel);
    9764             : 
    9765             :     /* this should have been checked at parse time */
    9766       10854 :     if (!indexInfo->ii_Unique)
    9767           0 :         elog(ERROR, "index \"%s\" is not unique", indexName);
    9768             : 
    9769             :     /*
    9770             :      * Determine name to assign to constraint.  We require a constraint to
    9771             :      * have the same name as the underlying index; therefore, use the index's
    9772             :      * existing name as the default constraint name, and if the user
    9773             :      * explicitly gives some other name for the constraint, rename the index
    9774             :      * to match.
    9775             :      */
    9776       10854 :     constraintName = stmt->idxname;
    9777       10854 :     if (constraintName == NULL)
    9778       10828 :         constraintName = indexName;
    9779          26 :     else if (strcmp(constraintName, indexName) != 0)
    9780             :     {
    9781          20 :         ereport(NOTICE,
    9782             :                 (errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX will rename index \"%s\" to \"%s\"",
    9783             :                         indexName, constraintName)));
    9784          20 :         RenameRelationInternal(index_oid, constraintName, false, true);
    9785             :     }
    9786             : 
    9787             :     /* Extra checks needed if making primary key */
    9788       10854 :     if (stmt->primary)
    9789        6130 :         index_check_primary_key(rel, indexInfo, true, stmt);
    9790             : 
    9791             :     /* Note we currently don't support EXCLUSION constraints here */
    9792       10848 :     if (stmt->primary)
    9793        6124 :         constraintType = CONSTRAINT_PRIMARY;
    9794             :     else
    9795        4724 :         constraintType = CONSTRAINT_UNIQUE;
    9796             : 
    9797             :     /* Create the catalog entries for the constraint */
    9798       10848 :     flags = INDEX_CONSTR_CREATE_UPDATE_INDEX |
    9799             :         INDEX_CONSTR_CREATE_REMOVE_OLD_DEPS |
    9800       21696 :         (stmt->initdeferred ? INDEX_CONSTR_CREATE_INIT_DEFERRED : 0) |
    9801       10848 :         (stmt->deferrable ? INDEX_CONSTR_CREATE_DEFERRABLE : 0) |
    9802       10848 :         (stmt->primary ? INDEX_CONSTR_CREATE_MARK_AS_PRIMARY : 0);
    9803             : 
    9804       10848 :     address = index_constraint_create(rel,
    9805             :                                       index_oid,
    9806             :                                       InvalidOid,
    9807             :                                       indexInfo,
    9808             :                                       constraintName,
    9809             :                                       constraintType,
    9810             :                                       flags,
    9811             :                                       allowSystemTableMods,
    9812             :                                       false);   /* is_internal */
    9813             : 
    9814       10848 :     index_close(indexRel, NoLock);
    9815             : 
    9816       10848 :     return address;
    9817             : }
    9818             : 
    9819             : /*
    9820             :  * ALTER TABLE ADD CONSTRAINT
    9821             :  *
    9822             :  * Return value is the address of the new constraint; if no constraint was
    9823             :  * added, InvalidObjectAddress is returned.
    9824             :  */
    9825             : static ObjectAddress
    9826       13108 : ATExecAddConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
    9827             :                     Constraint *newConstraint, bool recurse, bool is_readd,
    9828             :                     LOCKMODE lockmode)
    9829             : {
    9830       13108 :     ObjectAddress address = InvalidObjectAddress;
    9831             : 
    9832             :     Assert(IsA(newConstraint, Constraint));
    9833             : 
    9834             :     /*
    9835             :      * Currently, we only expect to see CONSTR_CHECK, CONSTR_NOTNULL and
    9836             :      * CONSTR_FOREIGN nodes arriving here (see the preprocessing done in
    9837             :      * parse_utilcmd.c).
    9838             :      */
    9839       13108 :     switch (newConstraint->contype)
    9840             :     {
    9841       10412 :         case CONSTR_CHECK:
    9842             :         case CONSTR_NOTNULL:
    9843             :             address =
    9844       10412 :                 ATAddCheckNNConstraint(wqueue, tab, rel,
    9845             :                                        newConstraint, recurse, false, is_readd,
    9846             :                                        lockmode);
    9847       10268 :             break;
    9848             : 
    9849        2696 :         case CONSTR_FOREIGN:
    9850             : 
    9851             :             /*
    9852             :              * Assign or validate constraint name
    9853             :              */
    9854        2696 :             if (newConstraint->conname)
    9855             :             {
    9856        1212 :                 if (ConstraintNameIsUsed(CONSTRAINT_RELATION,
    9857             :                                          RelationGetRelid(rel),
    9858        1212 :                                          newConstraint->conname))
    9859           0 :                     ereport(ERROR,
    9860             :                             (errcode(ERRCODE_DUPLICATE_OBJECT),
    9861             :                              errmsg("constraint \"%s\" for relation \"%s\" already exists",
    9862             :                                     newConstraint->conname,
    9863             :                                     RelationGetRelationName(rel))));
    9864             :             }
    9865             :             else
    9866        1484 :                 newConstraint->conname =
    9867        1484 :                     ChooseConstraintName(RelationGetRelationName(rel),
    9868        1484 :                                          ChooseForeignKeyConstraintNameAddition(newConstraint->fk_attrs),
    9869             :                                          "fkey",
    9870        1484 :                                          RelationGetNamespace(rel),
    9871             :                                          NIL);
    9872             : 
    9873        2696 :             address = ATAddForeignKeyConstraint(wqueue, tab, rel,
    9874             :                                                 newConstraint,
    9875             :                                                 recurse, false,
    9876             :                                                 lockmode);
    9877        2148 :             break;
    9878             : 
    9879           0 :         default:
    9880           0 :             elog(ERROR, "unrecognized constraint type: %d",
    9881             :                  (int) newConstraint->contype);
    9882             :     }
    9883             : 
    9884       12416 :     return address;
    9885             : }
    9886             : 
    9887             : /*
    9888             :  * Generate the column-name portion of the constraint name for a new foreign
    9889             :  * key given the list of column names that reference the referenced
    9890             :  * table.  This will be passed to ChooseConstraintName along with the parent
    9891             :  * table name and the "fkey" suffix.
    9892             :  *
    9893             :  * We know that less than NAMEDATALEN characters will actually be used, so we
    9894             :  * can truncate the result once we've generated that many.
    9895             :  *
    9896             :  * XXX see also ChooseExtendedStatisticNameAddition and
    9897             :  * ChooseIndexNameAddition.
    9898             :  */
    9899             : static char *
    9900        1484 : ChooseForeignKeyConstraintNameAddition(List *colnames)
    9901             : {
    9902             :     char        buf[NAMEDATALEN * 2];
    9903        1484 :     int         buflen = 0;
    9904             :     ListCell   *lc;
    9905             : 
    9906        1484 :     buf[0] = '\0';
    9907        3396 :     foreach(lc, colnames)
    9908             :     {
    9909        1912 :         const char *name = strVal(lfirst(lc));
    9910             : 
    9911        1912 :         if (buflen > 0)
    9912         428 :             buf[buflen++] = '_';    /* insert _ between names */
    9913             : 
    9914             :         /*
    9915             :          * At this point we have buflen <= NAMEDATALEN.  name should be less
    9916             :          * than NAMEDATALEN already, but use strlcpy for paranoia.
    9917             :          */
    9918        1912 :         strlcpy(buf + buflen, name, NAMEDATALEN);
    9919        1912 :         buflen += strlen(buf + buflen);
    9920        1912 :         if (buflen >= NAMEDATALEN)
    9921           0 :             break;
    9922             :     }
    9923        1484 :     return pstrdup(buf);
    9924             : }
    9925             : 
    9926             : /*
    9927             :  * Add a check or not-null constraint to a single table and its children.
    9928             :  * Returns the address of the constraint added to the parent relation,
    9929             :  * if one gets added, or InvalidObjectAddress otherwise.
    9930             :  *
    9931             :  * Subroutine for ATExecAddConstraint.
    9932             :  *
    9933             :  * We must recurse to child tables during execution, rather than using
    9934             :  * ALTER TABLE's normal prep-time recursion.  The reason is that all the
    9935             :  * constraints *must* be given the same name, else they won't be seen as
    9936             :  * related later.  If the user didn't explicitly specify a name, then
    9937             :  * AddRelationNewConstraints would normally assign different names to the
    9938             :  * child constraints.  To fix that, we must capture the name assigned at
    9939             :  * the parent table and pass that down.
    9940             :  */
    9941             : static ObjectAddress
    9942       11318 : ATAddCheckNNConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
    9943             :                        Constraint *constr, bool recurse, bool recursing,
    9944             :                        bool is_readd, LOCKMODE lockmode)
    9945             : {
    9946             :     List       *newcons;
    9947             :     ListCell   *lcon;
    9948             :     List       *children;
    9949             :     ListCell   *child;
    9950       11318 :     ObjectAddress address = InvalidObjectAddress;
    9951             : 
    9952             :     /* Guard against stack overflow due to overly deep inheritance tree. */
    9953       11318 :     check_stack_depth();
    9954             : 
    9955             :     /* At top level, permission check was done in ATPrepCmd, else do it */
    9956       11318 :     if (recursing)
    9957         906 :         ATSimplePermissions(AT_AddConstraint, rel,
    9958             :                             ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
    9959             : 
    9960             :     /*
    9961             :      * Call AddRelationNewConstraints to do the work, making sure it works on
    9962             :      * a copy of the Constraint so transformExpr can't modify the original. It
    9963             :      * returns a list of cooked constraints.
    9964             :      *
    9965             :      * If the constraint ends up getting merged with a pre-existing one, it's
    9966             :      * omitted from the returned list, which is what we want: we do not need
    9967             :      * to do any validation work.  That can only happen at child tables,
    9968             :      * though, since we disallow merging at the top level.
    9969             :      */
    9970       11318 :     newcons = AddRelationNewConstraints(rel, NIL,
    9971       11318 :                                         list_make1(copyObject(constr)),
    9972       11318 :                                         recursing || is_readd,  /* allow_merge */
    9973       11318 :                                         !recursing, /* is_local */
    9974             :                                         is_readd,   /* is_internal */
    9975       11318 :                                         NULL);  /* queryString not available
    9976             :                                                  * here */
    9977             : 
    9978             :     /* we don't expect more than one constraint here */
    9979             :     Assert(list_length(newcons) <= 1);
    9980             : 
    9981             :     /* Add each to-be-validated constraint to Phase 3's queue */
    9982       22160 :     foreach(lcon, newcons)
    9983             :     {
    9984       10980 :         CookedConstraint *ccon = (CookedConstraint *) lfirst(lcon);
    9985             : 
    9986       10980 :         if (!ccon->skip_validation && ccon->contype != CONSTR_NOTNULL)
    9987             :         {
    9988             :             NewConstraint *newcon;
    9989             : 
    9990        1006 :             newcon = palloc0_object(NewConstraint);
    9991        1006 :             newcon->name = ccon->name;
    9992        1006 :             newcon->contype = ccon->contype;
    9993        1006 :             newcon->qual = ccon->expr;
    9994             : 
    9995        1006 :             tab->constraints = lappend(tab->constraints, newcon);
    9996             :         }
    9997             : 
    9998             :         /* Save the actually assigned name if it was defaulted */
    9999       10980 :         if (constr->conname == NULL)
   10000        9074 :             constr->conname = ccon->name;
   10001             : 
   10002             :         /*
   10003             :          * If adding a valid not-null constraint, set the pg_attribute flag
   10004             :          * and tell phase 3 to verify existing rows, if needed.  For an
   10005             :          * invalid constraint, just set attnotnull, without queueing
   10006             :          * verification.
   10007             :          */
   10008       10980 :         if (constr->contype == CONSTR_NOTNULL)
   10009        9510 :             set_attnotnull(wqueue, rel, ccon->attnum,
   10010        9510 :                            !constr->skip_validation,
   10011        9510 :                            !constr->skip_validation);
   10012             : 
   10013       10980 :         ObjectAddressSet(address, ConstraintRelationId, ccon->conoid);
   10014             :     }
   10015             : 
   10016             :     /* At this point we must have a locked-down name to use */
   10017             :     Assert(newcons == NIL || constr->conname != NULL);
   10018             : 
   10019             :     /* Advance command counter in case same table is visited multiple times */
   10020       11180 :     CommandCounterIncrement();
   10021             : 
   10022             :     /*
   10023             :      * If the constraint got merged with an existing constraint, we're done.
   10024             :      * We mustn't recurse to child tables in this case, because they've
   10025             :      * already got the constraint, and visiting them again would lead to an
   10026             :      * incorrect value for coninhcount.
   10027             :      */
   10028       11180 :     if (newcons == NIL)
   10029         200 :         return address;
   10030             : 
   10031             :     /*
   10032             :      * If adding a NO INHERIT constraint, no need to find our children.
   10033             :      */
   10034       10980 :     if (constr->is_no_inherit)
   10035          84 :         return address;
   10036             : 
   10037             :     /*
   10038             :      * Propagate to children as appropriate.  Unlike most other ALTER
   10039             :      * routines, we have to do this one level of recursion at a time; we can't
   10040             :      * use find_all_inheritors to do it in one pass.
   10041             :      */
   10042             :     children =
   10043       10896 :         find_inheritance_children(RelationGetRelid(rel), lockmode);
   10044             : 
   10045             :     /*
   10046             :      * Check if ONLY was specified with ALTER TABLE.  If so, allow the
   10047             :      * constraint creation only if there are no children currently. Error out
   10048             :      * otherwise.
   10049             :      */
   10050       10896 :     if (!recurse && children != NIL)
   10051           6 :         ereport(ERROR,
   10052             :                 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
   10053             :                  errmsg("constraint must be added to child tables too")));
   10054             : 
   10055             :     /*
   10056             :      * Recurse to create the constraint on each child.
   10057             :      */
   10058       11766 :     foreach(child, children)
   10059             :     {
   10060         906 :         Oid         childrelid = lfirst_oid(child);
   10061             :         Relation    childrel;
   10062             :         AlteredTableInfo *childtab;
   10063             : 
   10064             :         /* find_inheritance_children already got lock */
   10065         906 :         childrel = table_open(childrelid, NoLock);
   10066         906 :         CheckAlterTableIsSafe(childrel);
   10067             : 
   10068             :         /* Find or create work queue entry for this table */
   10069         906 :         childtab = ATGetQueueEntry(wqueue, childrel);
   10070             : 
   10071             :         /* Recurse to this child */
   10072         906 :         ATAddCheckNNConstraint(wqueue, childtab, childrel,
   10073             :                                constr, recurse, true, is_readd, lockmode);
   10074             : 
   10075         876 :         table_close(childrel, NoLock);
   10076             :     }
   10077             : 
   10078       10860 :     return address;
   10079             : }
   10080             : 
   10081             : /*
   10082             :  * Add a foreign-key constraint to a single table; return the new constraint's
   10083             :  * address.
   10084             :  *
   10085             :  * Subroutine for ATExecAddConstraint.  Must already hold exclusive
   10086             :  * lock on the rel, and have done appropriate validity checks for it.
   10087             :  * We do permissions checks here, however.
   10088             :  *
   10089             :  * When the referenced or referencing tables (or both) are partitioned,
   10090             :  * multiple pg_constraint rows are required -- one for each partitioned table
   10091             :  * and each partition on each side (fortunately, not one for every combination
   10092             :  * thereof).  We also need action triggers on each leaf partition on the
   10093             :  * referenced side, and check triggers on each leaf partition on the
   10094             :  * referencing side.
   10095             :  */
   10096             : static ObjectAddress
   10097        2696 : ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
   10098             :                           Constraint *fkconstraint,
   10099             :                           bool recurse, bool recursing, LOCKMODE lockmode)
   10100             : {
   10101             :     Relation    pkrel;
   10102        2696 :     int16       pkattnum[INDEX_MAX_KEYS] = {0};
   10103        2696 :     int16       fkattnum[INDEX_MAX_KEYS] = {0};
   10104        2696 :     Oid         pktypoid[INDEX_MAX_KEYS] = {0};
   10105        2696 :     Oid         fktypoid[INDEX_MAX_KEYS] = {0};
   10106        2696 :     Oid         pkcolloid[INDEX_MAX_KEYS] = {0};
   10107        2696 :     Oid         fkcolloid[INDEX_MAX_KEYS] = {0};
   10108        2696 :     Oid         opclasses[INDEX_MAX_KEYS] = {0};
   10109        2696 :     Oid         pfeqoperators[INDEX_MAX_KEYS] = {0};
   10110        2696 :     Oid         ppeqoperators[INDEX_MAX_KEYS] = {0};
   10111        2696 :     Oid         ffeqoperators[INDEX_MAX_KEYS] = {0};
   10112        2696 :     int16       fkdelsetcols[INDEX_MAX_KEYS] = {0};
   10113             :     bool        with_period;
   10114             :     bool        pk_has_without_overlaps;
   10115             :     int         i;
   10116             :     int         numfks,
   10117             :                 numpks,
   10118             :                 numfkdelsetcols;
   10119             :     Oid         indexOid;
   10120             :     bool        old_check_ok;
   10121             :     ObjectAddress address;
   10122        2696 :     ListCell   *old_pfeqop_item = list_head(fkconstraint->old_conpfeqop);
   10123             : 
   10124             :     /*
   10125             :      * Grab ShareRowExclusiveLock on the pk table, so that someone doesn't
   10126             :      * delete rows out from under us.
   10127             :      */
   10128        2696 :     if (OidIsValid(fkconstraint->old_pktable_oid))
   10129          72 :         pkrel = table_open(fkconstraint->old_pktable_oid, ShareRowExclusiveLock);
   10130             :     else
   10131        2624 :         pkrel = table_openrv(fkconstraint->pktable, ShareRowExclusiveLock);
   10132             : 
   10133             :     /*
   10134             :      * Validity checks (permission checks wait till we have the column
   10135             :      * numbers)
   10136             :      */
   10137        2690 :     if (!recurse && rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
   10138           6 :         ereport(ERROR,
   10139             :                 errcode(ERRCODE_WRONG_OBJECT_TYPE),
   10140             :                 errmsg("cannot use ONLY for foreign key on partitioned table \"%s\" referencing relation \"%s\"",
   10141             :                        RelationGetRelationName(rel),
   10142             :                        RelationGetRelationName(pkrel)));
   10143             : 
   10144        2684 :     if (pkrel->rd_rel->relkind != RELKIND_RELATION &&
   10145         368 :         pkrel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
   10146           0 :         ereport(ERROR,
   10147             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   10148             :                  errmsg("referenced relation \"%s\" is not a table",
   10149             :                         RelationGetRelationName(pkrel))));
   10150             : 
   10151        2684 :     if (!allowSystemTableMods && IsSystemRelation(pkrel))
   10152           2 :         ereport(ERROR,
   10153             :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
   10154             :                  errmsg("permission denied: \"%s\" is a system catalog",
   10155             :                         RelationGetRelationName(pkrel))));
   10156             : 
   10157             :     /*
   10158             :      * References from permanent or unlogged tables to temp tables, and from
   10159             :      * permanent tables to unlogged tables, are disallowed because the
   10160             :      * referenced data can vanish out from under us.  References from temp
   10161             :      * tables to any other table type are also disallowed, because other
   10162             :      * backends might need to run the RI triggers on the perm table, but they
   10163             :      * can't reliably see tuples in the local buffers of other backends.
   10164             :      */
   10165        2682 :     switch (rel->rd_rel->relpersistence)
   10166             :     {
   10167        2392 :         case RELPERSISTENCE_PERMANENT:
   10168        2392 :             if (!RelationIsPermanent(pkrel))
   10169           0 :                 ereport(ERROR,
   10170             :                         (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
   10171             :                          errmsg("constraints on permanent tables may reference only permanent tables")));
   10172        2392 :             break;
   10173          12 :         case RELPERSISTENCE_UNLOGGED:
   10174          12 :             if (!RelationIsPermanent(pkrel)
   10175          12 :                 && pkrel->rd_rel->relpersistence != RELPERSISTENCE_UNLOGGED)
   10176           0 :                 ereport(ERROR,
   10177             :                         (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
   10178             :                          errmsg("constraints on unlogged tables may reference only permanent or unlogged tables")));
   10179          12 :             break;
   10180         278 :         case RELPERSISTENCE_TEMP:
   10181         278 :             if (pkrel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
   10182           0 :                 ereport(ERROR,
   10183             :                         (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
   10184             :                          errmsg("constraints on temporary tables may reference only temporary tables")));
   10185         278 :             if (!pkrel->rd_islocaltemp || !rel->rd_islocaltemp)
   10186           0 :                 ereport(ERROR,
   10187             :                         (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
   10188             :                          errmsg("constraints on temporary tables must involve temporary tables of this session")));
   10189         278 :             break;
   10190             :     }
   10191             : 
   10192             :     /*
   10193             :      * Look up the referencing attributes to make sure they exist, and record
   10194             :      * their attnums and type and collation OIDs.
   10195             :      */
   10196        2682 :     numfks = transformColumnNameList(RelationGetRelid(rel),
   10197             :                                      fkconstraint->fk_attrs,
   10198             :                                      fkattnum, fktypoid, fkcolloid);
   10199        2652 :     with_period = fkconstraint->fk_with_period || fkconstraint->pk_with_period;
   10200        2652 :     if (with_period && !fkconstraint->fk_with_period)
   10201          24 :         ereport(ERROR,
   10202             :                 errcode(ERRCODE_INVALID_FOREIGN_KEY),
   10203             :                 errmsg("foreign key uses PERIOD on the referenced table but not the referencing table"));
   10204             : 
   10205        2628 :     numfkdelsetcols = transformColumnNameList(RelationGetRelid(rel),
   10206             :                                               fkconstraint->fk_del_set_cols,
   10207             :                                               fkdelsetcols, NULL, NULL);
   10208        2622 :     numfkdelsetcols = validateFkOnDeleteSetColumns(numfks, fkattnum,
   10209             :                                                    numfkdelsetcols,
   10210             :                                                    fkdelsetcols,
   10211             :                                                    fkconstraint->fk_del_set_cols);
   10212             : 
   10213             :     /*
   10214             :      * If the attribute list for the referenced table was omitted, lookup the
   10215             :      * definition of the primary key and use it.  Otherwise, validate the
   10216             :      * supplied attribute list.  In either case, discover the index OID and
   10217             :      * index opclasses, and the attnums and type and collation OIDs of the
   10218             :      * attributes.
   10219             :      */
   10220        2616 :     if (fkconstraint->pk_attrs == NIL)
   10221             :     {
   10222        1268 :         numpks = transformFkeyGetPrimaryKey(pkrel, &indexOid,
   10223             :                                             &fkconstraint->pk_attrs,
   10224             :                                             pkattnum, pktypoid, pkcolloid,
   10225             :                                             opclasses, &pk_has_without_overlaps);
   10226             : 
   10227             :         /* If the primary key uses WITHOUT OVERLAPS, the fk must use PERIOD */
   10228        1268 :         if (pk_has_without_overlaps && !fkconstraint->fk_with_period)
   10229          24 :             ereport(ERROR,
   10230             :                     errcode(ERRCODE_INVALID_FOREIGN_KEY),
   10231             :                     errmsg("foreign key uses PERIOD on the referenced table but not the referencing table"));
   10232             :     }
   10233             :     else
   10234             :     {
   10235        1348 :         numpks = transformColumnNameList(RelationGetRelid(pkrel),
   10236             :                                          fkconstraint->pk_attrs,
   10237             :                                          pkattnum, pktypoid, pkcolloid);
   10238             : 
   10239             :         /* Since we got pk_attrs, one should be a period. */
   10240        1318 :         if (with_period && !fkconstraint->pk_with_period)
   10241          24 :             ereport(ERROR,
   10242             :                     errcode(ERRCODE_INVALID_FOREIGN_KEY),
   10243             :                     errmsg("foreign key uses PERIOD on the referencing table but not the referenced table"));
   10244             : 
   10245             :         /* Look for an index matching the column list */
   10246        1294 :         indexOid = transformFkeyCheckAttrs(pkrel, numpks, pkattnum,
   10247             :                                            with_period, opclasses, &pk_has_without_overlaps);
   10248             :     }
   10249             : 
   10250             :     /*
   10251             :      * If the referenced primary key has WITHOUT OVERLAPS, the foreign key
   10252             :      * must use PERIOD.
   10253             :      */
   10254        2502 :     if (pk_has_without_overlaps && !with_period)
   10255          12 :         ereport(ERROR,
   10256             :                 errcode(ERRCODE_INVALID_FOREIGN_KEY),
   10257             :                 errmsg("foreign key must use PERIOD when referencing a primary key using WITHOUT OVERLAPS"));
   10258             : 
   10259             :     /*
   10260             :      * Now we can check permissions.
   10261             :      */
   10262        2490 :     checkFkeyPermissions(pkrel, pkattnum, numpks);
   10263             : 
   10264             :     /*
   10265             :      * Check some things for generated columns.
   10266             :      */
   10267        5844 :     for (i = 0; i < numfks; i++)
   10268             :     {
   10269        3384 :         char        attgenerated = TupleDescAttr(RelationGetDescr(rel), fkattnum[i] - 1)->attgenerated;
   10270             : 
   10271        3384 :         if (attgenerated)
   10272             :         {
   10273             :             /*
   10274             :              * Check restrictions on UPDATE/DELETE actions, per SQL standard
   10275             :              */
   10276          48 :             if (fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETNULL ||
   10277          48 :                 fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETDEFAULT ||
   10278          48 :                 fkconstraint->fk_upd_action == FKCONSTR_ACTION_CASCADE)
   10279          12 :                 ereport(ERROR,
   10280             :                         (errcode(ERRCODE_SYNTAX_ERROR),
   10281             :                          errmsg("invalid %s action for foreign key constraint containing generated column",
   10282             :                                 "ON UPDATE")));
   10283          36 :             if (fkconstraint->fk_del_action == FKCONSTR_ACTION_SETNULL ||
   10284          24 :                 fkconstraint->fk_del_action == FKCONSTR_ACTION_SETDEFAULT)
   10285          12 :                 ereport(ERROR,
   10286             :                         (errcode(ERRCODE_SYNTAX_ERROR),
   10287             :                          errmsg("invalid %s action for foreign key constraint containing generated column",
   10288             :                                 "ON DELETE")));
   10289             :         }
   10290             : 
   10291             :         /*
   10292             :          * FKs on virtual columns are not supported.  This would require
   10293             :          * various additional support in ri_triggers.c, including special
   10294             :          * handling in ri_NullCheck(), ri_KeysEqual(),
   10295             :          * RI_FKey_fk_upd_check_required() (since all virtual columns appear
   10296             :          * as NULL there).  Also not really practical as long as you can't
   10297             :          * index virtual columns.
   10298             :          */
   10299        3360 :         if (attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
   10300           6 :             ereport(ERROR,
   10301             :                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   10302             :                      errmsg("foreign key constraints on virtual generated columns are not supported")));
   10303             :     }
   10304             : 
   10305             :     /*
   10306             :      * Some actions are currently unsupported for foreign keys using PERIOD.
   10307             :      */
   10308        2460 :     if (fkconstraint->fk_with_period)
   10309             :     {
   10310         278 :         if (fkconstraint->fk_upd_action == FKCONSTR_ACTION_RESTRICT ||
   10311         266 :             fkconstraint->fk_upd_action == FKCONSTR_ACTION_CASCADE ||
   10312         248 :             fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETNULL ||
   10313         230 :             fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETDEFAULT)
   10314          66 :             ereport(ERROR,
   10315             :                     errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   10316             :                     errmsg("unsupported %s action for foreign key constraint using PERIOD",
   10317             :                            "ON UPDATE"));
   10318             : 
   10319         212 :         if (fkconstraint->fk_del_action == FKCONSTR_ACTION_RESTRICT ||
   10320         206 :             fkconstraint->fk_del_action == FKCONSTR_ACTION_CASCADE ||
   10321         206 :             fkconstraint->fk_del_action == FKCONSTR_ACTION_SETNULL ||
   10322         206 :             fkconstraint->fk_del_action == FKCONSTR_ACTION_SETDEFAULT)
   10323           6 :             ereport(ERROR,
   10324             :                     errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   10325             :                     errmsg("unsupported %s action for foreign key constraint using PERIOD",
   10326             :                            "ON DELETE"));
   10327             :     }
   10328             : 
   10329             :     /*
   10330             :      * Look up the equality operators to use in the constraint.
   10331             :      *
   10332             :      * Note that we have to be careful about the difference between the actual
   10333             :      * PK column type and the opclass' declared input type, which might be
   10334             :      * only binary-compatible with it.  The declared opcintype is the right
   10335             :      * thing to probe pg_amop with.
   10336             :      */
   10337        2388 :     if (numfks != numpks)
   10338           0 :         ereport(ERROR,
   10339             :                 (errcode(ERRCODE_INVALID_FOREIGN_KEY),
   10340             :                  errmsg("number of referencing and referenced columns for foreign key disagree")));
   10341             : 
   10342             :     /*
   10343             :      * On the strength of a previous constraint, we might avoid scanning
   10344             :      * tables to validate this one.  See below.
   10345             :      */
   10346        2388 :     old_check_ok = (fkconstraint->old_conpfeqop != NIL);
   10347             :     Assert(!old_check_ok || numfks == list_length(fkconstraint->old_conpfeqop));
   10348             : 
   10349        5214 :     for (i = 0; i < numpks; i++)
   10350             :     {
   10351        3066 :         Oid         pktype = pktypoid[i];
   10352        3066 :         Oid         fktype = fktypoid[i];
   10353             :         Oid         fktyped;
   10354        3066 :         Oid         pkcoll = pkcolloid[i];
   10355        3066 :         Oid         fkcoll = fkcolloid[i];
   10356             :         HeapTuple   cla_ht;
   10357             :         Form_pg_opclass cla_tup;
   10358             :         Oid         amid;
   10359             :         Oid         opfamily;
   10360             :         Oid         opcintype;
   10361             :         bool        for_overlaps;
   10362             :         CompareType cmptype;
   10363             :         Oid         pfeqop;
   10364             :         Oid         ppeqop;
   10365             :         Oid         ffeqop;
   10366             :         int16       eqstrategy;
   10367             :         Oid         pfeqop_right;
   10368             : 
   10369             :         /* We need several fields out of the pg_opclass entry */
   10370        3066 :         cla_ht = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclasses[i]));
   10371        3066 :         if (!HeapTupleIsValid(cla_ht))
   10372           0 :             elog(ERROR, "cache lookup failed for opclass %u", opclasses[i]);
   10373        3066 :         cla_tup = (Form_pg_opclass) GETSTRUCT(cla_ht);
   10374        3066 :         amid = cla_tup->opcmethod;
   10375        3066 :         opfamily = cla_tup->opcfamily;
   10376        3066 :         opcintype = cla_tup->opcintype;
   10377        3066 :         ReleaseSysCache(cla_ht);
   10378             : 
   10379             :         /*
   10380             :          * Get strategy number from index AM.
   10381             :          *
   10382             :          * For a normal foreign-key constraint, this should not fail, since we
   10383             :          * already checked that the index is unique and should therefore have
   10384             :          * appropriate equal operators.  For a period foreign key, this could
   10385             :          * fail if we selected a non-matching exclusion constraint earlier.
   10386             :          * (XXX Maybe we should do these lookups earlier so we don't end up
   10387             :          * doing that.)
   10388             :          */
   10389        3066 :         for_overlaps = with_period && i == numpks - 1;
   10390        3066 :         cmptype = for_overlaps ? COMPARE_OVERLAP : COMPARE_EQ;
   10391        3066 :         eqstrategy = IndexAmTranslateCompareType(cmptype, amid, opfamily, true);
   10392        3066 :         if (eqstrategy == InvalidStrategy)
   10393           0 :             ereport(ERROR,
   10394             :                     errcode(ERRCODE_UNDEFINED_OBJECT),
   10395             :                     for_overlaps
   10396             :                     ? errmsg("could not identify an overlaps operator for foreign key")
   10397             :                     : errmsg("could not identify an equality operator for foreign key"),
   10398             :                     errdetail("Could not translate compare type %d for operator family \"%s\" of access method \"%s\".",
   10399             :                               cmptype, get_opfamily_name(opfamily, false), get_am_name(amid)));
   10400             : 
   10401             :         /*
   10402             :          * There had better be a primary equality operator for the index.
   10403             :          * We'll use it for PK = PK comparisons.
   10404             :          */
   10405        3066 :         ppeqop = get_opfamily_member(opfamily, opcintype, opcintype,
   10406             :                                      eqstrategy);
   10407             : 
   10408        3066 :         if (!OidIsValid(ppeqop))
   10409           0 :             elog(ERROR, "missing operator %d(%u,%u) in opfamily %u",
   10410             :                  eqstrategy, opcintype, opcintype, opfamily);
   10411             : 
   10412             :         /*
   10413             :          * Are there equality operators that take exactly the FK type? Assume
   10414             :          * we should look through any domain here.
   10415             :          */
   10416        3066 :         fktyped = getBaseType(fktype);
   10417             : 
   10418        3066 :         pfeqop = get_opfamily_member(opfamily, opcintype, fktyped,
   10419             :                                      eqstrategy);
   10420        3066 :         if (OidIsValid(pfeqop))
   10421             :         {
   10422        2370 :             pfeqop_right = fktyped;
   10423        2370 :             ffeqop = get_opfamily_member(opfamily, fktyped, fktyped,
   10424             :                                          eqstrategy);
   10425             :         }
   10426             :         else
   10427             :         {
   10428             :             /* keep compiler quiet */
   10429         696 :             pfeqop_right = InvalidOid;
   10430         696 :             ffeqop = InvalidOid;
   10431             :         }
   10432             : 
   10433        3066 :         if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
   10434             :         {
   10435             :             /*
   10436             :              * Otherwise, look for an implicit cast from the FK type to the
   10437             :              * opcintype, and if found, use the primary equality operator.
   10438             :              * This is a bit tricky because opcintype might be a polymorphic
   10439             :              * type such as ANYARRAY or ANYENUM; so what we have to test is
   10440             :              * whether the two actual column types can be concurrently cast to
   10441             :              * that type.  (Otherwise, we'd fail to reject combinations such
   10442             :              * as int[] and point[].)
   10443             :              */
   10444             :             Oid         input_typeids[2];
   10445             :             Oid         target_typeids[2];
   10446             : 
   10447         696 :             input_typeids[0] = pktype;
   10448         696 :             input_typeids[1] = fktype;
   10449         696 :             target_typeids[0] = opcintype;
   10450         696 :             target_typeids[1] = opcintype;
   10451         696 :             if (can_coerce_type(2, input_typeids, target_typeids,
   10452             :                                 COERCION_IMPLICIT))
   10453             :             {
   10454         468 :                 pfeqop = ffeqop = ppeqop;
   10455         468 :                 pfeqop_right = opcintype;
   10456             :             }
   10457             :         }
   10458             : 
   10459        3066 :         if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
   10460         228 :             ereport(ERROR,
   10461             :                     (errcode(ERRCODE_DATATYPE_MISMATCH),
   10462             :                      errmsg("foreign key constraint \"%s\" cannot be implemented",
   10463             :                             fkconstraint->conname),
   10464             :                      errdetail("Key columns \"%s\" of the referencing table and \"%s\" of the referenced table "
   10465             :                                "are of incompatible types: %s and %s.",
   10466             :                                strVal(list_nth(fkconstraint->fk_attrs, i)),
   10467             :                                strVal(list_nth(fkconstraint->pk_attrs, i)),
   10468             :                                format_type_be(fktype),
   10469             :                                format_type_be(pktype))));
   10470             : 
   10471             :         /*
   10472             :          * This shouldn't be possible, but better check to make sure we have a
   10473             :          * consistent state for the check below.
   10474             :          */
   10475        2838 :         if ((OidIsValid(pkcoll) && !OidIsValid(fkcoll)) || (!OidIsValid(pkcoll) && OidIsValid(fkcoll)))
   10476           0 :             elog(ERROR, "key columns are not both collatable");
   10477             : 
   10478        2838 :         if (OidIsValid(pkcoll) && OidIsValid(fkcoll))
   10479             :         {
   10480             :             bool        pkcolldet;
   10481             :             bool        fkcolldet;
   10482             : 
   10483         104 :             pkcolldet = get_collation_isdeterministic(pkcoll);
   10484         104 :             fkcolldet = get_collation_isdeterministic(fkcoll);
   10485             : 
   10486             :             /*
   10487             :              * SQL requires that both collations are the same.  This is
   10488             :              * because we need a consistent notion of equality on both
   10489             :              * columns.  We relax this by allowing different collations if
   10490             :              * they are both deterministic.  (This is also for backward
   10491             :              * compatibility, because PostgreSQL has always allowed this.)
   10492             :              */
   10493         104 :             if ((!pkcolldet || !fkcolldet) && pkcoll != fkcoll)
   10494          12 :                 ereport(ERROR,
   10495             :                         (errcode(ERRCODE_COLLATION_MISMATCH),
   10496             :                          errmsg("foreign key constraint \"%s\" cannot be implemented", fkconstraint->conname),
   10497             :                          errdetail("Key columns \"%s\" of the referencing table and \"%s\" of the referenced table "
   10498             :                                    "have incompatible collations: \"%s\" and \"%s\".  "
   10499             :                                    "If either collation is nondeterministic, then both collations have to be the same.",
   10500             :                                    strVal(list_nth(fkconstraint->fk_attrs, i)),
   10501             :                                    strVal(list_nth(fkconstraint->pk_attrs, i)),
   10502             :                                    get_collation_name(fkcoll),
   10503             :                                    get_collation_name(pkcoll))));
   10504             :         }
   10505             : 
   10506        2826 :         if (old_check_ok)
   10507             :         {
   10508             :             /*
   10509             :              * When a pfeqop changes, revalidate the constraint.  We could
   10510             :              * permit intra-opfamily changes, but that adds subtle complexity
   10511             :              * without any concrete benefit for core types.  We need not
   10512             :              * assess ppeqop or ffeqop, which RI_Initial_Check() does not use.
   10513             :              */
   10514           6 :             old_check_ok = (pfeqop == lfirst_oid(old_pfeqop_item));
   10515           6 :             old_pfeqop_item = lnext(fkconstraint->old_conpfeqop,
   10516             :                                     old_pfeqop_item);
   10517             :         }
   10518        2826 :         if (old_check_ok)
   10519             :         {
   10520             :             Oid         old_fktype;
   10521             :             Oid         new_fktype;
   10522             :             CoercionPathType old_pathtype;
   10523             :             CoercionPathType new_pathtype;
   10524             :             Oid         old_castfunc;
   10525             :             Oid         new_castfunc;
   10526             :             Oid         old_fkcoll;
   10527             :             Oid         new_fkcoll;
   10528           6 :             Form_pg_attribute attr = TupleDescAttr(tab->oldDesc,
   10529           6 :                                                    fkattnum[i] - 1);
   10530             : 
   10531             :             /*
   10532             :              * Identify coercion pathways from each of the old and new FK-side
   10533             :              * column types to the right (foreign) operand type of the pfeqop.
   10534             :              * We may assume that pg_constraint.conkey is not changing.
   10535             :              */
   10536           6 :             old_fktype = attr->atttypid;
   10537           6 :             new_fktype = fktype;
   10538           6 :             old_pathtype = findFkeyCast(pfeqop_right, old_fktype,
   10539             :                                         &old_castfunc);
   10540           6 :             new_pathtype = findFkeyCast(pfeqop_right, new_fktype,
   10541             :                                         &new_castfunc);
   10542             : 
   10543           6 :             old_fkcoll = attr->attcollation;
   10544           6 :             new_fkcoll = fkcoll;
   10545             : 
   10546             :             /*
   10547             :              * Upon a change to the cast from the FK column to its pfeqop
   10548             :              * operand, revalidate the constraint.  For this evaluation, a
   10549             :              * binary coercion cast is equivalent to no cast at all.  While
   10550             :              * type implementors should design implicit casts with an eye
   10551             :              * toward consistency of operations like equality, we cannot
   10552             :              * assume here that they have done so.
   10553             :              *
   10554             :              * A function with a polymorphic argument could change behavior
   10555             :              * arbitrarily in response to get_fn_expr_argtype().  Therefore,
   10556             :              * when the cast destination is polymorphic, we only avoid
   10557             :              * revalidation if the input type has not changed at all.  Given
   10558             :              * just the core data types and operator classes, this requirement
   10559             :              * prevents no would-be optimizations.
   10560             :              *
   10561             :              * If the cast converts from a base type to a domain thereon, then
   10562             :              * that domain type must be the opcintype of the unique index.
   10563             :              * Necessarily, the primary key column must then be of the domain
   10564             :              * type.  Since the constraint was previously valid, all values on
   10565             :              * the foreign side necessarily exist on the primary side and in
   10566             :              * turn conform to the domain.  Consequently, we need not treat
   10567             :              * domains specially here.
   10568             :              *
   10569             :              * If the collation changes, revalidation is required, unless both
   10570             :              * collations are deterministic, because those share the same
   10571             :              * notion of equality (because texteq reduces to bitwise
   10572             :              * equality).
   10573             :              *
   10574             :              * We need not directly consider the PK type.  It's necessarily
   10575             :              * binary coercible to the opcintype of the unique index column,
   10576             :              * and ri_triggers.c will only deal with PK datums in terms of
   10577             :              * that opcintype.  Changing the opcintype also changes pfeqop.
   10578             :              */
   10579           6 :             old_check_ok = (new_pathtype == old_pathtype &&
   10580           6 :                             new_castfunc == old_castfunc &&
   10581           6 :                             (!IsPolymorphicType(pfeqop_right) ||
   10582          12 :                              new_fktype == old_fktype) &&
   10583           0 :                             (new_fkcoll == old_fkcoll ||
   10584           0 :                              (get_collation_isdeterministic(old_fkcoll) && get_collation_isdeterministic(new_fkcoll))));
   10585             :         }
   10586             : 
   10587        2826 :         pfeqoperators[i] = pfeqop;
   10588        2826 :         ppeqoperators[i] = ppeqop;
   10589        2826 :         ffeqoperators[i] = ffeqop;
   10590             :     }
   10591             : 
   10592             :     /*
   10593             :      * For FKs with PERIOD we need additional operators to check whether the
   10594             :      * referencing row's range is contained by the aggregated ranges of the
   10595             :      * referenced row(s). For rangetypes and multirangetypes this is
   10596             :      * fk.periodatt <@ range_agg(pk.periodatt). Those are the only types we
   10597             :      * support for now. FKs will look these up at "runtime", but we should
   10598             :      * make sure the lookup works here, even if we don't use the values.
   10599             :      */
   10600        2148 :     if (with_period)
   10601             :     {
   10602             :         Oid         periodoperoid;
   10603             :         Oid         aggedperiodoperoid;
   10604             :         Oid         intersectoperoid;
   10605             : 
   10606         188 :         FindFKPeriodOpers(opclasses[numpks - 1], &periodoperoid, &aggedperiodoperoid,
   10607             :                           &intersectoperoid);
   10608             :     }
   10609             : 
   10610             :     /* First, create the constraint catalog entry itself. */
   10611        2148 :     address = addFkConstraint(addFkBothSides,
   10612             :                               fkconstraint->conname, fkconstraint, rel, pkrel,
   10613             :                               indexOid,
   10614             :                               InvalidOid,   /* no parent constraint */
   10615             :                               numfks,
   10616             :                               pkattnum,
   10617             :                               fkattnum,
   10618             :                               pfeqoperators,
   10619             :                               ppeqoperators,
   10620             :                               ffeqoperators,
   10621             :                               numfkdelsetcols,
   10622             :                               fkdelsetcols,
   10623             :                               false,
   10624             :                               with_period);
   10625             : 
   10626             :     /* Next process the action triggers at the referenced side and recurse */
   10627        2148 :     addFkRecurseReferenced(fkconstraint, rel, pkrel,
   10628             :                            indexOid,
   10629             :                            address.objectId,
   10630             :                            numfks,
   10631             :                            pkattnum,
   10632             :                            fkattnum,
   10633             :                            pfeqoperators,
   10634             :                            ppeqoperators,
   10635             :                            ffeqoperators,
   10636             :                            numfkdelsetcols,
   10637             :                            fkdelsetcols,
   10638             :                            old_check_ok,
   10639             :                            InvalidOid, InvalidOid,
   10640             :                            with_period);
   10641             : 
   10642             :     /* Lastly create the check triggers at the referencing side and recurse */
   10643        2148 :     addFkRecurseReferencing(wqueue, fkconstraint, rel, pkrel,
   10644             :                             indexOid,
   10645             :                             address.objectId,
   10646             :                             numfks,
   10647             :                             pkattnum,
   10648             :                             fkattnum,
   10649             :                             pfeqoperators,
   10650             :                             ppeqoperators,
   10651             :                             ffeqoperators,
   10652             :                             numfkdelsetcols,
   10653             :                             fkdelsetcols,
   10654             :                             old_check_ok,
   10655             :                             lockmode,
   10656             :                             InvalidOid, InvalidOid,
   10657             :                             with_period);
   10658             : 
   10659             :     /*
   10660             :      * Done.  Close pk table, but keep lock until we've committed.
   10661             :      */
   10662        2148 :     table_close(pkrel, NoLock);
   10663             : 
   10664        2148 :     return address;
   10665             : }
   10666             : 
   10667             : /*
   10668             :  * validateFkOnDeleteSetColumns
   10669             :  *      Verifies that columns used in ON DELETE SET NULL/DEFAULT (...)
   10670             :  *      column lists are valid.
   10671             :  *
   10672             :  * If there are duplicates in the fksetcolsattnums[] array, this silently
   10673             :  * removes the dups.  The new count of numfksetcols is returned.
   10674             :  */
   10675             : static int
   10676        2622 : validateFkOnDeleteSetColumns(int numfks, const int16 *fkattnums,
   10677             :                              int numfksetcols, int16 *fksetcolsattnums,
   10678             :                              List *fksetcols)
   10679             : {
   10680        2622 :     int         numcolsout = 0;
   10681             : 
   10682        2652 :     for (int i = 0; i < numfksetcols; i++)
   10683             :     {
   10684          36 :         int16       setcol_attnum = fksetcolsattnums[i];
   10685          36 :         bool        seen = false;
   10686             : 
   10687             :         /* Make sure it's in fkattnums[] */
   10688          66 :         for (int j = 0; j < numfks; j++)
   10689             :         {
   10690          60 :             if (fkattnums[j] == setcol_attnum)
   10691             :             {
   10692          30 :                 seen = true;
   10693          30 :                 break;
   10694             :             }
   10695             :         }
   10696             : 
   10697          36 :         if (!seen)
   10698             :         {
   10699           6 :             char       *col = strVal(list_nth(fksetcols, i));
   10700             : 
   10701           6 :             ereport(ERROR,
   10702             :                     (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
   10703             :                      errmsg("column \"%s\" referenced in ON DELETE SET action must be part of foreign key", col)));
   10704             :         }
   10705             : 
   10706             :         /* Now check for dups */
   10707          30 :         seen = false;
   10708          30 :         for (int j = 0; j < numcolsout; j++)
   10709             :         {
   10710           6 :             if (fksetcolsattnums[j] == setcol_attnum)
   10711             :             {
   10712           6 :                 seen = true;
   10713           6 :                 break;
   10714             :             }
   10715             :         }
   10716          30 :         if (!seen)
   10717          24 :             fksetcolsattnums[numcolsout++] = setcol_attnum;
   10718             :     }
   10719        2616 :     return numcolsout;
   10720             : }
   10721             : 
   10722             : /*
   10723             :  * addFkConstraint
   10724             :  *      Install pg_constraint entries to implement a foreign key constraint.
   10725             :  *      Caller must separately invoke addFkRecurseReferenced and
   10726             :  *      addFkRecurseReferencing, as appropriate, to install pg_trigger entries
   10727             :  *      and (for partitioned tables) recurse to partitions.
   10728             :  *
   10729             :  * fkside: the side of the FK (or both) to create.  Caller should
   10730             :  *      call addFkRecurseReferenced if this is addFkReferencedSide,
   10731             :  *      addFkRecurseReferencing if it's addFkReferencingSide, or both if it's
   10732             :  *      addFkBothSides.
   10733             :  * constraintname: the base name for the constraint being added,
   10734             :  *      copied to fkconstraint->conname if the latter is not set
   10735             :  * fkconstraint: the constraint being added
   10736             :  * rel: the root referencing relation
   10737             :  * pkrel: the referenced relation; might be a partition, if recursing
   10738             :  * indexOid: the OID of the index (on pkrel) implementing this constraint
   10739             :  * parentConstr: the OID of a parent constraint; InvalidOid if this is a
   10740             :  *      top-level constraint
   10741             :  * numfks: the number of columns in the foreign key
   10742             :  * pkattnum: the attnum array of referenced attributes
   10743             :  * fkattnum: the attnum array of referencing attributes
   10744             :  * pf/pp/ffeqoperators: OID array of operators between columns
   10745             :  * numfkdelsetcols: the number of columns in the ON DELETE SET NULL/DEFAULT
   10746             :  *      (...) clause
   10747             :  * fkdelsetcols: the attnum array of the columns in the ON DELETE SET
   10748             :  *      NULL/DEFAULT clause
   10749             :  * with_period: true if this is a temporal FK
   10750             :  */
   10751             : static ObjectAddress
   10752        4226 : addFkConstraint(addFkConstraintSides fkside,
   10753             :                 char *constraintname, Constraint *fkconstraint,
   10754             :                 Relation rel, Relation pkrel, Oid indexOid, Oid parentConstr,
   10755             :                 int numfks, int16 *pkattnum,
   10756             :                 int16 *fkattnum, Oid *pfeqoperators, Oid *ppeqoperators,
   10757             :                 Oid *ffeqoperators, int numfkdelsetcols, int16 *fkdelsetcols,
   10758             :                 bool is_internal, bool with_period)
   10759             : {
   10760             :     ObjectAddress address;
   10761             :     Oid         constrOid;
   10762             :     char       *conname;
   10763             :     bool        conislocal;
   10764             :     int16       coninhcount;
   10765             :     bool        connoinherit;
   10766             : 
   10767             :     /*
   10768             :      * Verify relkind for each referenced partition.  At the top level, this
   10769             :      * is redundant with a previous check, but we need it when recursing.
   10770             :      */
   10771        4226 :     if (pkrel->rd_rel->relkind != RELKIND_RELATION &&
   10772         886 :         pkrel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
   10773           0 :         ereport(ERROR,
   10774             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   10775             :                  errmsg("referenced relation \"%s\" is not a table",
   10776             :                         RelationGetRelationName(pkrel))));
   10777             : 
   10778             :     /*
   10779             :      * Caller supplies us with a constraint name; however, it may be used in
   10780             :      * this partition, so come up with a different one in that case.  Unless
   10781             :      * truncation to NAMEDATALEN dictates otherwise, the new name will be the
   10782             :      * supplied name with an underscore and digit(s) appended.
   10783             :      */
   10784        4226 :     if (ConstraintNameIsUsed(CONSTRAINT_RELATION,
   10785             :                              RelationGetRelid(rel),
   10786             :                              constraintname))
   10787        1242 :         conname = ChooseConstraintName(constraintname,
   10788             :                                        NULL,
   10789             :                                        "",
   10790        1242 :                                        RelationGetNamespace(rel), NIL);
   10791             :     else
   10792        2984 :         conname = constraintname;
   10793             : 
   10794        4226 :     if (fkconstraint->conname == NULL)
   10795         472 :         fkconstraint->conname = pstrdup(conname);
   10796             : 
   10797        4226 :     if (OidIsValid(parentConstr))
   10798             :     {
   10799        2078 :         conislocal = false;
   10800        2078 :         coninhcount = 1;
   10801        2078 :         connoinherit = false;
   10802             :     }
   10803             :     else
   10804             :     {
   10805        2148 :         conislocal = true;
   10806        2148 :         coninhcount = 0;
   10807             : 
   10808             :         /*
   10809             :          * always inherit for partitioned tables, never for legacy inheritance
   10810             :          */
   10811        2148 :         connoinherit = rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE;
   10812             :     }
   10813             : 
   10814             :     /*
   10815             :      * Record the FK constraint in pg_constraint.
   10816             :      */
   10817        4226 :     constrOid = CreateConstraintEntry(conname,
   10818        4226 :                                       RelationGetNamespace(rel),
   10819             :                                       CONSTRAINT_FOREIGN,
   10820        4226 :                                       fkconstraint->deferrable,
   10821        4226 :                                       fkconstraint->initdeferred,
   10822        4226 :                                       fkconstraint->is_enforced,
   10823        4226 :                                       fkconstraint->initially_valid,
   10824             :                                       parentConstr,
   10825             :                                       RelationGetRelid(rel),
   10826             :                                       fkattnum,
   10827             :                                       numfks,
   10828             :                                       numfks,
   10829             :                                       InvalidOid,   /* not a domain constraint */
   10830             :                                       indexOid,
   10831             :                                       RelationGetRelid(pkrel),
   10832             :                                       pkattnum,
   10833             :                                       pfeqoperators,
   10834             :                                       ppeqoperators,
   10835             :                                       ffeqoperators,
   10836             :                                       numfks,
   10837        4226 :                                       fkconstraint->fk_upd_action,
   10838        4226 :                                       fkconstraint->fk_del_action,
   10839             :                                       fkdelsetcols,
   10840             :                                       numfkdelsetcols,
   10841        4226 :                                       fkconstraint->fk_matchtype,
   10842             :                                       NULL, /* no exclusion constraint */
   10843             :                                       NULL, /* no check constraint */
   10844             :                                       NULL,
   10845             :                                       conislocal,   /* islocal */
   10846             :                                       coninhcount,  /* inhcount */
   10847             :                                       connoinherit, /* conNoInherit */
   10848             :                                       with_period,  /* conPeriod */
   10849             :                                       is_internal); /* is_internal */
   10850             : 
   10851        4226 :     ObjectAddressSet(address, ConstraintRelationId, constrOid);
   10852             : 
   10853             :     /*
   10854             :      * In partitioning cases, create the dependency entries for this
   10855             :      * constraint.  (For non-partitioned cases, relevant entries were created
   10856             :      * by CreateConstraintEntry.)
   10857             :      *
   10858             :      * On the referenced side, we need the constraint to have an internal
   10859             :      * dependency on its parent constraint; this means that this constraint
   10860             :      * cannot be dropped on its own -- only through the parent constraint. It
   10861             :      * also means the containing partition cannot be dropped on its own, but
   10862             :      * it can be detached, at which point this dependency is removed (after
   10863             :      * verifying that no rows are referenced via this FK.)
   10864             :      *
   10865             :      * When processing the referencing side, we link the constraint via the
   10866             :      * special partitioning dependencies: the parent constraint is the primary
   10867             :      * dependent, and the partition on which the foreign key exists is the
   10868             :      * secondary dependency.  That way, this constraint is dropped if either
   10869             :      * of these objects is.
   10870             :      *
   10871             :      * Note that this is only necessary for the subsidiary pg_constraint rows
   10872             :      * in partitions; the topmost row doesn't need any of this.
   10873             :      */
   10874        4226 :     if (OidIsValid(parentConstr))
   10875             :     {
   10876             :         ObjectAddress referenced;
   10877             : 
   10878        2078 :         ObjectAddressSet(referenced, ConstraintRelationId, parentConstr);
   10879             : 
   10880             :         Assert(fkside != addFkBothSides);
   10881        2078 :         if (fkside == addFkReferencedSide)
   10882        1236 :             recordDependencyOn(&address, &referenced, DEPENDENCY_INTERNAL);
   10883             :         else
   10884             :         {
   10885         842 :             recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_PRI);
   10886         842 :             ObjectAddressSet(referenced, RelationRelationId, RelationGetRelid(rel));
   10887         842 :             recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_SEC);
   10888             :         }
   10889             :     }
   10890             : 
   10891             :     /* make new constraint visible, in case we add more */
   10892        4226 :     CommandCounterIncrement();
   10893             : 
   10894        4226 :     return address;
   10895             : }
   10896             : 
   10897             : /*
   10898             :  * addFkRecurseReferenced
   10899             :  *      Recursive helper for the referenced side of foreign key creation,
   10900             :  *      which creates the action triggers and recurses
   10901             :  *
   10902             :  * If the referenced relation is a plain relation, create the necessary action
   10903             :  * triggers that implement the constraint.  If the referenced relation is a
   10904             :  * partitioned table, then we create a pg_constraint row referencing the parent
   10905             :  * of the referencing side for it and recurse on this routine for each
   10906             :  * partition.
   10907             :  *
   10908             :  * fkconstraint: the constraint being added
   10909             :  * rel: the root referencing relation
   10910             :  * pkrel: the referenced relation; might be a partition, if recursing
   10911             :  * indexOid: the OID of the index (on pkrel) implementing this constraint
   10912             :  * parentConstr: the OID of a parent constraint; InvalidOid if this is a
   10913             :  *      top-level constraint
   10914             :  * numfks: the number of columns in the foreign key
   10915             :  * pkattnum: the attnum array of referenced attributes
   10916             :  * fkattnum: the attnum array of referencing attributes
   10917             :  * numfkdelsetcols: the number of columns in the ON DELETE SET
   10918             :  *      NULL/DEFAULT (...) clause
   10919             :  * fkdelsetcols: the attnum array of the columns in the ON DELETE SET
   10920             :  *      NULL/DEFAULT clause
   10921             :  * pf/pp/ffeqoperators: OID array of operators between columns
   10922             :  * old_check_ok: true if this constraint replaces an existing one that
   10923             :  *      was already validated (thus this one doesn't need validation)
   10924             :  * parentDelTrigger and parentUpdTrigger: when recursively called on a
   10925             :  *      partition, the OIDs of the parent action triggers for DELETE and
   10926             :  *      UPDATE respectively.
   10927             :  * with_period: true if this is a temporal FK
   10928             :  */
   10929             : static void
   10930        3492 : addFkRecurseReferenced(Constraint *fkconstraint, Relation rel,
   10931             :                        Relation pkrel, Oid indexOid, Oid parentConstr,
   10932             :                        int numfks,
   10933             :                        int16 *pkattnum, int16 *fkattnum, Oid *pfeqoperators,
   10934             :                        Oid *ppeqoperators, Oid *ffeqoperators,
   10935             :                        int numfkdelsetcols, int16 *fkdelsetcols,
   10936             :                        bool old_check_ok,
   10937             :                        Oid parentDelTrigger, Oid parentUpdTrigger,
   10938             :                        bool with_period)
   10939             : {
   10940        3492 :     Oid         deleteTriggerOid = InvalidOid,
   10941        3492 :                 updateTriggerOid = InvalidOid;
   10942             : 
   10943             :     Assert(CheckRelationLockedByMe(pkrel, ShareRowExclusiveLock, true));
   10944             :     Assert(CheckRelationLockedByMe(rel, ShareRowExclusiveLock, true));
   10945             : 
   10946             :     /*
   10947             :      * Create action triggers to enforce the constraint, or skip them if the
   10948             :      * constraint is NOT ENFORCED.
   10949             :      */
   10950        3492 :     if (fkconstraint->is_enforced)
   10951        3420 :         createForeignKeyActionTriggers(RelationGetRelid(rel),
   10952             :                                        RelationGetRelid(pkrel),
   10953             :                                        fkconstraint,
   10954             :                                        parentConstr, indexOid,
   10955             :                                        parentDelTrigger, parentUpdTrigger,
   10956             :                                        &deleteTriggerOid, &updateTriggerOid);
   10957             : 
   10958             :     /*
   10959             :      * If the referenced table is partitioned, recurse on ourselves to handle
   10960             :      * each partition.  We need one pg_constraint row created for each
   10961             :      * partition in addition to the pg_constraint row for the parent table.
   10962             :      */
   10963        3492 :     if (pkrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
   10964             :     {
   10965         576 :         PartitionDesc pd = RelationGetPartitionDesc(pkrel, true);
   10966             : 
   10967        1548 :         for (int i = 0; i < pd->nparts; i++)
   10968             :         {
   10969             :             Relation    partRel;
   10970             :             AttrMap    *map;
   10971             :             AttrNumber *mapped_pkattnum;
   10972             :             Oid         partIndexId;
   10973             :             ObjectAddress address;
   10974             : 
   10975             :             /* XXX would it be better to acquire these locks beforehand? */
   10976         972 :             partRel = table_open(pd->oids[i], ShareRowExclusiveLock);
   10977             : 
   10978             :             /*
   10979             :              * Map the attribute numbers in the referenced side of the FK
   10980             :              * definition to match the partition's column layout.
   10981             :              */
   10982         972 :             map = build_attrmap_by_name_if_req(RelationGetDescr(partRel),
   10983             :                                                RelationGetDescr(pkrel),
   10984             :                                                false);
   10985         972 :             if (map)
   10986             :             {
   10987         136 :                 mapped_pkattnum = palloc_array(AttrNumber, numfks);
   10988         284 :                 for (int j = 0; j < numfks; j++)
   10989         148 :                     mapped_pkattnum[j] = map->attnums[pkattnum[j] - 1];
   10990             :             }
   10991             :             else
   10992         836 :                 mapped_pkattnum = pkattnum;
   10993             : 
   10994             :             /* Determine the index to use at this level */
   10995         972 :             partIndexId = index_get_partition(partRel, indexOid);
   10996         972 :             if (!OidIsValid(partIndexId))
   10997           0 :                 elog(ERROR, "index for %u not found in partition %s",
   10998             :                      indexOid, RelationGetRelationName(partRel));
   10999             : 
   11000             :             /* Create entry at this level ... */
   11001         972 :             address = addFkConstraint(addFkReferencedSide,
   11002             :                                       fkconstraint->conname, fkconstraint, rel,
   11003             :                                       partRel, partIndexId, parentConstr,
   11004             :                                       numfks, mapped_pkattnum,
   11005             :                                       fkattnum, pfeqoperators, ppeqoperators,
   11006             :                                       ffeqoperators, numfkdelsetcols,
   11007             :                                       fkdelsetcols, true, with_period);
   11008             :             /* ... and recurse to our children */
   11009         972 :             addFkRecurseReferenced(fkconstraint, rel, partRel,
   11010             :                                    partIndexId, address.objectId, numfks,
   11011             :                                    mapped_pkattnum, fkattnum,
   11012             :                                    pfeqoperators, ppeqoperators, ffeqoperators,
   11013             :                                    numfkdelsetcols, fkdelsetcols,
   11014             :                                    old_check_ok,
   11015             :                                    deleteTriggerOid, updateTriggerOid,
   11016             :                                    with_period);
   11017             : 
   11018             :             /* Done -- clean up (but keep the lock) */
   11019         972 :             table_close(partRel, NoLock);
   11020         972 :             if (map)
   11021             :             {
   11022         136 :                 pfree(mapped_pkattnum);
   11023         136 :                 free_attrmap(map);
   11024             :             }
   11025             :         }
   11026             :     }
   11027        3492 : }
   11028             : 
   11029             : /*
   11030             :  * addFkRecurseReferencing
   11031             :  *      Recursive helper for the referencing side of foreign key creation,
   11032             :  *      which creates the check triggers and recurses
   11033             :  *
   11034             :  * If the referencing relation is a plain relation, create the necessary check
   11035             :  * triggers that implement the constraint, and set up for Phase 3 constraint
   11036             :  * verification.  If the referencing relation is a partitioned table, then
   11037             :  * we create a pg_constraint row for it and recurse on this routine for each
   11038             :  * partition.
   11039             :  *
   11040             :  * We assume that the referenced relation is locked against concurrent
   11041             :  * deletions.  If it's a partitioned relation, every partition must be so
   11042             :  * locked.
   11043             :  *
   11044             :  * wqueue: the ALTER TABLE work queue; NULL when not running as part
   11045             :  *      of an ALTER TABLE sequence.
   11046             :  * fkconstraint: the constraint being added
   11047             :  * rel: the referencing relation; might be a partition, if recursing
   11048             :  * pkrel: the root referenced relation
   11049             :  * indexOid: the OID of the index (on pkrel) implementing this constraint
   11050             :  * parentConstr: the OID of the parent constraint (there is always one)
   11051             :  * numfks: the number of columns in the foreign key
   11052             :  * pkattnum: the attnum array of referenced attributes
   11053             :  * fkattnum: the attnum array of referencing attributes
   11054             :  * pf/pp/ffeqoperators: OID array of operators between columns
   11055             :  * numfkdelsetcols: the number of columns in the ON DELETE SET NULL/DEFAULT
   11056             :  *      (...) clause
   11057             :  * fkdelsetcols: the attnum array of the columns in the ON DELETE SET
   11058             :  *      NULL/DEFAULT clause
   11059             :  * old_check_ok: true if this constraint replaces an existing one that
   11060             :  *      was already validated (thus this one doesn't need validation)
   11061             :  * lockmode: the lockmode to acquire on partitions when recursing
   11062             :  * parentInsTrigger and parentUpdTrigger: when being recursively called on
   11063             :  *      a partition, the OIDs of the parent check triggers for INSERT and
   11064             :  *      UPDATE respectively.
   11065             :  * with_period: true if this is a temporal FK
   11066             :  */
   11067             : static void
   11068        2990 : addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint, Relation rel,
   11069             :                         Relation pkrel, Oid indexOid, Oid parentConstr,
   11070             :                         int numfks, int16 *pkattnum, int16 *fkattnum,
   11071             :                         Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators,
   11072             :                         int numfkdelsetcols, int16 *fkdelsetcols,
   11073             :                         bool old_check_ok, LOCKMODE lockmode,
   11074             :                         Oid parentInsTrigger, Oid parentUpdTrigger,
   11075             :                         bool with_period)
   11076             : {
   11077        2990 :     Oid         insertTriggerOid = InvalidOid,
   11078        2990 :                 updateTriggerOid = InvalidOid;
   11079             : 
   11080             :     Assert(OidIsValid(parentConstr));
   11081             :     Assert(CheckRelationLockedByMe(rel, ShareRowExclusiveLock, true));
   11082             :     Assert(CheckRelationLockedByMe(pkrel, ShareRowExclusiveLock, true));
   11083             : 
   11084        2990 :     if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
   11085           0 :         ereport(ERROR,
   11086             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   11087             :                  errmsg("foreign key constraints are not supported on foreign tables")));
   11088             : 
   11089             :     /*
   11090             :      * Add check triggers if the constraint is ENFORCED, and if needed,
   11091             :      * schedule them to be checked in Phase 3.
   11092             :      *
   11093             :      * If the relation is partitioned, drill down to do it to its partitions.
   11094             :      */
   11095        2990 :     if (fkconstraint->is_enforced)
   11096        2942 :         createForeignKeyCheckTriggers(RelationGetRelid(rel),
   11097             :                                       RelationGetRelid(pkrel),
   11098             :                                       fkconstraint,
   11099             :                                       parentConstr,
   11100             :                                       indexOid,
   11101             :                                       parentInsTrigger, parentUpdTrigger,
   11102             :                                       &insertTriggerOid, &updateTriggerOid);
   11103             : 
   11104        2990 :     if (rel->rd_rel->relkind == RELKIND_RELATION)
   11105             :     {
   11106             :         /*
   11107             :          * Tell Phase 3 to check that the constraint is satisfied by existing
   11108             :          * rows. We can skip this during table creation, when constraint is
   11109             :          * specified as NOT ENFORCED, or when requested explicitly by
   11110             :          * specifying NOT VALID in an ADD FOREIGN KEY command, and when we're
   11111             :          * recreating a constraint following a SET DATA TYPE operation that
   11112             :          * did not impugn its validity.
   11113             :          */
   11114        2500 :         if (wqueue && !old_check_ok && !fkconstraint->skip_validation &&
   11115         772 :             fkconstraint->is_enforced)
   11116             :         {
   11117             :             NewConstraint *newcon;
   11118             :             AlteredTableInfo *tab;
   11119             : 
   11120         772 :             tab = ATGetQueueEntry(wqueue, rel);
   11121             : 
   11122         772 :             newcon = palloc0_object(NewConstraint);
   11123         772 :             newcon->name = get_constraint_name(parentConstr);
   11124         772 :             newcon->contype = CONSTR_FOREIGN;
   11125         772 :             newcon->refrelid = RelationGetRelid(pkrel);
   11126         772 :             newcon->refindid = indexOid;
   11127         772 :             newcon->conid = parentConstr;
   11128         772 :             newcon->conwithperiod = fkconstraint->fk_with_period;
   11129         772 :             newcon->qual = (Node *) fkconstraint;
   11130             : 
   11131         772 :             tab->constraints = lappend(tab->constraints, newcon);
   11132             :         }
   11133             :     }
   11134         490 :     else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
   11135             :     {
   11136         490 :         PartitionDesc pd = RelationGetPartitionDesc(rel, true);
   11137             :         Relation    trigrel;
   11138             : 
   11139             :         /*
   11140             :          * Triggers of the foreign keys will be manipulated a bunch of times
   11141             :          * in the loop below.  To avoid repeatedly opening/closing the trigger
   11142             :          * catalog relation, we open it here and pass it to the subroutines
   11143             :          * called below.
   11144             :          */
   11145         490 :         trigrel = table_open(TriggerRelationId, RowExclusiveLock);
   11146             : 
   11147             :         /*
   11148             :          * Recurse to take appropriate action on each partition; either we
   11149             :          * find an existing constraint to reparent to ours, or we create a new
   11150             :          * one.
   11151             :          */
   11152         872 :         for (int i = 0; i < pd->nparts; i++)
   11153             :         {
   11154         388 :             Relation    partition = table_open(pd->oids[i], lockmode);
   11155             :             List       *partFKs;
   11156             :             AttrMap    *attmap;
   11157             :             AttrNumber  mapped_fkattnum[INDEX_MAX_KEYS];
   11158             :             bool        attached;
   11159             :             ObjectAddress address;
   11160             : 
   11161         388 :             CheckAlterTableIsSafe(partition);
   11162             : 
   11163         382 :             attmap = build_attrmap_by_name(RelationGetDescr(partition),
   11164             :                                            RelationGetDescr(rel),
   11165             :                                            false);
   11166         986 :             for (int j = 0; j < numfks; j++)
   11167         604 :                 mapped_fkattnum[j] = attmap->attnums[fkattnum[j] - 1];
   11168             : 
   11169             :             /* Check whether an existing constraint can be repurposed */
   11170         382 :             partFKs = copyObject(RelationGetFKeyList(partition));
   11171         382 :             attached = false;
   11172         782 :             foreach_node(ForeignKeyCacheInfo, fk, partFKs)
   11173             :             {
   11174          30 :                 if (tryAttachPartitionForeignKey(wqueue,
   11175             :                                                  fk,
   11176             :                                                  partition,
   11177             :                                                  parentConstr,
   11178             :                                                  numfks,
   11179             :                                                  mapped_fkattnum,
   11180             :                                                  pkattnum,
   11181             :                                                  pfeqoperators,
   11182             :                                                  insertTriggerOid,
   11183             :                                                  updateTriggerOid,
   11184             :                                                  trigrel))
   11185             :                 {
   11186          12 :                     attached = true;
   11187          12 :                     break;
   11188             :                 }
   11189             :             }
   11190         382 :             if (attached)
   11191             :             {
   11192          12 :                 table_close(partition, NoLock);
   11193          12 :                 continue;
   11194             :             }
   11195             : 
   11196             :             /*
   11197             :              * No luck finding a good constraint to reuse; create our own.
   11198             :              */
   11199         370 :             address = addFkConstraint(addFkReferencingSide,
   11200             :                                       fkconstraint->conname, fkconstraint,
   11201             :                                       partition, pkrel, indexOid, parentConstr,
   11202             :                                       numfks, pkattnum,
   11203             :                                       mapped_fkattnum, pfeqoperators,
   11204             :                                       ppeqoperators, ffeqoperators,
   11205             :                                       numfkdelsetcols, fkdelsetcols, true,
   11206             :                                       with_period);
   11207             : 
   11208             :             /* call ourselves to finalize the creation and we're done */
   11209         370 :             addFkRecurseReferencing(wqueue, fkconstraint, partition, pkrel,
   11210             :                                     indexOid,
   11211             :                                     address.objectId,
   11212             :                                     numfks,
   11213             :                                     pkattnum,
   11214             :                                     mapped_fkattnum,
   11215             :                                     pfeqoperators,
   11216             :                                     ppeqoperators,
   11217             :                                     ffeqoperators,
   11218             :                                     numfkdelsetcols,
   11219             :                                     fkdelsetcols,
   11220             :                                     old_check_ok,
   11221             :                                     lockmode,
   11222             :                                     insertTriggerOid,
   11223             :                                     updateTriggerOid,
   11224             :                                     with_period);
   11225             : 
   11226         370 :             table_close(partition, NoLock);
   11227             :         }
   11228             : 
   11229         484 :         table_close(trigrel, RowExclusiveLock);
   11230             :     }
   11231        2984 : }
   11232             : 
   11233             : /*
   11234             :  * CloneForeignKeyConstraints
   11235             :  *      Clone foreign keys from a partitioned table to a newly acquired
   11236             :  *      partition.
   11237             :  *
   11238             :  * partitionRel is a partition of parentRel, so we can be certain that it has
   11239             :  * the same columns with the same datatypes.  The columns may be in different
   11240             :  * order, though.
   11241             :  *
   11242             :  * wqueue must be passed to set up phase 3 constraint checking, unless the
   11243             :  * referencing-side partition is known to be empty (such as in CREATE TABLE /
   11244             :  * PARTITION OF).
   11245             :  */
   11246             : static void
   11247       11548 : CloneForeignKeyConstraints(List **wqueue, Relation parentRel,
   11248             :                            Relation partitionRel)
   11249             : {
   11250             :     /* This only works for declarative partitioning */
   11251             :     Assert(parentRel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
   11252             : 
   11253             :     /*
   11254             :      * First, clone constraints where the parent is on the referencing side.
   11255             :      */
   11256       11548 :     CloneFkReferencing(wqueue, parentRel, partitionRel);
   11257             : 
   11258             :     /*
   11259             :      * Clone constraints for which the parent is on the referenced side.
   11260             :      */
   11261       11530 :     CloneFkReferenced(parentRel, partitionRel);
   11262       11530 : }
   11263             : 
   11264             : /*
   11265             :  * CloneFkReferenced
   11266             :  *      Subroutine for CloneForeignKeyConstraints
   11267             :  *
   11268             :  * Find all the FKs that have the parent relation on the referenced side;
   11269             :  * clone those constraints to the given partition.  This is to be called
   11270             :  * when the partition is being created or attached.
   11271             :  *
   11272             :  * This recurses to partitions, if the relation being attached is partitioned.
   11273             :  * Recursion is done by calling addFkRecurseReferenced.
   11274             :  */
   11275             : static void
   11276       11530 : CloneFkReferenced(Relation parentRel, Relation partitionRel)
   11277             : {
   11278             :     Relation    pg_constraint;
   11279             :     AttrMap    *attmap;
   11280             :     ListCell   *cell;
   11281             :     SysScanDesc scan;
   11282             :     ScanKeyData key[2];
   11283             :     HeapTuple   tuple;
   11284       11530 :     List       *clone = NIL;
   11285             :     Relation    trigrel;
   11286             : 
   11287             :     /*
   11288             :      * Search for any constraints where this partition's parent is in the
   11289             :      * referenced side.  However, we must not clone any constraint whose
   11290             :      * parent constraint is also going to be cloned, to avoid duplicates.  So
   11291             :      * do it in two steps: first construct the list of constraints to clone,
   11292             :      * then go over that list cloning those whose parents are not in the list.
   11293             :      * (We must not rely on the parent being seen first, since the catalog
   11294             :      * scan could return children first.)
   11295             :      */
   11296       11530 :     pg_constraint = table_open(ConstraintRelationId, RowShareLock);
   11297       11530 :     ScanKeyInit(&key[0],
   11298             :                 Anum_pg_constraint_confrelid, BTEqualStrategyNumber,
   11299             :                 F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(parentRel)));
   11300       11530 :     ScanKeyInit(&key[1],
   11301             :                 Anum_pg_constraint_contype, BTEqualStrategyNumber,
   11302             :                 F_CHAREQ, CharGetDatum(CONSTRAINT_FOREIGN));
   11303             :     /* This is a seqscan, as we don't have a usable index ... */
   11304       11530 :     scan = systable_beginscan(pg_constraint, InvalidOid, true,
   11305             :                               NULL, 2, key);
   11306       12016 :     while ((tuple = systable_getnext(scan)) != NULL)
   11307             :     {
   11308         486 :         Form_pg_constraint constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
   11309             : 
   11310         486 :         clone = lappend_oid(clone, constrForm->oid);
   11311             :     }
   11312       11530 :     systable_endscan(scan);
   11313       11530 :     table_close(pg_constraint, RowShareLock);
   11314             : 
   11315             :     /*
   11316             :      * Triggers of the foreign keys will be manipulated a bunch of times in
   11317             :      * the loop below.  To avoid repeatedly opening/closing the trigger
   11318             :      * catalog relation, we open it here and pass it to the subroutines called
   11319             :      * below.
   11320             :      */
   11321       11530 :     trigrel = table_open(TriggerRelationId, RowExclusiveLock);
   11322             : 
   11323       11530 :     attmap = build_attrmap_by_name(RelationGetDescr(partitionRel),
   11324             :                                    RelationGetDescr(parentRel),
   11325             :                                    false);
   11326       12016 :     foreach(cell, clone)
   11327             :     {
   11328         486 :         Oid         constrOid = lfirst_oid(cell);
   11329             :         Form_pg_constraint constrForm;
   11330             :         Relation    fkRel;
   11331             :         Oid         indexOid;
   11332             :         Oid         partIndexId;
   11333             :         int         numfks;
   11334             :         AttrNumber  conkey[INDEX_MAX_KEYS];
   11335             :         AttrNumber  mapped_confkey[INDEX_MAX_KEYS];
   11336             :         AttrNumber  confkey[INDEX_MAX_KEYS];
   11337             :         Oid         conpfeqop[INDEX_MAX_KEYS];
   11338             :         Oid         conppeqop[INDEX_MAX_KEYS];
   11339             :         Oid         conffeqop[INDEX_MAX_KEYS];
   11340             :         int         numfkdelsetcols;
   11341             :         AttrNumber  confdelsetcols[INDEX_MAX_KEYS];
   11342             :         Constraint *fkconstraint;
   11343             :         ObjectAddress address;
   11344         486 :         Oid         deleteTriggerOid = InvalidOid,
   11345         486 :                     updateTriggerOid = InvalidOid;
   11346             : 
   11347         486 :         tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constrOid));
   11348         486 :         if (!HeapTupleIsValid(tuple))
   11349           0 :             elog(ERROR, "cache lookup failed for constraint %u", constrOid);
   11350         486 :         constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
   11351             : 
   11352             :         /*
   11353             :          * As explained above: don't try to clone a constraint for which we're
   11354             :          * going to clone the parent.
   11355             :          */
   11356         486 :         if (list_member_oid(clone, constrForm->conparentid))
   11357             :         {
   11358         222 :             ReleaseSysCache(tuple);
   11359         222 :             continue;
   11360             :         }
   11361             : 
   11362             :         /* We need the same lock level that CreateTrigger will acquire */
   11363         264 :         fkRel = table_open(constrForm->conrelid, ShareRowExclusiveLock);
   11364             : 
   11365         264 :         indexOid = constrForm->conindid;
   11366         264 :         DeconstructFkConstraintRow(tuple,
   11367             :                                    &numfks,
   11368             :                                    conkey,
   11369             :                                    confkey,
   11370             :                                    conpfeqop,
   11371             :                                    conppeqop,
   11372             :                                    conffeqop,
   11373             :                                    &numfkdelsetcols,
   11374             :                                    confdelsetcols);
   11375             : 
   11376         570 :         for (int i = 0; i < numfks; i++)
   11377         306 :             mapped_confkey[i] = attmap->attnums[confkey[i] - 1];
   11378             : 
   11379         264 :         fkconstraint = makeNode(Constraint);
   11380         264 :         fkconstraint->contype = CONSTRAINT_FOREIGN;
   11381         264 :         fkconstraint->conname = NameStr(constrForm->conname);
   11382         264 :         fkconstraint->deferrable = constrForm->condeferrable;
   11383         264 :         fkconstraint->initdeferred = constrForm->condeferred;
   11384         264 :         fkconstraint->location = -1;
   11385         264 :         fkconstraint->pktable = NULL;
   11386             :         /* ->fk_attrs determined below */
   11387         264 :         fkconstraint->pk_attrs = NIL;
   11388         264 :         fkconstraint->fk_matchtype = constrForm->confmatchtype;
   11389         264 :         fkconstraint->fk_upd_action = constrForm->confupdtype;
   11390         264 :         fkconstraint->fk_del_action = constrForm->confdeltype;
   11391         264 :         fkconstraint->fk_del_set_cols = NIL;
   11392         264 :         fkconstraint->old_conpfeqop = NIL;
   11393         264 :         fkconstraint->old_pktable_oid = InvalidOid;
   11394         264 :         fkconstraint->is_enforced = constrForm->conenforced;
   11395         264 :         fkconstraint->skip_validation = false;
   11396         264 :         fkconstraint->initially_valid = constrForm->convalidated;
   11397             : 
   11398             :         /* set up colnames that are used to generate the constraint name */
   11399         570 :         for (int i = 0; i < numfks; i++)
   11400             :         {
   11401             :             Form_pg_attribute att;
   11402             : 
   11403         306 :             att = TupleDescAttr(RelationGetDescr(fkRel),
   11404         306 :                                 conkey[i] - 1);
   11405         306 :             fkconstraint->fk_attrs = lappend(fkconstraint->fk_attrs,
   11406         306 :                                              makeString(NameStr(att->attname)));
   11407             :         }
   11408             : 
   11409             :         /*
   11410             :          * Add the new foreign key constraint pointing to the new partition.
   11411             :          * Because this new partition appears in the referenced side of the
   11412             :          * constraint, we don't need to set up for Phase 3 check.
   11413             :          */
   11414         264 :         partIndexId = index_get_partition(partitionRel, indexOid);
   11415         264 :         if (!OidIsValid(partIndexId))
   11416           0 :             elog(ERROR, "index for %u not found in partition %s",
   11417             :                  indexOid, RelationGetRelationName(partitionRel));
   11418             : 
   11419             :         /*
   11420             :          * Get the "action" triggers belonging to the constraint to pass as
   11421             :          * parent OIDs for similar triggers that will be created on the
   11422             :          * partition in addFkRecurseReferenced().
   11423             :          */
   11424         264 :         if (constrForm->conenforced)
   11425         258 :             GetForeignKeyActionTriggers(trigrel, constrOid,
   11426             :                                         constrForm->confrelid, constrForm->conrelid,
   11427             :                                         &deleteTriggerOid, &updateTriggerOid);
   11428             : 
   11429             :         /* Add this constraint ... */
   11430         264 :         address = addFkConstraint(addFkReferencedSide,
   11431             :                                   fkconstraint->conname, fkconstraint, fkRel,
   11432             :                                   partitionRel, partIndexId, constrOid,
   11433             :                                   numfks, mapped_confkey,
   11434             :                                   conkey, conpfeqop, conppeqop, conffeqop,
   11435             :                                   numfkdelsetcols, confdelsetcols, false,
   11436         264 :                                   constrForm->conperiod);
   11437             :         /* ... and recurse */
   11438         264 :         addFkRecurseReferenced(fkconstraint,
   11439             :                                fkRel,
   11440             :                                partitionRel,
   11441             :                                partIndexId,
   11442             :                                address.objectId,
   11443             :                                numfks,
   11444             :                                mapped_confkey,
   11445             :                                conkey,
   11446             :                                conpfeqop,
   11447             :                                conppeqop,
   11448             :                                conffeqop,
   11449             :                                numfkdelsetcols,
   11450             :                                confdelsetcols,
   11451             :                                true,
   11452             :                                deleteTriggerOid,
   11453             :                                updateTriggerOid,
   11454         264 :                                constrForm->conperiod);
   11455             : 
   11456         264 :         table_close(fkRel, NoLock);
   11457         264 :         ReleaseSysCache(tuple);
   11458             :     }
   11459             : 
   11460       11530 :     table_close(trigrel, RowExclusiveLock);
   11461       11530 : }
   11462             : 
   11463             : /*
   11464             :  * CloneFkReferencing
   11465             :  *      Subroutine for CloneForeignKeyConstraints
   11466             :  *
   11467             :  * For each FK constraint of the parent relation in the given list, find an
   11468             :  * equivalent constraint in its partition relation that can be reparented;
   11469             :  * if one cannot be found, create a new constraint in the partition as its
   11470             :  * child.
   11471             :  *
   11472             :  * If wqueue is given, it is used to set up phase-3 verification for each
   11473             :  * cloned constraint; omit it if such verification is not needed
   11474             :  * (example: the partition is being created anew).
   11475             :  */
   11476             : static void
   11477       11548 : CloneFkReferencing(List **wqueue, Relation parentRel, Relation partRel)
   11478             : {
   11479             :     AttrMap    *attmap;
   11480             :     List       *partFKs;
   11481       11548 :     List       *clone = NIL;
   11482             :     ListCell   *cell;
   11483             :     Relation    trigrel;
   11484             : 
   11485             :     /* obtain a list of constraints that we need to clone */
   11486       12900 :     foreach(cell, RelationGetFKeyList(parentRel))
   11487             :     {
   11488        1358 :         ForeignKeyCacheInfo *fk = lfirst(cell);
   11489             : 
   11490             :         /*
   11491             :          * Refuse to attach a table as partition that this partitioned table
   11492             :          * already has a foreign key to.  This isn't useful schema, which is
   11493             :          * proven by the fact that there have been no user complaints that
   11494             :          * it's already impossible to achieve this in the opposite direction,
   11495             :          * i.e., creating a foreign key that references a partition.  This
   11496             :          * restriction allows us to dodge some complexities around
   11497             :          * pg_constraint and pg_trigger row creations that would be needed
   11498             :          * during ATTACH/DETACH for this kind of relationship.
   11499             :          */
   11500        1358 :         if (fk->confrelid == RelationGetRelid(partRel))
   11501           6 :             ereport(ERROR,
   11502             :                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   11503             :                      errmsg("cannot attach table \"%s\" as a partition because it is referenced by foreign key \"%s\"",
   11504             :                             RelationGetRelationName(partRel),
   11505             :                             get_constraint_name(fk->conoid))));
   11506             : 
   11507        1352 :         clone = lappend_oid(clone, fk->conoid);
   11508             :     }
   11509             : 
   11510             :     /*
   11511             :      * Silently do nothing if there's nothing to do.  In particular, this
   11512             :      * avoids throwing a spurious error for foreign tables.
   11513             :      */
   11514       11542 :     if (clone == NIL)
   11515       10950 :         return;
   11516             : 
   11517         592 :     if (partRel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
   11518           0 :         ereport(ERROR,
   11519             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   11520             :                  errmsg("foreign key constraints are not supported on foreign tables")));
   11521             : 
   11522             :     /*
   11523             :      * Triggers of the foreign keys will be manipulated a bunch of times in
   11524             :      * the loop below.  To avoid repeatedly opening/closing the trigger
   11525             :      * catalog relation, we open it here and pass it to the subroutines called
   11526             :      * below.
   11527             :      */
   11528         592 :     trigrel = table_open(TriggerRelationId, RowExclusiveLock);
   11529             : 
   11530             :     /*
   11531             :      * The constraint key may differ, if the columns in the partition are
   11532             :      * different.  This map is used to convert them.
   11533             :      */
   11534         592 :     attmap = build_attrmap_by_name(RelationGetDescr(partRel),
   11535             :                                    RelationGetDescr(parentRel),
   11536             :                                    false);
   11537             : 
   11538         592 :     partFKs = copyObject(RelationGetFKeyList(partRel));
   11539             : 
   11540        1932 :     foreach(cell, clone)
   11541             :     {
   11542        1352 :         Oid         parentConstrOid = lfirst_oid(cell);
   11543             :         Form_pg_constraint constrForm;
   11544             :         Relation    pkrel;
   11545             :         HeapTuple   tuple;
   11546             :         int         numfks;
   11547             :         AttrNumber  conkey[INDEX_MAX_KEYS];
   11548             :         AttrNumber  mapped_conkey[INDEX_MAX_KEYS];
   11549             :         AttrNumber  confkey[INDEX_MAX_KEYS];
   11550             :         Oid         conpfeqop[INDEX_MAX_KEYS];
   11551             :         Oid         conppeqop[INDEX_MAX_KEYS];
   11552             :         Oid         conffeqop[INDEX_MAX_KEYS];
   11553             :         int         numfkdelsetcols;
   11554             :         AttrNumber  confdelsetcols[INDEX_MAX_KEYS];
   11555             :         Constraint *fkconstraint;
   11556             :         bool        attached;
   11557             :         Oid         indexOid;
   11558             :         ObjectAddress address;
   11559             :         ListCell   *lc;
   11560        1352 :         Oid         insertTriggerOid = InvalidOid,
   11561        1352 :                     updateTriggerOid = InvalidOid;
   11562             :         bool        with_period;
   11563             : 
   11564        1352 :         tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(parentConstrOid));
   11565        1352 :         if (!HeapTupleIsValid(tuple))
   11566           0 :             elog(ERROR, "cache lookup failed for constraint %u",
   11567             :                  parentConstrOid);
   11568        1352 :         constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
   11569             : 
   11570             :         /* Don't clone constraints whose parents are being cloned */
   11571        1352 :         if (list_member_oid(clone, constrForm->conparentid))
   11572             :         {
   11573         724 :             ReleaseSysCache(tuple);
   11574         874 :             continue;
   11575             :         }
   11576             : 
   11577             :         /*
   11578             :          * Need to prevent concurrent deletions.  If pkrel is a partitioned
   11579             :          * relation, that means to lock all partitions.
   11580             :          */
   11581         628 :         pkrel = table_open(constrForm->confrelid, ShareRowExclusiveLock);
   11582         628 :         if (pkrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
   11583         250 :             (void) find_all_inheritors(RelationGetRelid(pkrel),
   11584             :                                        ShareRowExclusiveLock, NULL);
   11585             : 
   11586         628 :         DeconstructFkConstraintRow(tuple, &numfks, conkey, confkey,
   11587             :                                    conpfeqop, conppeqop, conffeqop,
   11588             :                                    &numfkdelsetcols, confdelsetcols);
   11589        1490 :         for (int i = 0; i < numfks; i++)
   11590         862 :             mapped_conkey[i] = attmap->attnums[conkey[i] - 1];
   11591             : 
   11592             :         /*
   11593             :          * Get the "check" triggers belonging to the constraint, if it is
   11594             :          * ENFORCED, to pass as parent OIDs for similar triggers that will be
   11595             :          * created on the partition in addFkRecurseReferencing().  They are
   11596             :          * also passed to tryAttachPartitionForeignKey() below to simply
   11597             :          * assign as parents to the partition's existing "check" triggers,
   11598             :          * that is, if the corresponding constraints is deemed attachable to
   11599             :          * the parent constraint.
   11600             :          */
   11601         628 :         if (constrForm->conenforced)
   11602         616 :             GetForeignKeyCheckTriggers(trigrel, constrForm->oid,
   11603             :                                        constrForm->confrelid, constrForm->conrelid,
   11604             :                                        &insertTriggerOid, &updateTriggerOid);
   11605             : 
   11606             :         /*
   11607             :          * Before creating a new constraint, see whether any existing FKs are
   11608             :          * fit for the purpose.  If one is, attach the parent constraint to
   11609             :          * it, and don't clone anything.  This way we avoid the expensive
   11610             :          * verification step and don't end up with a duplicate FK, and we
   11611             :          * don't need to recurse to partitions for this constraint.
   11612             :          */
   11613         628 :         attached = false;
   11614         718 :         foreach(lc, partFKs)
   11615             :         {
   11616         246 :             ForeignKeyCacheInfo *fk = lfirst_node(ForeignKeyCacheInfo, lc);
   11617             : 
   11618         246 :             if (tryAttachPartitionForeignKey(wqueue,
   11619             :                                              fk,
   11620             :                                              partRel,
   11621             :                                              parentConstrOid,
   11622             :                                              numfks,
   11623             :                                              mapped_conkey,
   11624             :                                              confkey,
   11625             :                                              conpfeqop,
   11626             :                                              insertTriggerOid,
   11627             :                                              updateTriggerOid,
   11628             :                                              trigrel))
   11629             :             {
   11630         150 :                 attached = true;
   11631         150 :                 table_close(pkrel, NoLock);
   11632         150 :                 break;
   11633             :             }
   11634             :         }
   11635         622 :         if (attached)
   11636             :         {
   11637         150 :             ReleaseSysCache(tuple);
   11638         150 :             continue;
   11639             :         }
   11640             : 
   11641             :         /* No dice.  Set up to create our own constraint */
   11642         472 :         fkconstraint = makeNode(Constraint);
   11643         472 :         fkconstraint->contype = CONSTRAINT_FOREIGN;
   11644             :         /* ->conname determined below */
   11645         472 :         fkconstraint->deferrable = constrForm->condeferrable;
   11646         472 :         fkconstraint->initdeferred = constrForm->condeferred;
   11647         472 :         fkconstraint->location = -1;
   11648         472 :         fkconstraint->pktable = NULL;
   11649             :         /* ->fk_attrs determined below */
   11650         472 :         fkconstraint->pk_attrs = NIL;
   11651         472 :         fkconstraint->fk_matchtype = constrForm->confmatchtype;
   11652         472 :         fkconstraint->fk_upd_action = constrForm->confupdtype;
   11653         472 :         fkconstraint->fk_del_action = constrForm->confdeltype;
   11654         472 :         fkconstraint->fk_del_set_cols = NIL;
   11655         472 :         fkconstraint->old_conpfeqop = NIL;
   11656         472 :         fkconstraint->old_pktable_oid = InvalidOid;
   11657         472 :         fkconstraint->is_enforced = constrForm->conenforced;
   11658         472 :         fkconstraint->skip_validation = false;
   11659         472 :         fkconstraint->initially_valid = constrForm->convalidated;
   11660        1064 :         for (int i = 0; i < numfks; i++)
   11661             :         {
   11662             :             Form_pg_attribute att;
   11663             : 
   11664         592 :             att = TupleDescAttr(RelationGetDescr(partRel),
   11665         592 :                                 mapped_conkey[i] - 1);
   11666         592 :             fkconstraint->fk_attrs = lappend(fkconstraint->fk_attrs,
   11667         592 :                                              makeString(NameStr(att->attname)));
   11668             :         }
   11669             : 
   11670         472 :         indexOid = constrForm->conindid;
   11671         472 :         with_period = constrForm->conperiod;
   11672             : 
   11673             :         /* Create the pg_constraint entry at this level */
   11674         472 :         address = addFkConstraint(addFkReferencingSide,
   11675         472 :                                   NameStr(constrForm->conname), fkconstraint,
   11676             :                                   partRel, pkrel, indexOid, parentConstrOid,
   11677             :                                   numfks, confkey,
   11678             :                                   mapped_conkey, conpfeqop,
   11679             :                                   conppeqop, conffeqop,
   11680             :                                   numfkdelsetcols, confdelsetcols,
   11681             :                                   false, with_period);
   11682             : 
   11683             :         /* Done with the cloned constraint's tuple */
   11684         472 :         ReleaseSysCache(tuple);
   11685             : 
   11686             :         /* Create the check triggers, and recurse to partitions, if any */
   11687         472 :         addFkRecurseReferencing(wqueue,
   11688             :                                 fkconstraint,
   11689             :                                 partRel,
   11690             :                                 pkrel,
   11691             :                                 indexOid,
   11692             :                                 address.objectId,
   11693             :                                 numfks,
   11694             :                                 confkey,
   11695             :                                 mapped_conkey,
   11696             :                                 conpfeqop,
   11697             :                                 conppeqop,
   11698             :                                 conffeqop,
   11699             :                                 numfkdelsetcols,
   11700             :                                 confdelsetcols,
   11701             :                                 false,  /* no old check exists */
   11702             :                                 AccessExclusiveLock,
   11703             :                                 insertTriggerOid,
   11704             :                                 updateTriggerOid,
   11705             :                                 with_period);
   11706         466 :         table_close(pkrel, NoLock);
   11707             :     }
   11708             : 
   11709         580 :     table_close(trigrel, RowExclusiveLock);
   11710             : }
   11711             : 
   11712             : /*
   11713             :  * When the parent of a partition receives [the referencing side of] a foreign
   11714             :  * key, we must propagate that foreign key to the partition.  However, the
   11715             :  * partition might already have an equivalent foreign key; this routine
   11716             :  * compares the given ForeignKeyCacheInfo (in the partition) to the FK defined
   11717             :  * by the other parameters.  If they are equivalent, create the link between
   11718             :  * the two constraints and return true.
   11719             :  *
   11720             :  * If the given FK does not match the one defined by rest of the params,
   11721             :  * return false.
   11722             :  */
   11723             : static bool
   11724         276 : tryAttachPartitionForeignKey(List **wqueue,
   11725             :                              ForeignKeyCacheInfo *fk,
   11726             :                              Relation partition,
   11727             :                              Oid parentConstrOid,
   11728             :                              int numfks,
   11729             :                              AttrNumber *mapped_conkey,
   11730             :                              AttrNumber *confkey,
   11731             :                              Oid *conpfeqop,
   11732             :                              Oid parentInsTrigger,
   11733             :                              Oid parentUpdTrigger,
   11734             :                              Relation trigrel)
   11735             : {
   11736             :     HeapTuple   parentConstrTup;
   11737             :     Form_pg_constraint parentConstr;
   11738             :     HeapTuple   partcontup;
   11739             :     Form_pg_constraint partConstr;
   11740             : 
   11741         276 :     parentConstrTup = SearchSysCache1(CONSTROID,
   11742             :                                       ObjectIdGetDatum(parentConstrOid));
   11743         276 :     if (!HeapTupleIsValid(parentConstrTup))
   11744           0 :         elog(ERROR, "cache lookup failed for constraint %u", parentConstrOid);
   11745         276 :     parentConstr = (Form_pg_constraint) GETSTRUCT(parentConstrTup);
   11746             : 
   11747             :     /*
   11748             :      * Do some quick & easy initial checks.  If any of these fail, we cannot
   11749             :      * use this constraint.
   11750             :      */
   11751         276 :     if (fk->confrelid != parentConstr->confrelid || fk->nkeys != numfks)
   11752             :     {
   11753           0 :         ReleaseSysCache(parentConstrTup);
   11754           0 :         return false;
   11755             :     }
   11756         768 :     for (int i = 0; i < numfks; i++)
   11757             :     {
   11758         492 :         if (fk->conkey[i] != mapped_conkey[i] ||
   11759         492 :             fk->confkey[i] != confkey[i] ||
   11760         492 :             fk->conpfeqop[i] != conpfeqop[i])
   11761             :         {
   11762           0 :             ReleaseSysCache(parentConstrTup);
   11763           0 :             return false;
   11764             :         }
   11765             :     }
   11766             : 
   11767             :     /* Looks good so far; perform more extensive checks. */
   11768         276 :     partcontup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(fk->conoid));
   11769         276 :     if (!HeapTupleIsValid(partcontup))
   11770           0 :         elog(ERROR, "cache lookup failed for constraint %u", fk->conoid);
   11771         276 :     partConstr = (Form_pg_constraint) GETSTRUCT(partcontup);
   11772             : 
   11773             :     /*
   11774             :      * An error should be raised if the constraint enforceability is
   11775             :      * different. Returning false without raising an error, as we do for other
   11776             :      * attributes, could lead to a duplicate constraint with the same
   11777             :      * enforceability as the parent. While this may be acceptable, it may not
   11778             :      * be ideal. Therefore, it's better to raise an error and allow the user
   11779             :      * to correct the enforceability before proceeding.
   11780             :      */
   11781         276 :     if (partConstr->conenforced != parentConstr->conenforced)
   11782           6 :         ereport(ERROR,
   11783             :                 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
   11784             :                  errmsg("constraint \"%s\" enforceability conflicts with constraint \"%s\" on relation \"%s\"",
   11785             :                         NameStr(parentConstr->conname),
   11786             :                         NameStr(partConstr->conname),
   11787             :                         RelationGetRelationName(partition))));
   11788             : 
   11789         270 :     if (OidIsValid(partConstr->conparentid) ||
   11790         240 :         partConstr->condeferrable != parentConstr->condeferrable ||
   11791         212 :         partConstr->condeferred != parentConstr->condeferred ||
   11792         212 :         partConstr->confupdtype != parentConstr->confupdtype ||
   11793         176 :         partConstr->confdeltype != parentConstr->confdeltype ||
   11794         176 :         partConstr->confmatchtype != parentConstr->confmatchtype)
   11795             :     {
   11796         108 :         ReleaseSysCache(parentConstrTup);
   11797         108 :         ReleaseSysCache(partcontup);
   11798         108 :         return false;
   11799             :     }
   11800             : 
   11801         162 :     ReleaseSysCache(parentConstrTup);
   11802         162 :     ReleaseSysCache(partcontup);
   11803             : 
   11804             :     /* Looks good!  Attach this constraint. */
   11805         162 :     AttachPartitionForeignKey(wqueue, partition, fk->conoid,
   11806             :                               parentConstrOid, parentInsTrigger,
   11807             :                               parentUpdTrigger, trigrel);
   11808             : 
   11809         162 :     return true;
   11810             : }
   11811             : 
   11812             : /*
   11813             :  * AttachPartitionForeignKey
   11814             :  *
   11815             :  * The subroutine for tryAttachPartitionForeignKey performs the final tasks of
   11816             :  * attaching the constraint, removing redundant triggers and entries from
   11817             :  * pg_constraint, and setting the constraint's parent.
   11818             :  */
   11819             : static void
   11820         162 : AttachPartitionForeignKey(List **wqueue,
   11821             :                           Relation partition,
   11822             :                           Oid partConstrOid,
   11823             :                           Oid parentConstrOid,
   11824             :                           Oid parentInsTrigger,
   11825             :                           Oid parentUpdTrigger,
   11826             :                           Relation trigrel)
   11827             : {
   11828             :     HeapTuple   parentConstrTup;
   11829             :     Form_pg_constraint parentConstr;
   11830             :     HeapTuple   partcontup;
   11831             :     Form_pg_constraint partConstr;
   11832             :     bool        queueValidation;
   11833             :     Oid         partConstrFrelid;
   11834             :     Oid         partConstrRelid;
   11835             :     bool        parentConstrIsEnforced;
   11836             : 
   11837             :     /* Fetch the parent constraint tuple */
   11838         162 :     parentConstrTup = SearchSysCache1(CONSTROID,
   11839             :                                       ObjectIdGetDatum(parentConstrOid));
   11840         162 :     if (!HeapTupleIsValid(parentConstrTup))
   11841           0 :         elog(ERROR, "cache lookup failed for constraint %u", parentConstrOid);
   11842         162 :     parentConstr = (Form_pg_constraint) GETSTRUCT(parentConstrTup);
   11843         162 :     parentConstrIsEnforced = parentConstr->conenforced;
   11844             : 
   11845             :     /* Fetch the child constraint tuple */
   11846         162 :     partcontup = SearchSysCache1(CONSTROID,
   11847             :                                  ObjectIdGetDatum(partConstrOid));
   11848         162 :     if (!HeapTupleIsValid(partcontup))
   11849           0 :         elog(ERROR, "cache lookup failed for constraint %u", partConstrOid);
   11850         162 :     partConstr = (Form_pg_constraint) GETSTRUCT(partcontup);
   11851         162 :     partConstrFrelid = partConstr->confrelid;
   11852         162 :     partConstrRelid = partConstr->conrelid;
   11853             : 
   11854             :     /*
   11855             :      * If the referenced table is partitioned, then the partition we're
   11856             :      * attaching now has extra pg_constraint rows and action triggers that are
   11857             :      * no longer needed.  Remove those.
   11858             :      */
   11859         162 :     if (get_rel_relkind(partConstrFrelid) == RELKIND_PARTITIONED_TABLE)
   11860             :     {
   11861          36 :         Relation    pg_constraint = table_open(ConstraintRelationId, RowShareLock);
   11862             : 
   11863          36 :         RemoveInheritedConstraint(pg_constraint, trigrel, partConstrOid,
   11864             :                                   partConstrRelid);
   11865             : 
   11866          36 :         table_close(pg_constraint, RowShareLock);
   11867             :     }
   11868             : 
   11869             :     /*
   11870             :      * Will we need to validate this constraint?   A valid parent constraint
   11871             :      * implies that all child constraints have been validated, so if this one
   11872             :      * isn't, we must trigger phase 3 validation.
   11873             :      */
   11874         162 :     queueValidation = parentConstr->convalidated && !partConstr->convalidated;
   11875             : 
   11876         162 :     ReleaseSysCache(partcontup);
   11877         162 :     ReleaseSysCache(parentConstrTup);
   11878             : 
   11879             :     /*
   11880             :      * The action triggers in the new partition become redundant -- the parent
   11881             :      * table already has equivalent ones, and those will be able to reach the
   11882             :      * partition.  Remove the ones in the partition.  We identify them because
   11883             :      * they have our constraint OID, as well as being on the referenced rel.
   11884             :      */
   11885         162 :     DropForeignKeyConstraintTriggers(trigrel, partConstrOid, partConstrFrelid,
   11886             :                                      partConstrRelid);
   11887             : 
   11888         162 :     ConstraintSetParentConstraint(partConstrOid, parentConstrOid,
   11889             :                                   RelationGetRelid(partition));
   11890             : 
   11891             :     /*
   11892             :      * Like the constraint, attach partition's "check" triggers to the
   11893             :      * corresponding parent triggers if the constraint is ENFORCED. NOT
   11894             :      * ENFORCED constraints do not have these triggers.
   11895             :      */
   11896         162 :     if (parentConstrIsEnforced)
   11897             :     {
   11898             :         Oid         insertTriggerOid,
   11899             :                     updateTriggerOid;
   11900             : 
   11901         150 :         GetForeignKeyCheckTriggers(trigrel,
   11902             :                                    partConstrOid, partConstrFrelid, partConstrRelid,
   11903             :                                    &insertTriggerOid, &updateTriggerOid);
   11904             :         Assert(OidIsValid(insertTriggerOid) && OidIsValid(parentInsTrigger));
   11905         150 :         TriggerSetParentTrigger(trigrel, insertTriggerOid, parentInsTrigger,
   11906             :                                 RelationGetRelid(partition));
   11907             :         Assert(OidIsValid(updateTriggerOid) && OidIsValid(parentUpdTrigger));
   11908         150 :         TriggerSetParentTrigger(trigrel, updateTriggerOid, parentUpdTrigger,
   11909             :                                 RelationGetRelid(partition));
   11910             :     }
   11911             : 
   11912             :     /*
   11913             :      * We updated this pg_constraint row above to set its parent; validating
   11914             :      * it will cause its convalidated flag to change, so we need CCI here.  In
   11915             :      * addition, we need it unconditionally for the rare case where the parent
   11916             :      * table has *two* identical constraints; when reaching this function for
   11917             :      * the second one, we must have made our changes visible, otherwise we
   11918             :      * would try to attach both to this one.
   11919             :      */
   11920         162 :     CommandCounterIncrement();
   11921             : 
   11922             :     /* If validation is needed, put it in the queue now. */
   11923         162 :     if (queueValidation)
   11924             :     {
   11925             :         Relation    conrel;
   11926             :         Oid         confrelid;
   11927             : 
   11928          18 :         conrel = table_open(ConstraintRelationId, RowExclusiveLock);
   11929             : 
   11930          18 :         partcontup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(partConstrOid));
   11931          18 :         if (!HeapTupleIsValid(partcontup))
   11932           0 :             elog(ERROR, "cache lookup failed for constraint %u", partConstrOid);
   11933             : 
   11934          18 :         confrelid = ((Form_pg_constraint) GETSTRUCT(partcontup))->confrelid;
   11935             : 
   11936             :         /* Use the same lock as for AT_ValidateConstraint */
   11937          18 :         QueueFKConstraintValidation(wqueue, conrel, partition, confrelid,
   11938             :                                     partcontup, ShareUpdateExclusiveLock);
   11939          18 :         ReleaseSysCache(partcontup);
   11940          18 :         table_close(conrel, RowExclusiveLock);
   11941             :     }
   11942         162 : }
   11943             : 
   11944             : /*
   11945             :  * RemoveInheritedConstraint
   11946             :  *
   11947             :  * Removes the constraint and its associated trigger from the specified
   11948             :  * relation, which inherited the given constraint.
   11949             :  */
   11950             : static void
   11951          36 : RemoveInheritedConstraint(Relation conrel, Relation trigrel, Oid conoid,
   11952             :                           Oid conrelid)
   11953             : {
   11954             :     ObjectAddresses *objs;
   11955             :     HeapTuple   consttup;
   11956             :     ScanKeyData key;
   11957             :     SysScanDesc scan;
   11958             :     HeapTuple   trigtup;
   11959             : 
   11960          36 :     ScanKeyInit(&key,
   11961             :                 Anum_pg_constraint_conrelid,
   11962             :                 BTEqualStrategyNumber, F_OIDEQ,
   11963             :                 ObjectIdGetDatum(conrelid));
   11964             : 
   11965          36 :     scan = systable_beginscan(conrel,
   11966             :                               ConstraintRelidTypidNameIndexId,
   11967             :                               true, NULL, 1, &key);
   11968          36 :     objs = new_object_addresses();
   11969         324 :     while ((consttup = systable_getnext(scan)) != NULL)
   11970             :     {
   11971         288 :         Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(consttup);
   11972             : 
   11973         288 :         if (conform->conparentid != conoid)
   11974         210 :             continue;
   11975             :         else
   11976             :         {
   11977             :             ObjectAddress addr;
   11978             :             SysScanDesc scan2;
   11979             :             ScanKeyData key2;
   11980             :             int         n PG_USED_FOR_ASSERTS_ONLY;
   11981             : 
   11982          78 :             ObjectAddressSet(addr, ConstraintRelationId, conform->oid);
   11983          78 :             add_exact_object_address(&addr, objs);
   11984             : 
   11985             :             /*
   11986             :              * First we must delete the dependency record that binds the
   11987             :              * constraint records together.
   11988             :              */
   11989          78 :             n = deleteDependencyRecordsForSpecific(ConstraintRelationId,
   11990             :                                                    conform->oid,
   11991             :                                                    DEPENDENCY_INTERNAL,
   11992             :                                                    ConstraintRelationId,
   11993             :                                                    conoid);
   11994             :             Assert(n == 1);     /* actually only one is expected */
   11995             : 
   11996             :             /*
   11997             :              * Now search for the triggers for this constraint and set them up
   11998             :              * for deletion too
   11999             :              */
   12000          78 :             ScanKeyInit(&key2,
   12001             :                         Anum_pg_trigger_tgconstraint,
   12002             :                         BTEqualStrategyNumber, F_OIDEQ,
   12003             :                         ObjectIdGetDatum(conform->oid));
   12004          78 :             scan2 = systable_beginscan(trigrel, TriggerConstraintIndexId,
   12005             :                                        true, NULL, 1, &key2);
   12006         234 :             while ((trigtup = systable_getnext(scan2)) != NULL)
   12007             :             {
   12008         156 :                 ObjectAddressSet(addr, TriggerRelationId,
   12009             :                                  ((Form_pg_trigger) GETSTRUCT(trigtup))->oid);
   12010         156 :                 add_exact_object_address(&addr, objs);
   12011             :             }
   12012          78 :             systable_endscan(scan2);
   12013             :         }
   12014             :     }
   12015             :     /* make the dependency deletions visible */
   12016          36 :     CommandCounterIncrement();
   12017          36 :     performMultipleDeletions(objs, DROP_RESTRICT,
   12018             :                              PERFORM_DELETION_INTERNAL);
   12019          36 :     systable_endscan(scan);
   12020          36 : }
   12021             : 
   12022             : /*
   12023             :  * DropForeignKeyConstraintTriggers
   12024             :  *
   12025             :  * The subroutine for tryAttachPartitionForeignKey handles the deletion of
   12026             :  * action triggers for the foreign key constraint.
   12027             :  *
   12028             :  * If valid confrelid and conrelid values are not provided, the respective
   12029             :  * trigger check will be skipped, and the trigger will be considered for
   12030             :  * removal.
   12031             :  */
   12032             : static void
   12033         234 : DropForeignKeyConstraintTriggers(Relation trigrel, Oid conoid, Oid confrelid,
   12034             :                                  Oid conrelid)
   12035             : {
   12036             :     ScanKeyData key;
   12037             :     SysScanDesc scan;
   12038             :     HeapTuple   trigtup;
   12039             : 
   12040         234 :     ScanKeyInit(&key,
   12041             :                 Anum_pg_trigger_tgconstraint,
   12042             :                 BTEqualStrategyNumber, F_OIDEQ,
   12043             :                 ObjectIdGetDatum(conoid));
   12044         234 :     scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
   12045             :                               NULL, 1, &key);
   12046        1014 :     while ((trigtup = systable_getnext(scan)) != NULL)
   12047             :     {
   12048         780 :         Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
   12049             :         ObjectAddress trigger;
   12050             : 
   12051             :         /* Invalid if trigger is not for a referential integrity constraint */
   12052         780 :         if (!OidIsValid(trgform->tgconstrrelid))
   12053         300 :             continue;
   12054         780 :         if (OidIsValid(conrelid) && trgform->tgconstrrelid != conrelid)
   12055         300 :             continue;
   12056         480 :         if (OidIsValid(confrelid) && trgform->tgrelid != confrelid)
   12057           0 :             continue;
   12058             : 
   12059             :         /* We should be dropping trigger related to foreign key constraint */
   12060             :         Assert(trgform->tgfoid == F_RI_FKEY_CHECK_INS ||
   12061             :                trgform->tgfoid == F_RI_FKEY_CHECK_UPD ||
   12062             :                trgform->tgfoid == F_RI_FKEY_CASCADE_DEL ||
   12063             :                trgform->tgfoid == F_RI_FKEY_CASCADE_UPD ||
   12064             :                trgform->tgfoid == F_RI_FKEY_RESTRICT_DEL ||
   12065             :                trgform->tgfoid == F_RI_FKEY_RESTRICT_UPD ||
   12066             :                trgform->tgfoid == F_RI_FKEY_SETNULL_DEL ||
   12067             :                trgform->tgfoid == F_RI_FKEY_SETNULL_UPD ||
   12068             :                trgform->tgfoid == F_RI_FKEY_SETDEFAULT_DEL ||
   12069             :                trgform->tgfoid == F_RI_FKEY_SETDEFAULT_UPD ||
   12070             :                trgform->tgfoid == F_RI_FKEY_NOACTION_DEL ||
   12071             :                trgform->tgfoid == F_RI_FKEY_NOACTION_UPD);
   12072             : 
   12073             :         /*
   12074             :          * The constraint is originally set up to contain this trigger as an
   12075             :          * implementation object, so there's a dependency record that links
   12076             :          * the two; however, since the trigger is no longer needed, we remove
   12077             :          * the dependency link in order to be able to drop the trigger while
   12078             :          * keeping the constraint intact.
   12079             :          */
   12080         480 :         deleteDependencyRecordsFor(TriggerRelationId,
   12081             :                                    trgform->oid,
   12082             :                                    false);
   12083             :         /* make dependency deletion visible to performDeletion */
   12084         480 :         CommandCounterIncrement();
   12085         480 :         ObjectAddressSet(trigger, TriggerRelationId,
   12086             :                          trgform->oid);
   12087         480 :         performDeletion(&trigger, DROP_RESTRICT, 0);
   12088             :         /* make trigger drop visible, in case the loop iterates */
   12089         480 :         CommandCounterIncrement();
   12090             :     }
   12091             : 
   12092         234 :     systable_endscan(scan);
   12093         234 : }
   12094             : 
   12095             : /*
   12096             :  * GetForeignKeyActionTriggers
   12097             :  *      Returns delete and update "action" triggers of the given relation
   12098             :  *      belonging to the given constraint
   12099             :  */
   12100             : static void
   12101         258 : GetForeignKeyActionTriggers(Relation trigrel,
   12102             :                             Oid conoid, Oid confrelid, Oid conrelid,
   12103             :                             Oid *deleteTriggerOid,
   12104             :                             Oid *updateTriggerOid)
   12105             : {
   12106             :     ScanKeyData key;
   12107             :     SysScanDesc scan;
   12108             :     HeapTuple   trigtup;
   12109             : 
   12110         258 :     *deleteTriggerOid = *updateTriggerOid = InvalidOid;
   12111         258 :     ScanKeyInit(&key,
   12112             :                 Anum_pg_trigger_tgconstraint,
   12113             :                 BTEqualStrategyNumber, F_OIDEQ,
   12114             :                 ObjectIdGetDatum(conoid));
   12115             : 
   12116         258 :     scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
   12117             :                               NULL, 1, &key);
   12118         528 :     while ((trigtup = systable_getnext(scan)) != NULL)
   12119             :     {
   12120         528 :         Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
   12121             : 
   12122         528 :         if (trgform->tgconstrrelid != conrelid)
   12123          10 :             continue;
   12124         518 :         if (trgform->tgrelid != confrelid)
   12125           0 :             continue;
   12126             :         /* Only ever look at "action" triggers on the PK side. */
   12127         518 :         if (RI_FKey_trigger_type(trgform->tgfoid) != RI_TRIGGER_PK)
   12128           2 :             continue;
   12129         516 :         if (TRIGGER_FOR_DELETE(trgform->tgtype))
   12130             :         {
   12131             :             Assert(*deleteTriggerOid == InvalidOid);
   12132         258 :             *deleteTriggerOid = trgform->oid;
   12133             :         }
   12134         258 :         else if (TRIGGER_FOR_UPDATE(trgform->tgtype))
   12135             :         {
   12136             :             Assert(*updateTriggerOid == InvalidOid);
   12137         258 :             *updateTriggerOid = trgform->oid;
   12138             :         }
   12139             : #ifndef USE_ASSERT_CHECKING
   12140             :         /* In an assert-enabled build, continue looking to find duplicates */
   12141         516 :         if (OidIsValid(*deleteTriggerOid) && OidIsValid(*updateTriggerOid))
   12142         258 :             break;
   12143             : #endif
   12144             :     }
   12145             : 
   12146         258 :     if (!OidIsValid(*deleteTriggerOid))
   12147           0 :         elog(ERROR, "could not find ON DELETE action trigger of foreign key constraint %u",
   12148             :              conoid);
   12149         258 :     if (!OidIsValid(*updateTriggerOid))
   12150           0 :         elog(ERROR, "could not find ON UPDATE action trigger of foreign key constraint %u",
   12151             :              conoid);
   12152             : 
   12153         258 :     systable_endscan(scan);
   12154         258 : }
   12155             : 
   12156             : /*
   12157             :  * GetForeignKeyCheckTriggers
   12158             :  *      Returns insert and update "check" triggers of the given relation
   12159             :  *      belonging to the given constraint
   12160             :  */
   12161             : static void
   12162         874 : GetForeignKeyCheckTriggers(Relation trigrel,
   12163             :                            Oid conoid, Oid confrelid, Oid conrelid,
   12164             :                            Oid *insertTriggerOid,
   12165             :                            Oid *updateTriggerOid)
   12166             : {
   12167             :     ScanKeyData key;
   12168             :     SysScanDesc scan;
   12169             :     HeapTuple   trigtup;
   12170             : 
   12171         874 :     *insertTriggerOid = *updateTriggerOid = InvalidOid;
   12172         874 :     ScanKeyInit(&key,
   12173             :                 Anum_pg_trigger_tgconstraint,
   12174             :                 BTEqualStrategyNumber, F_OIDEQ,
   12175             :                 ObjectIdGetDatum(conoid));
   12176             : 
   12177         874 :     scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
   12178             :                               NULL, 1, &key);
   12179        2848 :     while ((trigtup = systable_getnext(scan)) != NULL)
   12180             :     {
   12181        2848 :         Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
   12182             : 
   12183        2848 :         if (trgform->tgconstrrelid != confrelid)
   12184         992 :             continue;
   12185        1856 :         if (trgform->tgrelid != conrelid)
   12186           0 :             continue;
   12187             :         /* Only ever look at "check" triggers on the FK side. */
   12188        1856 :         if (RI_FKey_trigger_type(trgform->tgfoid) != RI_TRIGGER_FK)
   12189         108 :             continue;
   12190        1748 :         if (TRIGGER_FOR_INSERT(trgform->tgtype))
   12191             :         {
   12192             :             Assert(*insertTriggerOid == InvalidOid);
   12193         874 :             *insertTriggerOid = trgform->oid;
   12194             :         }
   12195         874 :         else if (TRIGGER_FOR_UPDATE(trgform->tgtype))
   12196             :         {
   12197             :             Assert(*updateTriggerOid == InvalidOid);
   12198         874 :             *updateTriggerOid = trgform->oid;
   12199             :         }
   12200             : #ifndef USE_ASSERT_CHECKING
   12201             :         /* In an assert-enabled build, continue looking to find duplicates. */
   12202        1748 :         if (OidIsValid(*insertTriggerOid) && OidIsValid(*updateTriggerOid))
   12203         874 :             break;
   12204             : #endif
   12205             :     }
   12206             : 
   12207         874 :     if (!OidIsValid(*insertTriggerOid))
   12208           0 :         elog(ERROR, "could not find ON INSERT check triggers of foreign key constraint %u",
   12209             :              conoid);
   12210         874 :     if (!OidIsValid(*updateTriggerOid))
   12211           0 :         elog(ERROR, "could not find ON UPDATE check triggers of foreign key constraint %u",
   12212             :              conoid);
   12213             : 
   12214         874 :     systable_endscan(scan);
   12215         874 : }
   12216             : 
   12217             : /*
   12218             :  * ALTER TABLE ALTER CONSTRAINT
   12219             :  *
   12220             :  * Update the attributes of a constraint.
   12221             :  *
   12222             :  * Currently only works for Foreign Key and not null constraints.
   12223             :  *
   12224             :  * If the constraint is modified, returns its address; otherwise, return
   12225             :  * InvalidObjectAddress.
   12226             :  */
   12227             : static ObjectAddress
   12228         294 : ATExecAlterConstraint(List **wqueue, Relation rel, ATAlterConstraint *cmdcon,
   12229             :                       bool recurse, LOCKMODE lockmode)
   12230             : {
   12231             :     Relation    conrel;
   12232             :     Relation    tgrel;
   12233             :     SysScanDesc scan;
   12234             :     ScanKeyData skey[3];
   12235             :     HeapTuple   contuple;
   12236             :     Form_pg_constraint currcon;
   12237             :     ObjectAddress address;
   12238             : 
   12239             :     /*
   12240             :      * Disallow altering ONLY a partitioned table, as it would make no sense.
   12241             :      * This is okay for legacy inheritance.
   12242             :      */
   12243         294 :     if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && !recurse)
   12244           0 :         ereport(ERROR,
   12245             :                 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
   12246             :                 errmsg("constraint must be altered in child tables too"),
   12247             :                 errhint("Do not specify the ONLY keyword."));
   12248             : 
   12249             : 
   12250         294 :     conrel = table_open(ConstraintRelationId, RowExclusiveLock);
   12251         294 :     tgrel = table_open(TriggerRelationId, RowExclusiveLock);
   12252             : 
   12253             :     /*
   12254             :      * Find and check the target constraint
   12255             :      */
   12256         294 :     ScanKeyInit(&skey[0],
   12257             :                 Anum_pg_constraint_conrelid,
   12258             :                 BTEqualStrategyNumber, F_OIDEQ,
   12259             :                 ObjectIdGetDatum(RelationGetRelid(rel)));
   12260         294 :     ScanKeyInit(&skey[1],
   12261             :                 Anum_pg_constraint_contypid,
   12262             :                 BTEqualStrategyNumber, F_OIDEQ,
   12263             :                 ObjectIdGetDatum(InvalidOid));
   12264         294 :     ScanKeyInit(&skey[2],
   12265             :                 Anum_pg_constraint_conname,
   12266             :                 BTEqualStrategyNumber, F_NAMEEQ,
   12267         294 :                 CStringGetDatum(cmdcon->conname));
   12268         294 :     scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
   12269             :                               true, NULL, 3, skey);
   12270             : 
   12271             :     /* There can be at most one matching row */
   12272         294 :     if (!HeapTupleIsValid(contuple = systable_getnext(scan)))
   12273           6 :         ereport(ERROR,
   12274             :                 (errcode(ERRCODE_UNDEFINED_OBJECT),
   12275             :                  errmsg("constraint \"%s\" of relation \"%s\" does not exist",
   12276             :                         cmdcon->conname, RelationGetRelationName(rel))));
   12277             : 
   12278         288 :     currcon = (Form_pg_constraint) GETSTRUCT(contuple);
   12279         288 :     if (cmdcon->alterDeferrability && currcon->contype != CONSTRAINT_FOREIGN)
   12280           0 :         ereport(ERROR,
   12281             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   12282             :                  errmsg("constraint \"%s\" of relation \"%s\" is not a foreign key constraint",
   12283             :                         cmdcon->conname, RelationGetRelationName(rel))));
   12284         288 :     if (cmdcon->alterEnforceability && currcon->contype != CONSTRAINT_FOREIGN)
   12285          12 :         ereport(ERROR,
   12286             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   12287             :                  errmsg("cannot alter enforceability of constraint \"%s\" of relation \"%s\"",
   12288             :                         cmdcon->conname, RelationGetRelationName(rel))));
   12289         276 :     if (cmdcon->alterInheritability &&
   12290          90 :         currcon->contype != CONSTRAINT_NOTNULL)
   12291          24 :         ereport(ERROR,
   12292             :                 errcode(ERRCODE_WRONG_OBJECT_TYPE),
   12293             :                 errmsg("constraint \"%s\" of relation \"%s\" is not a not-null constraint",
   12294             :                        cmdcon->conname, RelationGetRelationName(rel)));
   12295             : 
   12296             :     /* Refuse to modify inheritability of inherited constraints */
   12297         252 :     if (cmdcon->alterInheritability &&
   12298          66 :         cmdcon->noinherit && currcon->coninhcount > 0)
   12299           6 :         ereport(ERROR,
   12300             :                 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
   12301             :                 errmsg("cannot alter inherited constraint \"%s\" on relation \"%s\"",
   12302             :                        NameStr(currcon->conname),
   12303             :                        RelationGetRelationName(rel)));
   12304             : 
   12305             :     /*
   12306             :      * If it's not the topmost constraint, raise an error.
   12307             :      *
   12308             :      * Altering a non-topmost constraint leaves some triggers untouched, since
   12309             :      * they are not directly connected to this constraint; also, pg_dump would
   12310             :      * ignore the deferrability status of the individual constraint, since it
   12311             :      * only dumps topmost constraints.  Avoid these problems by refusing this
   12312             :      * operation and telling the user to alter the parent constraint instead.
   12313             :      */
   12314         246 :     if (OidIsValid(currcon->conparentid))
   12315             :     {
   12316             :         HeapTuple   tp;
   12317          12 :         Oid         parent = currcon->conparentid;
   12318          12 :         char       *ancestorname = NULL;
   12319          12 :         char       *ancestortable = NULL;
   12320             : 
   12321             :         /* Loop to find the topmost constraint */
   12322          24 :         while (HeapTupleIsValid(tp = SearchSysCache1(CONSTROID, ObjectIdGetDatum(parent))))
   12323             :         {
   12324          24 :             Form_pg_constraint contup = (Form_pg_constraint) GETSTRUCT(tp);
   12325             : 
   12326             :             /* If no parent, this is the constraint we want */
   12327          24 :             if (!OidIsValid(contup->conparentid))
   12328             :             {
   12329          12 :                 ancestorname = pstrdup(NameStr(contup->conname));
   12330          12 :                 ancestortable = get_rel_name(contup->conrelid);
   12331          12 :                 ReleaseSysCache(tp);
   12332          12 :                 break;
   12333             :             }
   12334             : 
   12335          12 :             parent = contup->conparentid;
   12336          12 :             ReleaseSysCache(tp);
   12337             :         }
   12338             : 
   12339          12 :         ereport(ERROR,
   12340             :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
   12341             :                  errmsg("cannot alter constraint \"%s\" on relation \"%s\"",
   12342             :                         cmdcon->conname, RelationGetRelationName(rel)),
   12343             :                  ancestorname && ancestortable ?
   12344             :                  errdetail("Constraint \"%s\" is derived from constraint \"%s\" of relation \"%s\".",
   12345             :                            cmdcon->conname, ancestorname, ancestortable) : 0,
   12346             :                  errhint("You may alter the constraint it derives from instead.")));
   12347             :     }
   12348             : 
   12349         234 :     address = InvalidObjectAddress;
   12350             : 
   12351             :     /*
   12352             :      * Do the actual catalog work, and recurse if necessary.
   12353             :      */
   12354         234 :     if (ATExecAlterConstraintInternal(wqueue, cmdcon, conrel, tgrel, rel,
   12355             :                                       contuple, recurse, lockmode))
   12356         222 :         ObjectAddressSet(address, ConstraintRelationId, currcon->oid);
   12357             : 
   12358         228 :     systable_endscan(scan);
   12359             : 
   12360         228 :     table_close(tgrel, RowExclusiveLock);
   12361         228 :     table_close(conrel, RowExclusiveLock);
   12362             : 
   12363         228 :     return address;
   12364             : }
   12365             : 
   12366             : /*
   12367             :  * A subroutine of ATExecAlterConstraint that calls the respective routines for
   12368             :  * altering constraint's enforceability, deferrability or inheritability.
   12369             :  */
   12370             : static bool
   12371         234 : ATExecAlterConstraintInternal(List **wqueue, ATAlterConstraint *cmdcon,
   12372             :                               Relation conrel, Relation tgrel, Relation rel,
   12373             :                               HeapTuple contuple, bool recurse,
   12374             :                               LOCKMODE lockmode)
   12375             : {
   12376             :     Form_pg_constraint currcon;
   12377         234 :     bool        changed = false;
   12378         234 :     List       *otherrelids = NIL;
   12379             : 
   12380         234 :     currcon = (Form_pg_constraint) GETSTRUCT(contuple);
   12381             : 
   12382             :     /*
   12383             :      * Do the catalog work for the enforceability or deferrability change,
   12384             :      * recurse if necessary.
   12385             :      *
   12386             :      * Note that even if deferrability is requested to be altered along with
   12387             :      * enforceability, we don't need to explicitly update multiple entries in
   12388             :      * pg_trigger related to deferrability.
   12389             :      *
   12390             :      * Modifying enforceability involves either creating or dropping the
   12391             :      * trigger, during which the deferrability setting will be adjusted
   12392             :      * automatically.
   12393             :      */
   12394         312 :     if (cmdcon->alterEnforceability &&
   12395          78 :         ATExecAlterConstrEnforceability(wqueue, cmdcon, conrel, tgrel,
   12396             :                                         currcon->conrelid, currcon->confrelid,
   12397             :                                         contuple, lockmode, InvalidOid,
   12398             :                                         InvalidOid, InvalidOid, InvalidOid))
   12399          72 :         changed = true;
   12400             : 
   12401         258 :     else if (cmdcon->alterDeferrability &&
   12402          96 :              ATExecAlterConstrDeferrability(wqueue, cmdcon, conrel, tgrel, rel,
   12403             :                                             contuple, recurse, &otherrelids,
   12404             :                                             lockmode))
   12405             :     {
   12406             :         /*
   12407             :          * AlterConstrUpdateConstraintEntry already invalidated relcache for
   12408             :          * the relations having the constraint itself; here we also invalidate
   12409             :          * for relations that have any triggers that are part of the
   12410             :          * constraint.
   12411             :          */
   12412         306 :         foreach_oid(relid, otherrelids)
   12413         114 :             CacheInvalidateRelcacheByRelid(relid);
   12414             : 
   12415          96 :         changed = true;
   12416             :     }
   12417             : 
   12418             :     /*
   12419             :      * Do the catalog work for the inheritability change.
   12420             :      */
   12421         288 :     if (cmdcon->alterInheritability &&
   12422          60 :         ATExecAlterConstrInheritability(wqueue, cmdcon, conrel, rel, contuple,
   12423             :                                         lockmode))
   12424          54 :         changed = true;
   12425             : 
   12426         228 :     return changed;
   12427             : }
   12428             : 
   12429             : /*
   12430             :  * Returns true if the constraint's enforceability is altered.
   12431             :  *
   12432             :  * Depending on whether the constraint is being set to ENFORCED or NOT
   12433             :  * ENFORCED, it creates or drops the trigger accordingly.
   12434             :  *
   12435             :  * Note that we must recurse even when trying to change a constraint to not
   12436             :  * enforced if it is already not enforced, in case descendant constraints
   12437             :  * might be enforced and need to be changed to not enforced. Conversely, we
   12438             :  * should do nothing if a constraint is being set to enforced and is already
   12439             :  * enforced, as descendant constraints cannot be different in that case.
   12440             :  */
   12441             : static bool
   12442         180 : ATExecAlterConstrEnforceability(List **wqueue, ATAlterConstraint *cmdcon,
   12443             :                                 Relation conrel, Relation tgrel,
   12444             :                                 Oid fkrelid, Oid pkrelid,
   12445             :                                 HeapTuple contuple, LOCKMODE lockmode,
   12446             :                                 Oid ReferencedParentDelTrigger,
   12447             :                                 Oid ReferencedParentUpdTrigger,
   12448             :                                 Oid ReferencingParentInsTrigger,
   12449             :                                 Oid ReferencingParentUpdTrigger)
   12450             : {
   12451             :     Form_pg_constraint currcon;
   12452             :     Oid         conoid;
   12453             :     Relation    rel;
   12454         180 :     bool        changed = false;
   12455             : 
   12456             :     /* Since this function recurses, it could be driven to stack overflow */
   12457         180 :     check_stack_depth();
   12458             : 
   12459             :     Assert(cmdcon->alterEnforceability);
   12460             : 
   12461         180 :     currcon = (Form_pg_constraint) GETSTRUCT(contuple);
   12462         180 :     conoid = currcon->oid;
   12463             : 
   12464             :     /* Should be foreign key constraint */
   12465             :     Assert(currcon->contype == CONSTRAINT_FOREIGN);
   12466             : 
   12467         180 :     rel = table_open(currcon->conrelid, lockmode);
   12468             : 
   12469         180 :     if (currcon->conenforced != cmdcon->is_enforced)
   12470             :     {
   12471         174 :         AlterConstrUpdateConstraintEntry(cmdcon, conrel, contuple);
   12472         174 :         changed = true;
   12473             :     }
   12474             : 
   12475             :     /* Drop triggers */
   12476         180 :     if (!cmdcon->is_enforced)
   12477             :     {
   12478             :         /*
   12479             :          * When setting a constraint to NOT ENFORCED, the constraint triggers
   12480             :          * need to be dropped. Therefore, we must process the child relations
   12481             :          * first, followed by the parent, to account for dependencies.
   12482             :          */
   12483         126 :         if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
   12484          54 :             get_rel_relkind(currcon->confrelid) == RELKIND_PARTITIONED_TABLE)
   12485          18 :             AlterConstrEnforceabilityRecurse(wqueue, cmdcon, conrel, tgrel,
   12486             :                                              fkrelid, pkrelid, contuple,
   12487             :                                              lockmode, InvalidOid, InvalidOid,
   12488             :                                              InvalidOid, InvalidOid);
   12489             : 
   12490             :         /* Drop all the triggers */
   12491          72 :         DropForeignKeyConstraintTriggers(tgrel, conoid, InvalidOid, InvalidOid);
   12492             :     }
   12493         108 :     else if (changed)           /* Create triggers */
   12494             :     {
   12495         108 :         Oid         ReferencedDelTriggerOid = InvalidOid,
   12496         108 :                     ReferencedUpdTriggerOid = InvalidOid,
   12497         108 :                     ReferencingInsTriggerOid = InvalidOid,
   12498         108 :                     ReferencingUpdTriggerOid = InvalidOid;
   12499             : 
   12500             :         /* Prepare the minimal information required for trigger creation. */
   12501         108 :         Constraint *fkconstraint = makeNode(Constraint);
   12502             : 
   12503         108 :         fkconstraint->conname = pstrdup(NameStr(currcon->conname));
   12504         108 :         fkconstraint->fk_matchtype = currcon->confmatchtype;
   12505         108 :         fkconstraint->fk_upd_action = currcon->confupdtype;
   12506         108 :         fkconstraint->fk_del_action = currcon->confdeltype;
   12507             : 
   12508             :         /* Create referenced triggers */
   12509         108 :         if (currcon->conrelid == fkrelid)
   12510          66 :             createForeignKeyActionTriggers(currcon->conrelid,
   12511             :                                            currcon->confrelid,
   12512             :                                            fkconstraint,
   12513             :                                            conoid,
   12514             :                                            currcon->conindid,
   12515             :                                            ReferencedParentDelTrigger,
   12516             :                                            ReferencedParentUpdTrigger,
   12517             :                                            &ReferencedDelTriggerOid,
   12518             :                                            &ReferencedUpdTriggerOid);
   12519             : 
   12520             :         /* Create referencing triggers */
   12521         108 :         if (currcon->confrelid == pkrelid)
   12522          90 :             createForeignKeyCheckTriggers(currcon->conrelid,
   12523             :                                           pkrelid,
   12524             :                                           fkconstraint,
   12525             :                                           conoid,
   12526             :                                           currcon->conindid,
   12527             :                                           ReferencingParentInsTrigger,
   12528             :                                           ReferencingParentUpdTrigger,
   12529             :                                           &ReferencingInsTriggerOid,
   12530             :                                           &ReferencingUpdTriggerOid);
   12531             : 
   12532             :         /*
   12533             :          * Tell Phase 3 to check that the constraint is satisfied by existing
   12534             :          * rows.  Only applies to leaf partitions, and (for constraints that
   12535             :          * reference a partitioned table) only if this is not one of the
   12536             :          * pg_constraint rows that exist solely to support action triggers.
   12537             :          */
   12538         108 :         if (rel->rd_rel->relkind == RELKIND_RELATION &&
   12539          90 :             currcon->confrelid == pkrelid)
   12540             :         {
   12541             :             AlteredTableInfo *tab;
   12542             :             NewConstraint *newcon;
   12543             : 
   12544          72 :             newcon = palloc0_object(NewConstraint);
   12545          72 :             newcon->name = fkconstraint->conname;
   12546          72 :             newcon->contype = CONSTR_FOREIGN;
   12547          72 :             newcon->refrelid = currcon->confrelid;
   12548          72 :             newcon->refindid = currcon->conindid;
   12549          72 :             newcon->conid = currcon->oid;
   12550          72 :             newcon->qual = (Node *) fkconstraint;
   12551             : 
   12552             :             /* Find or create work queue entry for this table */
   12553          72 :             tab = ATGetQueueEntry(wqueue, rel);
   12554          72 :             tab->constraints = lappend(tab->constraints, newcon);
   12555             :         }
   12556             : 
   12557             :         /*
   12558             :          * If the table at either end of the constraint is partitioned, we
   12559             :          * need to recurse and create triggers for each constraint that is a
   12560             :          * child of this one.
   12561             :          */
   12562         198 :         if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
   12563          90 :             get_rel_relkind(currcon->confrelid) == RELKIND_PARTITIONED_TABLE)
   12564          30 :             AlterConstrEnforceabilityRecurse(wqueue, cmdcon, conrel, tgrel,
   12565             :                                              fkrelid, pkrelid, contuple,
   12566             :                                              lockmode, ReferencedDelTriggerOid,
   12567             :                                              ReferencedUpdTriggerOid,
   12568             :                                              ReferencingInsTriggerOid,
   12569             :                                              ReferencingUpdTriggerOid);
   12570             :     }
   12571             : 
   12572         180 :     table_close(rel, NoLock);
   12573             : 
   12574         180 :     return changed;
   12575             : }
   12576             : 
   12577             : /*
   12578             :  * Returns true if the constraint's deferrability is altered.
   12579             :  *
   12580             :  * *otherrelids is appended OIDs of relations containing affected triggers.
   12581             :  *
   12582             :  * Note that we must recurse even when the values are correct, in case
   12583             :  * indirect descendants have had their constraints altered locally.
   12584             :  * (This could be avoided if we forbade altering constraints in partitions
   12585             :  * but existing releases don't do that.)
   12586             :  */
   12587             : static bool
   12588         162 : ATExecAlterConstrDeferrability(List **wqueue, ATAlterConstraint *cmdcon,
   12589             :                                Relation conrel, Relation tgrel, Relation rel,
   12590             :                                HeapTuple contuple, bool recurse,
   12591             :                                List **otherrelids, LOCKMODE lockmode)
   12592             : {
   12593             :     Form_pg_constraint currcon;
   12594             :     Oid         refrelid;
   12595         162 :     bool        changed = false;
   12596             : 
   12597             :     /* since this function recurses, it could be driven to stack overflow */
   12598         162 :     check_stack_depth();
   12599             : 
   12600             :     Assert(cmdcon->alterDeferrability);
   12601             : 
   12602         162 :     currcon = (Form_pg_constraint) GETSTRUCT(contuple);
   12603         162 :     refrelid = currcon->confrelid;
   12604             : 
   12605             :     /* Should be foreign key constraint */
   12606             :     Assert(currcon->contype == CONSTRAINT_FOREIGN);
   12607             : 
   12608             :     /*
   12609             :      * If called to modify a constraint that's already in the desired state,
   12610             :      * silently do nothing.
   12611             :      */
   12612         162 :     if (currcon->condeferrable != cmdcon->deferrable ||
   12613           6 :         currcon->condeferred != cmdcon->initdeferred)
   12614             :     {
   12615         162 :         AlterConstrUpdateConstraintEntry(cmdcon, conrel, contuple);
   12616         162 :         changed = true;
   12617             : 
   12618             :         /*
   12619             :          * Now we need to update the multiple entries in pg_trigger that
   12620             :          * implement the constraint.
   12621             :          */
   12622         162 :         AlterConstrTriggerDeferrability(currcon->oid, tgrel, rel,
   12623         162 :                                         cmdcon->deferrable,
   12624         162 :                                         cmdcon->initdeferred, otherrelids);
   12625             :     }
   12626             : 
   12627             :     /*
   12628             :      * If the table at either end of the constraint is partitioned, we need to
   12629             :      * handle every constraint that is a child of this one.
   12630             :      */
   12631         162 :     if (recurse && changed &&
   12632         300 :         (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
   12633         138 :          get_rel_relkind(refrelid) == RELKIND_PARTITIONED_TABLE))
   12634          42 :         AlterConstrDeferrabilityRecurse(wqueue, cmdcon, conrel, tgrel, rel,
   12635             :                                         contuple, recurse, otherrelids,
   12636             :                                         lockmode);
   12637             : 
   12638         162 :     return changed;
   12639             : }
   12640             : 
   12641             : /*
   12642             :  * Returns true if the constraint's inheritability is altered.
   12643             :  */
   12644             : static bool
   12645          60 : ATExecAlterConstrInheritability(List **wqueue, ATAlterConstraint *cmdcon,
   12646             :                                 Relation conrel, Relation rel,
   12647             :                                 HeapTuple contuple, LOCKMODE lockmode)
   12648             : {
   12649             :     Form_pg_constraint currcon;
   12650             :     AttrNumber  colNum;
   12651             :     char       *colName;
   12652             :     List       *children;
   12653             : 
   12654             :     Assert(cmdcon->alterInheritability);
   12655             : 
   12656          60 :     currcon = (Form_pg_constraint) GETSTRUCT(contuple);
   12657             : 
   12658             :     /* The current implementation only works for NOT NULL constraints */
   12659             :     Assert(currcon->contype == CONSTRAINT_NOTNULL);
   12660             : 
   12661             :     /*
   12662             :      * If called to modify a constraint that's already in the desired state,
   12663             :      * silently do nothing.
   12664             :      */
   12665          60 :     if (cmdcon->noinherit == currcon->connoinherit)
   12666           0 :         return false;
   12667             : 
   12668          60 :     AlterConstrUpdateConstraintEntry(cmdcon, conrel, contuple);
   12669          60 :     CommandCounterIncrement();
   12670             : 
   12671             :     /* Fetch the column number and name */
   12672          60 :     colNum = extractNotNullColumn(contuple);
   12673          60 :     colName = get_attname(currcon->conrelid, colNum, false);
   12674             : 
   12675             :     /*
   12676             :      * Propagate the change to children.  For this subcommand type we don't
   12677             :      * recursively affect children, just the immediate level.
   12678             :      */
   12679          60 :     children = find_inheritance_children(RelationGetRelid(rel),
   12680             :                                          lockmode);
   12681         192 :     foreach_oid(childoid, children)
   12682             :     {
   12683             :         ObjectAddress addr;
   12684             : 
   12685          84 :         if (cmdcon->noinherit)
   12686             :         {
   12687             :             HeapTuple   childtup;
   12688             :             Form_pg_constraint childcon;
   12689             : 
   12690          30 :             childtup = findNotNullConstraint(childoid, colName);
   12691          30 :             if (!childtup)
   12692           0 :                 elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation %u",
   12693             :                      colName, childoid);
   12694          30 :             childcon = (Form_pg_constraint) GETSTRUCT(childtup);
   12695             :             Assert(childcon->coninhcount > 0);
   12696          30 :             childcon->coninhcount--;
   12697          30 :             childcon->conislocal = true;
   12698          30 :             CatalogTupleUpdate(conrel, &childtup->t_self, childtup);
   12699          30 :             heap_freetuple(childtup);
   12700             :         }
   12701             :         else
   12702             :         {
   12703          54 :             Relation    childrel = table_open(childoid, NoLock);
   12704             : 
   12705          54 :             addr = ATExecSetNotNull(wqueue, childrel, NameStr(currcon->conname),
   12706             :                                     colName, true, true, lockmode);
   12707          48 :             if (OidIsValid(addr.objectId))
   12708          48 :                 CommandCounterIncrement();
   12709          48 :             table_close(childrel, NoLock);
   12710             :         }
   12711             :     }
   12712             : 
   12713          54 :     return true;
   12714             : }
   12715             : 
   12716             : /*
   12717             :  * A subroutine of ATExecAlterConstrDeferrability that updated constraint
   12718             :  * trigger's deferrability.
   12719             :  *
   12720             :  * The arguments to this function have the same meaning as the arguments to
   12721             :  * ATExecAlterConstrDeferrability.
   12722             :  */
   12723             : static void
   12724         162 : AlterConstrTriggerDeferrability(Oid conoid, Relation tgrel, Relation rel,
   12725             :                                 bool deferrable, bool initdeferred,
   12726             :                                 List **otherrelids)
   12727             : {
   12728             :     HeapTuple   tgtuple;
   12729             :     ScanKeyData tgkey;
   12730             :     SysScanDesc tgscan;
   12731             : 
   12732         162 :     ScanKeyInit(&tgkey,
   12733             :                 Anum_pg_trigger_tgconstraint,
   12734             :                 BTEqualStrategyNumber, F_OIDEQ,
   12735             :                 ObjectIdGetDatum(conoid));
   12736         162 :     tgscan = systable_beginscan(tgrel, TriggerConstraintIndexId, true,
   12737             :                                 NULL, 1, &tgkey);
   12738         630 :     while (HeapTupleIsValid(tgtuple = systable_getnext(tgscan)))
   12739             :     {
   12740         468 :         Form_pg_trigger tgform = (Form_pg_trigger) GETSTRUCT(tgtuple);
   12741             :         Form_pg_trigger copy_tg;
   12742             :         HeapTuple   tgCopyTuple;
   12743             : 
   12744             :         /*
   12745             :          * Remember OIDs of other relation(s) involved in FK constraint.
   12746             :          * (Note: it's likely that we could skip forcing a relcache inval for
   12747             :          * other rels that don't have a trigger whose properties change, but
   12748             :          * let's be conservative.)
   12749             :          */
   12750         468 :         if (tgform->tgrelid != RelationGetRelid(rel))
   12751         228 :             *otherrelids = list_append_unique_oid(*otherrelids,
   12752             :                                                   tgform->tgrelid);
   12753             : 
   12754             :         /*
   12755             :          * Update enable status and deferrability of RI_FKey_noaction_del,
   12756             :          * RI_FKey_noaction_upd, RI_FKey_check_ins and RI_FKey_check_upd
   12757             :          * triggers, but not others; see createForeignKeyActionTriggers and
   12758             :          * CreateFKCheckTrigger.
   12759             :          */
   12760         468 :         if (tgform->tgfoid != F_RI_FKEY_NOACTION_DEL &&
   12761         372 :             tgform->tgfoid != F_RI_FKEY_NOACTION_UPD &&
   12762         258 :             tgform->tgfoid != F_RI_FKEY_CHECK_INS &&
   12763         138 :             tgform->tgfoid != F_RI_FKEY_CHECK_UPD)
   12764          18 :             continue;
   12765             : 
   12766         450 :         tgCopyTuple = heap_copytuple(tgtuple);
   12767         450 :         copy_tg = (Form_pg_trigger) GETSTRUCT(tgCopyTuple);
   12768             : 
   12769         450 :         copy_tg->tgdeferrable = deferrable;
   12770         450 :         copy_tg->tginitdeferred = initdeferred;
   12771         450 :         CatalogTupleUpdate(tgrel, &tgCopyTuple->t_self, tgCopyTuple);
   12772             : 
   12773         450 :         InvokeObjectPostAlterHook(TriggerRelationId, tgform->oid, 0);
   12774             : 
   12775         450 :         heap_freetuple(tgCopyTuple);
   12776             :     }
   12777             : 
   12778         162 :     systable_endscan(tgscan);
   12779         162 : }
   12780             : 
   12781             : /*
   12782             :  * Invokes ATExecAlterConstrEnforceability for each constraint that is a child of
   12783             :  * the specified constraint.
   12784             :  *
   12785             :  * Note that this doesn't handle recursion the normal way, viz. by scanning the
   12786             :  * list of child relations and recursing; instead it uses the conparentid
   12787             :  * relationships.  This may need to be reconsidered.
   12788             :  *
   12789             :  * The arguments to this function have the same meaning as the arguments to
   12790             :  * ATExecAlterConstrEnforceability.
   12791             :  */
   12792             : static void
   12793          48 : AlterConstrEnforceabilityRecurse(List **wqueue, ATAlterConstraint *cmdcon,
   12794             :                                  Relation conrel, Relation tgrel,
   12795             :                                  Oid fkrelid, Oid pkrelid,
   12796             :                                  HeapTuple contuple, LOCKMODE lockmode,
   12797             :                                  Oid ReferencedParentDelTrigger,
   12798             :                                  Oid ReferencedParentUpdTrigger,
   12799             :                                  Oid ReferencingParentInsTrigger,
   12800             :                                  Oid ReferencingParentUpdTrigger)
   12801             : {
   12802             :     Form_pg_constraint currcon;
   12803             :     Oid         conoid;
   12804             :     ScanKeyData pkey;
   12805             :     SysScanDesc pscan;
   12806             :     HeapTuple   childtup;
   12807             : 
   12808          48 :     currcon = (Form_pg_constraint) GETSTRUCT(contuple);
   12809          48 :     conoid = currcon->oid;
   12810             : 
   12811          48 :     ScanKeyInit(&pkey,
   12812             :                 Anum_pg_constraint_conparentid,
   12813             :                 BTEqualStrategyNumber, F_OIDEQ,
   12814             :                 ObjectIdGetDatum(conoid));
   12815             : 
   12816          48 :     pscan = systable_beginscan(conrel, ConstraintParentIndexId,
   12817             :                                true, NULL, 1, &pkey);
   12818             : 
   12819         150 :     while (HeapTupleIsValid(childtup = systable_getnext(pscan)))
   12820         102 :         ATExecAlterConstrEnforceability(wqueue, cmdcon, conrel, tgrel, fkrelid,
   12821             :                                         pkrelid, childtup, lockmode,
   12822             :                                         ReferencedParentDelTrigger,
   12823             :                                         ReferencedParentUpdTrigger,
   12824             :                                         ReferencingParentInsTrigger,
   12825             :                                         ReferencingParentUpdTrigger);
   12826             : 
   12827          48 :     systable_endscan(pscan);
   12828          48 : }
   12829             : 
   12830             : /*
   12831             :  * Invokes ATExecAlterConstrDeferrability for each constraint that is a child of
   12832             :  * the specified constraint.
   12833             :  *
   12834             :  * Note that this doesn't handle recursion the normal way, viz. by scanning the
   12835             :  * list of child relations and recursing; instead it uses the conparentid
   12836             :  * relationships.  This may need to be reconsidered.
   12837             :  *
   12838             :  * The arguments to this function have the same meaning as the arguments to
   12839             :  * ATExecAlterConstrDeferrability.
   12840             :  */
   12841             : static void
   12842          42 : AlterConstrDeferrabilityRecurse(List **wqueue, ATAlterConstraint *cmdcon,
   12843             :                                 Relation conrel, Relation tgrel, Relation rel,
   12844             :                                 HeapTuple contuple, bool recurse,
   12845             :                                 List **otherrelids, LOCKMODE lockmode)
   12846             : {
   12847             :     Form_pg_constraint currcon;
   12848             :     Oid         conoid;
   12849             :     ScanKeyData pkey;
   12850             :     SysScanDesc pscan;
   12851             :     HeapTuple   childtup;
   12852             : 
   12853          42 :     currcon = (Form_pg_constraint) GETSTRUCT(contuple);
   12854          42 :     conoid = currcon->oid;
   12855             : 
   12856          42 :     ScanKeyInit(&pkey,
   12857             :                 Anum_pg_constraint_conparentid,
   12858             :                 BTEqualStrategyNumber, F_OIDEQ,
   12859             :                 ObjectIdGetDatum(conoid));
   12860             : 
   12861          42 :     pscan = systable_beginscan(conrel, ConstraintParentIndexId,
   12862             :                                true, NULL, 1, &pkey);
   12863             : 
   12864         108 :     while (HeapTupleIsValid(childtup = systable_getnext(pscan)))
   12865             :     {
   12866          66 :         Form_pg_constraint childcon = (Form_pg_constraint) GETSTRUCT(childtup);
   12867             :         Relation    childrel;
   12868             : 
   12869          66 :         childrel = table_open(childcon->conrelid, lockmode);
   12870             : 
   12871          66 :         ATExecAlterConstrDeferrability(wqueue, cmdcon, conrel, tgrel, childrel,
   12872             :                                        childtup, recurse, otherrelids, lockmode);
   12873          66 :         table_close(childrel, NoLock);
   12874             :     }
   12875             : 
   12876          42 :     systable_endscan(pscan);
   12877          42 : }
   12878             : 
   12879             : /*
   12880             :  * Update the constraint entry for the given ATAlterConstraint command, and
   12881             :  * invoke the appropriate hooks.
   12882             :  */
   12883             : static void
   12884         396 : AlterConstrUpdateConstraintEntry(ATAlterConstraint *cmdcon, Relation conrel,
   12885             :                                  HeapTuple contuple)
   12886             : {
   12887             :     HeapTuple   copyTuple;
   12888             :     Form_pg_constraint copy_con;
   12889             : 
   12890             :     Assert(cmdcon->alterEnforceability || cmdcon->alterDeferrability ||
   12891             :            cmdcon->alterInheritability);
   12892             : 
   12893         396 :     copyTuple = heap_copytuple(contuple);
   12894         396 :     copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
   12895             : 
   12896         396 :     if (cmdcon->alterEnforceability)
   12897             :     {
   12898         174 :         copy_con->conenforced = cmdcon->is_enforced;
   12899             : 
   12900             :         /*
   12901             :          * NB: The convalidated status is irrelevant when the constraint is
   12902             :          * set to NOT ENFORCED, but for consistency, it should still be set
   12903             :          * appropriately. Similarly, if the constraint is later changed to
   12904             :          * ENFORCED, validation will be performed during phase 3, so it makes
   12905             :          * sense to mark it as valid in that case.
   12906             :          */
   12907         174 :         copy_con->convalidated = cmdcon->is_enforced;
   12908             :     }
   12909         396 :     if (cmdcon->alterDeferrability)
   12910             :     {
   12911         168 :         copy_con->condeferrable = cmdcon->deferrable;
   12912         168 :         copy_con->condeferred = cmdcon->initdeferred;
   12913             :     }
   12914         396 :     if (cmdcon->alterInheritability)
   12915          60 :         copy_con->connoinherit = cmdcon->noinherit;
   12916             : 
   12917         396 :     CatalogTupleUpdate(conrel, &copyTuple->t_self, copyTuple);
   12918         396 :     InvokeObjectPostAlterHook(ConstraintRelationId, copy_con->oid, 0);
   12919             : 
   12920             :     /* Make new constraint flags visible to others */
   12921         396 :     CacheInvalidateRelcacheByRelid(copy_con->conrelid);
   12922             : 
   12923         396 :     heap_freetuple(copyTuple);
   12924         396 : }
   12925             : 
   12926             : /*
   12927             :  * ALTER TABLE VALIDATE CONSTRAINT
   12928             :  *
   12929             :  * XXX The reason we handle recursion here rather than at Phase 1 is because
   12930             :  * there's no good way to skip recursing when handling foreign keys: there is
   12931             :  * no need to lock children in that case, yet we wouldn't be able to avoid
   12932             :  * doing so at that level.
   12933             :  *
   12934             :  * Return value is the address of the validated constraint.  If the constraint
   12935             :  * was already validated, InvalidObjectAddress is returned.
   12936             :  */
   12937             : static ObjectAddress
   12938         590 : ATExecValidateConstraint(List **wqueue, Relation rel, char *constrName,
   12939             :                          bool recurse, bool recursing, LOCKMODE lockmode)
   12940             : {
   12941             :     Relation    conrel;
   12942             :     SysScanDesc scan;
   12943             :     ScanKeyData skey[3];
   12944             :     HeapTuple   tuple;
   12945             :     Form_pg_constraint con;
   12946             :     ObjectAddress address;
   12947             : 
   12948         590 :     conrel = table_open(ConstraintRelationId, RowExclusiveLock);
   12949             : 
   12950             :     /*
   12951             :      * Find and check the target constraint
   12952             :      */
   12953         590 :     ScanKeyInit(&skey[0],
   12954             :                 Anum_pg_constraint_conrelid,
   12955             :                 BTEqualStrategyNumber, F_OIDEQ,
   12956             :                 ObjectIdGetDatum(RelationGetRelid(rel)));
   12957         590 :     ScanKeyInit(&skey[1],
   12958             :                 Anum_pg_constraint_contypid,
   12959             :                 BTEqualStrategyNumber, F_OIDEQ,
   12960             :                 ObjectIdGetDatum(InvalidOid));
   12961         590 :     ScanKeyInit(&skey[2],
   12962             :                 Anum_pg_constraint_conname,
   12963             :                 BTEqualStrategyNumber, F_NAMEEQ,
   12964             :                 CStringGetDatum(constrName));
   12965         590 :     scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
   12966             :                               true, NULL, 3, skey);
   12967             : 
   12968             :     /* There can be at most one matching row */
   12969         590 :     if (!HeapTupleIsValid(tuple = systable_getnext(scan)))
   12970           0 :         ereport(ERROR,
   12971             :                 (errcode(ERRCODE_UNDEFINED_OBJECT),
   12972             :                  errmsg("constraint \"%s\" of relation \"%s\" does not exist",
   12973             :                         constrName, RelationGetRelationName(rel))));
   12974             : 
   12975         590 :     con = (Form_pg_constraint) GETSTRUCT(tuple);
   12976         590 :     if (con->contype != CONSTRAINT_FOREIGN &&
   12977         256 :         con->contype != CONSTRAINT_CHECK &&
   12978         112 :         con->contype != CONSTRAINT_NOTNULL)
   12979           0 :         ereport(ERROR,
   12980             :                 errcode(ERRCODE_WRONG_OBJECT_TYPE),
   12981             :                 errmsg("cannot validate constraint \"%s\" of relation \"%s\"",
   12982             :                        constrName, RelationGetRelationName(rel)),
   12983             :                 errdetail("This operation is not supported for this type of constraint."));
   12984             : 
   12985         590 :     if (!con->conenforced)
   12986           6 :         ereport(ERROR,
   12987             :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
   12988             :                  errmsg("cannot validate NOT ENFORCED constraint")));
   12989             : 
   12990         584 :     if (!con->convalidated)
   12991             :     {
   12992         566 :         if (con->contype == CONSTRAINT_FOREIGN)
   12993             :         {
   12994         328 :             QueueFKConstraintValidation(wqueue, conrel, rel, con->confrelid,
   12995             :                                         tuple, lockmode);
   12996             :         }
   12997         238 :         else if (con->contype == CONSTRAINT_CHECK)
   12998             :         {
   12999         126 :             QueueCheckConstraintValidation(wqueue, conrel, rel, constrName,
   13000             :                                            tuple, recurse, recursing, lockmode);
   13001             :         }
   13002         112 :         else if (con->contype == CONSTRAINT_NOTNULL)
   13003             :         {
   13004         112 :             QueueNNConstraintValidation(wqueue, conrel, rel,
   13005             :                                         tuple, recurse, recursing, lockmode);
   13006             :         }
   13007             : 
   13008         566 :         ObjectAddressSet(address, ConstraintRelationId, con->oid);
   13009             :     }
   13010             :     else
   13011          18 :         address = InvalidObjectAddress; /* already validated */
   13012             : 
   13013         584 :     systable_endscan(scan);
   13014             : 
   13015         584 :     table_close(conrel, RowExclusiveLock);
   13016             : 
   13017         584 :     return address;
   13018             : }
   13019             : 
   13020             : /*
   13021             :  * QueueFKConstraintValidation
   13022             :  *
   13023             :  * Add an entry to the wqueue to validate the given foreign key constraint in
   13024             :  * Phase 3 and update the convalidated field in the pg_constraint catalog
   13025             :  * for the specified relation and all its children.
   13026             :  */
   13027             : static void
   13028         406 : QueueFKConstraintValidation(List **wqueue, Relation conrel, Relation fkrel,
   13029             :                             Oid pkrelid, HeapTuple contuple, LOCKMODE lockmode)
   13030             : {
   13031             :     Form_pg_constraint con;
   13032             :     AlteredTableInfo *tab;
   13033             :     HeapTuple   copyTuple;
   13034             :     Form_pg_constraint copy_con;
   13035             : 
   13036         406 :     con = (Form_pg_constraint) GETSTRUCT(contuple);
   13037             :     Assert(con->contype == CONSTRAINT_FOREIGN);
   13038             :     Assert(!con->convalidated);
   13039             : 
   13040             :     /*
   13041             :      * Add the validation to phase 3's queue; not needed for partitioned
   13042             :      * tables themselves, only for their partitions.
   13043             :      *
   13044             :      * When the referenced table (pkrelid) is partitioned, the referencing
   13045             :      * table (fkrel) has one pg_constraint row pointing to each partition
   13046             :      * thereof.  These rows are there only to support action triggers and no
   13047             :      * table scan is needed, therefore skip this for them as well.
   13048             :      */
   13049         406 :     if (fkrel->rd_rel->relkind == RELKIND_RELATION &&
   13050         358 :         con->confrelid == pkrelid)
   13051             :     {
   13052             :         NewConstraint *newcon;
   13053             :         Constraint *fkconstraint;
   13054             : 
   13055             :         /* Queue validation for phase 3 */
   13056         340 :         fkconstraint = makeNode(Constraint);
   13057             :         /* for now this is all we need */
   13058         340 :         fkconstraint->conname = pstrdup(NameStr(con->conname));
   13059             : 
   13060         340 :         newcon = palloc0_object(NewConstraint);
   13061         340 :         newcon->name = fkconstraint->conname;
   13062         340 :         newcon->contype = CONSTR_FOREIGN;
   13063         340 :         newcon->refrelid = con->confrelid;
   13064         340 :         newcon->refindid = con->conindid;
   13065         340 :         newcon->conid = con->oid;
   13066         340 :         newcon->qual = (Node *) fkconstraint;
   13067             : 
   13068             :         /* Find or create work queue entry for this table */
   13069         340 :         tab = ATGetQueueEntry(wqueue, fkrel);
   13070         340 :         tab->constraints = lappend(tab->constraints, newcon);
   13071             :     }
   13072             : 
   13073             :     /*
   13074             :      * If the table at either end of the constraint is partitioned, we need to
   13075             :      * recurse and handle every unvalidated constraint that is a child of this
   13076             :      * constraint.
   13077             :      */
   13078         764 :     if (fkrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
   13079         358 :         get_rel_relkind(con->confrelid) == RELKIND_PARTITIONED_TABLE)
   13080             :     {
   13081             :         ScanKeyData pkey;
   13082             :         SysScanDesc pscan;
   13083             :         HeapTuple   childtup;
   13084             : 
   13085          78 :         ScanKeyInit(&pkey,
   13086             :                     Anum_pg_constraint_conparentid,
   13087             :                     BTEqualStrategyNumber, F_OIDEQ,
   13088             :                     ObjectIdGetDatum(con->oid));
   13089             : 
   13090          78 :         pscan = systable_beginscan(conrel, ConstraintParentIndexId,
   13091             :                                    true, NULL, 1, &pkey);
   13092             : 
   13093         156 :         while (HeapTupleIsValid(childtup = systable_getnext(pscan)))
   13094             :         {
   13095             :             Form_pg_constraint childcon;
   13096             :             Relation    childrel;
   13097             : 
   13098          78 :             childcon = (Form_pg_constraint) GETSTRUCT(childtup);
   13099             : 
   13100             :             /*
   13101             :              * If the child constraint has already been validated, no further
   13102             :              * action is required for it or its descendants, as they are all
   13103             :              * valid.
   13104             :              */
   13105          78 :             if (childcon->convalidated)
   13106          18 :                 continue;
   13107             : 
   13108          60 :             childrel = table_open(childcon->conrelid, lockmode);
   13109             : 
   13110             :             /*
   13111             :              * NB: Note that pkrelid should be passed as-is during recursion,
   13112             :              * as it is required to identify the root referenced table.
   13113             :              */
   13114          60 :             QueueFKConstraintValidation(wqueue, conrel, childrel, pkrelid,
   13115             :                                         childtup, lockmode);
   13116          60 :             table_close(childrel, NoLock);
   13117             :         }
   13118             : 
   13119          78 :         systable_endscan(pscan);
   13120             :     }
   13121             : 
   13122             :     /*
   13123             :      * Now mark the pg_constraint row as validated (even if we didn't check,
   13124             :      * notably the ones for partitions on the referenced side).
   13125             :      *
   13126             :      * We rely on transaction abort to roll back this change if phase 3
   13127             :      * ultimately finds violating rows.  This is a bit ugly.
   13128             :      */
   13129         406 :     copyTuple = heap_copytuple(contuple);
   13130         406 :     copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
   13131         406 :     copy_con->convalidated = true;
   13132         406 :     CatalogTupleUpdate(conrel, &copyTuple->t_self, copyTuple);
   13133             : 
   13134         406 :     InvokeObjectPostAlterHook(ConstraintRelationId, con->oid, 0);
   13135             : 
   13136         406 :     heap_freetuple(copyTuple);
   13137         406 : }
   13138             : 
   13139             : /*
   13140             :  * QueueCheckConstraintValidation
   13141             :  *
   13142             :  * Add an entry to the wqueue to validate the given check constraint in Phase 3
   13143             :  * and update the convalidated field in the pg_constraint catalog for the
   13144             :  * specified relation and all its inheriting children.
   13145             :  */
   13146             : static void
   13147         126 : QueueCheckConstraintValidation(List **wqueue, Relation conrel, Relation rel,
   13148             :                                char *constrName, HeapTuple contuple,
   13149             :                                bool recurse, bool recursing, LOCKMODE lockmode)
   13150             : {
   13151             :     Form_pg_constraint con;
   13152             :     AlteredTableInfo *tab;
   13153             :     HeapTuple   copyTuple;
   13154             :     Form_pg_constraint copy_con;
   13155             : 
   13156         126 :     List       *children = NIL;
   13157             :     ListCell   *child;
   13158             :     NewConstraint *newcon;
   13159             :     Datum       val;
   13160             :     char       *conbin;
   13161             : 
   13162         126 :     con = (Form_pg_constraint) GETSTRUCT(contuple);
   13163             :     Assert(con->contype == CONSTRAINT_CHECK);
   13164             : 
   13165             :     /*
   13166             :      * If we're recursing, the parent has already done this, so skip it. Also,
   13167             :      * if the constraint is a NO INHERIT constraint, we shouldn't try to look
   13168             :      * for it in the children.
   13169             :      */
   13170         126 :     if (!recursing && !con->connoinherit)
   13171          72 :         children = find_all_inheritors(RelationGetRelid(rel),
   13172             :                                        lockmode, NULL);
   13173             : 
   13174             :     /*
   13175             :      * For CHECK constraints, we must ensure that we only mark the constraint
   13176             :      * as validated on the parent if it's already validated on the children.
   13177             :      *
   13178             :      * We recurse before validating on the parent, to reduce risk of
   13179             :      * deadlocks.
   13180             :      */
   13181         246 :     foreach(child, children)
   13182             :     {
   13183         120 :         Oid         childoid = lfirst_oid(child);
   13184             :         Relation    childrel;
   13185             : 
   13186         120 :         if (childoid == RelationGetRelid(rel))
   13187          72 :             continue;
   13188             : 
   13189             :         /*
   13190             :          * If we are told not to recurse, there had better not be any child
   13191             :          * tables, because we can't mark the constraint on the parent valid
   13192             :          * unless it is valid for all child tables.
   13193             :          */
   13194          48 :         if (!recurse)
   13195           0 :             ereport(ERROR,
   13196             :                     (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
   13197             :                      errmsg("constraint must be validated on child tables too")));
   13198             : 
   13199             :         /* find_all_inheritors already got lock */
   13200          48 :         childrel = table_open(childoid, NoLock);
   13201             : 
   13202          48 :         ATExecValidateConstraint(wqueue, childrel, constrName, false,
   13203             :                                  true, lockmode);
   13204          48 :         table_close(childrel, NoLock);
   13205             :     }
   13206             : 
   13207             :     /* Queue validation for phase 3 */
   13208         126 :     newcon = palloc0_object(NewConstraint);
   13209         126 :     newcon->name = constrName;
   13210         126 :     newcon->contype = CONSTR_CHECK;
   13211         126 :     newcon->refrelid = InvalidOid;
   13212         126 :     newcon->refindid = InvalidOid;
   13213         126 :     newcon->conid = con->oid;
   13214             : 
   13215         126 :     val = SysCacheGetAttrNotNull(CONSTROID, contuple,
   13216             :                                  Anum_pg_constraint_conbin);
   13217         126 :     conbin = TextDatumGetCString(val);
   13218         126 :     newcon->qual = expand_generated_columns_in_expr(stringToNode(conbin), rel, 1);
   13219             : 
   13220             :     /* Find or create work queue entry for this table */
   13221         126 :     tab = ATGetQueueEntry(wqueue, rel);
   13222         126 :     tab->constraints = lappend(tab->constraints, newcon);
   13223             : 
   13224             :     /*
   13225             :      * Invalidate relcache so that others see the new validated constraint.
   13226             :      */
   13227         126 :     CacheInvalidateRelcache(rel);
   13228             : 
   13229             :     /*
   13230             :      * Now update the catalog, while we have the door open.
   13231             :      */
   13232         126 :     copyTuple = heap_copytuple(contuple);
   13233         126 :     copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
   13234         126 :     copy_con->convalidated = true;
   13235         126 :     CatalogTupleUpdate(conrel, &copyTuple->t_self, copyTuple);
   13236             : 
   13237         126 :     InvokeObjectPostAlterHook(ConstraintRelationId, con->oid, 0);
   13238             : 
   13239         126 :     heap_freetuple(copyTuple);
   13240         126 : }
   13241             : 
   13242             : /*
   13243             :  * QueueNNConstraintValidation
   13244             :  *
   13245             :  * Add an entry to the wqueue to validate the given not-null constraint in
   13246             :  * Phase 3 and update the convalidated field in the pg_constraint catalog for
   13247             :  * the specified relation and all its inheriting children.
   13248             :  */
   13249             : static void
   13250         112 : QueueNNConstraintValidation(List **wqueue, Relation conrel, Relation rel,
   13251             :                             HeapTuple contuple, bool recurse, bool recursing,
   13252             :                             LOCKMODE lockmode)
   13253             : {
   13254             :     Form_pg_constraint con;
   13255             :     AlteredTableInfo *tab;
   13256             :     HeapTuple   copyTuple;
   13257             :     Form_pg_constraint copy_con;
   13258         112 :     List       *children = NIL;
   13259             :     AttrNumber  attnum;
   13260             :     char       *colname;
   13261             : 
   13262         112 :     con = (Form_pg_constraint) GETSTRUCT(contuple);
   13263             :     Assert(con->contype == CONSTRAINT_NOTNULL);
   13264             : 
   13265         112 :     attnum = extractNotNullColumn(contuple);
   13266             : 
   13267             :     /*
   13268             :      * If we're recursing, we've already done this for parent, so skip it.
   13269             :      * Also, if the constraint is a NO INHERIT constraint, we shouldn't try to
   13270             :      * look for it in the children.
   13271             :      *
   13272             :      * We recurse before validating on the parent, to reduce risk of
   13273             :      * deadlocks.
   13274             :      */
   13275         112 :     if (!recursing && !con->connoinherit)
   13276          76 :         children = find_all_inheritors(RelationGetRelid(rel), lockmode, NULL);
   13277             : 
   13278         112 :     colname = get_attname(RelationGetRelid(rel), attnum, false);
   13279         378 :     foreach_oid(childoid, children)
   13280             :     {
   13281             :         Relation    childrel;
   13282             :         HeapTuple   contup;
   13283             :         Form_pg_constraint childcon;
   13284             :         char       *conname;
   13285             : 
   13286         154 :         if (childoid == RelationGetRelid(rel))
   13287          76 :             continue;
   13288             : 
   13289             :         /*
   13290             :          * If we are told not to recurse, there had better not be any child
   13291             :          * tables, because we can't mark the constraint on the parent valid
   13292             :          * unless it is valid for all child tables.
   13293             :          */
   13294          78 :         if (!recurse)
   13295           0 :             ereport(ERROR,
   13296             :                     errcode(ERRCODE_INVALID_TABLE_DEFINITION),
   13297             :                     errmsg("constraint must be validated on child tables too"));
   13298             : 
   13299             :         /*
   13300             :          * The column on child might have a different attnum, so search by
   13301             :          * column name.
   13302             :          */
   13303          78 :         contup = findNotNullConstraint(childoid, colname);
   13304          78 :         if (!contup)
   13305           0 :             elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation \"%s\"",
   13306             :                  colname, get_rel_name(childoid));
   13307          78 :         childcon = (Form_pg_constraint) GETSTRUCT(contup);
   13308          78 :         if (childcon->convalidated)
   13309          42 :             continue;
   13310             : 
   13311             :         /* find_all_inheritors already got lock */
   13312          36 :         childrel = table_open(childoid, NoLock);
   13313          36 :         conname = pstrdup(NameStr(childcon->conname));
   13314             : 
   13315             :         /* XXX improve ATExecValidateConstraint API to avoid double search */
   13316          36 :         ATExecValidateConstraint(wqueue, childrel, conname,
   13317             :                                  false, true, lockmode);
   13318          36 :         table_close(childrel, NoLock);
   13319             :     }
   13320             : 
   13321             :     /* Set attnotnull appropriately without queueing another validation */
   13322         112 :     set_attnotnull(NULL, rel, attnum, true, false);
   13323             : 
   13324         112 :     tab = ATGetQueueEntry(wqueue, rel);
   13325         112 :     tab->verify_new_notnull = true;
   13326             : 
   13327             :     /*
   13328             :      * Invalidate relcache so that others see the new validated constraint.
   13329             :      */
   13330         112 :     CacheInvalidateRelcache(rel);
   13331             : 
   13332             :     /*
   13333             :      * Now update the catalogs, while we have the door open.
   13334             :      */
   13335         112 :     copyTuple = heap_copytuple(contuple);
   13336         112 :     copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
   13337         112 :     copy_con->convalidated = true;
   13338         112 :     CatalogTupleUpdate(conrel, &copyTuple->t_self, copyTuple);
   13339             : 
   13340         112 :     InvokeObjectPostAlterHook(ConstraintRelationId, con->oid, 0);
   13341             : 
   13342         112 :     heap_freetuple(copyTuple);
   13343         112 : }
   13344             : 
   13345             : /*
   13346             :  * transformColumnNameList - transform list of column names
   13347             :  *
   13348             :  * Lookup each name and return its attnum and, optionally, type and collation
   13349             :  * OIDs
   13350             :  *
   13351             :  * Note: the name of this function suggests that it's general-purpose,
   13352             :  * but actually it's only used to look up names appearing in foreign-key
   13353             :  * clauses.  The error messages would need work to use it in other cases,
   13354             :  * and perhaps the validity checks as well.
   13355             :  */
   13356             : static int
   13357        6658 : transformColumnNameList(Oid relId, List *colList,
   13358             :                         int16 *attnums, Oid *atttypids, Oid *attcollids)
   13359             : {
   13360             :     ListCell   *l;
   13361             :     int         attnum;
   13362             : 
   13363        6658 :     attnum = 0;
   13364       12142 :     foreach(l, colList)
   13365             :     {
   13366        5550 :         char       *attname = strVal(lfirst(l));
   13367             :         HeapTuple   atttuple;
   13368             :         Form_pg_attribute attform;
   13369             : 
   13370        5550 :         atttuple = SearchSysCacheAttName(relId, attname);
   13371        5550 :         if (!HeapTupleIsValid(atttuple))
   13372          54 :             ereport(ERROR,
   13373             :                     (errcode(ERRCODE_UNDEFINED_COLUMN),
   13374             :                      errmsg("column \"%s\" referenced in foreign key constraint does not exist",
   13375             :                             attname)));
   13376        5496 :         attform = (Form_pg_attribute) GETSTRUCT(atttuple);
   13377        5496 :         if (attform->attnum < 0)
   13378          12 :             ereport(ERROR,
   13379             :                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   13380             :                      errmsg("system columns cannot be used in foreign keys")));
   13381        5484 :         if (attnum >= INDEX_MAX_KEYS)
   13382           0 :             ereport(ERROR,
   13383             :                     (errcode(ERRCODE_TOO_MANY_COLUMNS),
   13384             :                      errmsg("cannot have more than %d keys in a foreign key",
   13385             :                             INDEX_MAX_KEYS)));
   13386        5484 :         attnums[attnum] = attform->attnum;
   13387        5484 :         if (atttypids != NULL)
   13388        5448 :             atttypids[attnum] = attform->atttypid;
   13389        5484 :         if (attcollids != NULL)
   13390        5448 :             attcollids[attnum] = attform->attcollation;
   13391        5484 :         ReleaseSysCache(atttuple);
   13392        5484 :         attnum++;
   13393             :     }
   13394             : 
   13395        6592 :     return attnum;
   13396             : }
   13397             : 
   13398             : /*
   13399             :  * transformFkeyGetPrimaryKey -
   13400             :  *
   13401             :  *  Look up the names, attnums, types, and collations of the primary key attributes
   13402             :  *  for the pkrel.  Also return the index OID and index opclasses of the
   13403             :  *  index supporting the primary key.  Also return whether the index has
   13404             :  *  WITHOUT OVERLAPS.
   13405             :  *
   13406             :  *  All parameters except pkrel are output parameters.  Also, the function
   13407             :  *  return value is the number of attributes in the primary key.
   13408             :  *
   13409             :  *  Used when the column list in the REFERENCES specification is omitted.
   13410             :  */
   13411             : static int
   13412        1268 : transformFkeyGetPrimaryKey(Relation pkrel, Oid *indexOid,
   13413             :                            List **attnamelist,
   13414             :                            int16 *attnums, Oid *atttypids, Oid *attcollids,
   13415             :                            Oid *opclasses, bool *pk_has_without_overlaps)
   13416             : {
   13417             :     List       *indexoidlist;
   13418             :     ListCell   *indexoidscan;
   13419        1268 :     HeapTuple   indexTuple = NULL;
   13420        1268 :     Form_pg_index indexStruct = NULL;
   13421             :     Datum       indclassDatum;
   13422             :     oidvector  *indclass;
   13423             :     int         i;
   13424             : 
   13425             :     /*
   13426             :      * Get the list of index OIDs for the table from the relcache, and look up
   13427             :      * each one in the pg_index syscache until we find one marked primary key
   13428             :      * (hopefully there isn't more than one such).  Insist it's valid, too.
   13429             :      */
   13430        1268 :     *indexOid = InvalidOid;
   13431             : 
   13432        1268 :     indexoidlist = RelationGetIndexList(pkrel);
   13433             : 
   13434        1274 :     foreach(indexoidscan, indexoidlist)
   13435             :     {
   13436        1274 :         Oid         indexoid = lfirst_oid(indexoidscan);
   13437             : 
   13438        1274 :         indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexoid));
   13439        1274 :         if (!HeapTupleIsValid(indexTuple))
   13440           0 :             elog(ERROR, "cache lookup failed for index %u", indexoid);
   13441        1274 :         indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
   13442        1274 :         if (indexStruct->indisprimary && indexStruct->indisvalid)
   13443             :         {
   13444             :             /*
   13445             :              * Refuse to use a deferrable primary key.  This is per SQL spec,
   13446             :              * and there would be a lot of interesting semantic problems if we
   13447             :              * tried to allow it.
   13448             :              */
   13449        1268 :             if (!indexStruct->indimmediate)
   13450           0 :                 ereport(ERROR,
   13451             :                         (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
   13452             :                          errmsg("cannot use a deferrable primary key for referenced table \"%s\"",
   13453             :                                 RelationGetRelationName(pkrel))));
   13454             : 
   13455        1268 :             *indexOid = indexoid;
   13456        1268 :             break;
   13457             :         }
   13458           6 :         ReleaseSysCache(indexTuple);
   13459             :     }
   13460             : 
   13461        1268 :     list_free(indexoidlist);
   13462             : 
   13463             :     /*
   13464             :      * Check that we found it
   13465             :      */
   13466        1268 :     if (!OidIsValid(*indexOid))
   13467           0 :         ereport(ERROR,
   13468             :                 (errcode(ERRCODE_UNDEFINED_OBJECT),
   13469             :                  errmsg("there is no primary key for referenced table \"%s\"",
   13470             :                         RelationGetRelationName(pkrel))));
   13471             : 
   13472             :     /* Must get indclass the hard way */
   13473        1268 :     indclassDatum = SysCacheGetAttrNotNull(INDEXRELID, indexTuple,
   13474             :                                            Anum_pg_index_indclass);
   13475        1268 :     indclass = (oidvector *) DatumGetPointer(indclassDatum);
   13476             : 
   13477             :     /*
   13478             :      * Now build the list of PK attributes from the indkey definition (we
   13479             :      * assume a primary key cannot have expressional elements)
   13480             :      */
   13481        1268 :     *attnamelist = NIL;
   13482        3014 :     for (i = 0; i < indexStruct->indnkeyatts; i++)
   13483             :     {
   13484        1746 :         int         pkattno = indexStruct->indkey.values[i];
   13485             : 
   13486        1746 :         attnums[i] = pkattno;
   13487        1746 :         atttypids[i] = attnumTypeId(pkrel, pkattno);
   13488        1746 :         attcollids[i] = attnumCollationId(pkrel, pkattno);
   13489        1746 :         opclasses[i] = indclass->values[i];
   13490        1746 :         *attnamelist = lappend(*attnamelist,
   13491        1746 :                                makeString(pstrdup(NameStr(*attnumAttName(pkrel, pkattno)))));
   13492             :     }
   13493             : 
   13494        1268 :     *pk_has_without_overlaps = indexStruct->indisexclusion;
   13495             : 
   13496        1268 :     ReleaseSysCache(indexTuple);
   13497             : 
   13498        1268 :     return i;
   13499             : }
   13500             : 
   13501             : /*
   13502             :  * transformFkeyCheckAttrs -
   13503             :  *
   13504             :  *  Validate that the 'attnums' columns in the 'pkrel' relation are valid to
   13505             :  *  reference as part of a foreign key constraint.
   13506             :  *
   13507             :  *  Returns the OID of the unique index supporting the constraint and
   13508             :  *  populates the caller-provided 'opclasses' array with the opclasses
   13509             :  *  associated with the index columns.  Also sets whether the index
   13510             :  *  uses WITHOUT OVERLAPS.
   13511             :  *
   13512             :  *  Raises an ERROR on validation failure.
   13513             :  */
   13514             : static Oid
   13515        1294 : transformFkeyCheckAttrs(Relation pkrel,
   13516             :                         int numattrs, int16 *attnums,
   13517             :                         bool with_period, Oid *opclasses,
   13518             :                         bool *pk_has_without_overlaps)
   13519             : {
   13520        1294 :     Oid         indexoid = InvalidOid;
   13521        1294 :     bool        found = false;
   13522        1294 :     bool        found_deferrable = false;
   13523             :     List       *indexoidlist;
   13524             :     ListCell   *indexoidscan;
   13525             :     int         i,
   13526             :                 j;
   13527             : 
   13528             :     /*
   13529             :      * Reject duplicate appearances of columns in the referenced-columns list.
   13530             :      * Such a case is forbidden by the SQL standard, and even if we thought it
   13531             :      * useful to allow it, there would be ambiguity about how to match the
   13532             :      * list to unique indexes (in particular, it'd be unclear which index
   13533             :      * opclass goes with which FK column).
   13534             :      */
   13535        3016 :     for (i = 0; i < numattrs; i++)
   13536             :     {
   13537        2268 :         for (j = i + 1; j < numattrs; j++)
   13538             :         {
   13539         546 :             if (attnums[i] == attnums[j])
   13540          24 :                 ereport(ERROR,
   13541             :                         (errcode(ERRCODE_INVALID_FOREIGN_KEY),
   13542             :                          errmsg("foreign key referenced-columns list must not contain duplicates")));
   13543             :         }
   13544             :     }
   13545             : 
   13546             :     /*
   13547             :      * Get the list of index OIDs for the table from the relcache, and look up
   13548             :      * each one in the pg_index syscache, and match unique indexes to the list
   13549             :      * of attnums we are given.
   13550             :      */
   13551        1270 :     indexoidlist = RelationGetIndexList(pkrel);
   13552             : 
   13553        1450 :     foreach(indexoidscan, indexoidlist)
   13554             :     {
   13555             :         HeapTuple   indexTuple;
   13556             :         Form_pg_index indexStruct;
   13557             : 
   13558        1438 :         indexoid = lfirst_oid(indexoidscan);
   13559        1438 :         indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexoid));
   13560        1438 :         if (!HeapTupleIsValid(indexTuple))
   13561           0 :             elog(ERROR, "cache lookup failed for index %u", indexoid);
   13562        1438 :         indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
   13563             : 
   13564             :         /*
   13565             :          * Must have the right number of columns; must be unique (or if
   13566             :          * temporal then exclusion instead) and not a partial index; forget it
   13567             :          * if there are any expressions, too. Invalid indexes are out as well.
   13568             :          */
   13569        2768 :         if (indexStruct->indnkeyatts == numattrs &&
   13570        1330 :             (with_period ? indexStruct->indisexclusion : indexStruct->indisunique) &&
   13571        2632 :             indexStruct->indisvalid &&
   13572        2632 :             heap_attisnull(indexTuple, Anum_pg_index_indpred, NULL) &&
   13573        1316 :             heap_attisnull(indexTuple, Anum_pg_index_indexprs, NULL))
   13574             :         {
   13575             :             Datum       indclassDatum;
   13576             :             oidvector  *indclass;
   13577             : 
   13578             :             /* Must get indclass the hard way */
   13579        1316 :             indclassDatum = SysCacheGetAttrNotNull(INDEXRELID, indexTuple,
   13580             :                                                    Anum_pg_index_indclass);
   13581        1316 :             indclass = (oidvector *) DatumGetPointer(indclassDatum);
   13582             : 
   13583             :             /*
   13584             :              * The given attnum list may match the index columns in any order.
   13585             :              * Check for a match, and extract the appropriate opclasses while
   13586             :              * we're at it.
   13587             :              *
   13588             :              * We know that attnums[] is duplicate-free per the test at the
   13589             :              * start of this function, and we checked above that the number of
   13590             :              * index columns agrees, so if we find a match for each attnums[]
   13591             :              * entry then we must have a one-to-one match in some order.
   13592             :              */
   13593        3026 :             for (i = 0; i < numattrs; i++)
   13594             :             {
   13595        1768 :                 found = false;
   13596        2348 :                 for (j = 0; j < numattrs; j++)
   13597             :                 {
   13598        2290 :                     if (attnums[i] == indexStruct->indkey.values[j])
   13599             :                     {
   13600        1710 :                         opclasses[i] = indclass->values[j];
   13601        1710 :                         found = true;
   13602        1710 :                         break;
   13603             :                     }
   13604             :                 }
   13605        1768 :                 if (!found)
   13606          58 :                     break;
   13607             :             }
   13608             :             /* The last attribute in the index must be the PERIOD FK part */
   13609        1316 :             if (found && with_period)
   13610             :             {
   13611         122 :                 int16       periodattnum = attnums[numattrs - 1];
   13612             : 
   13613         122 :                 found = (periodattnum == indexStruct->indkey.values[numattrs - 1]);
   13614             :             }
   13615             : 
   13616             :             /*
   13617             :              * Refuse to use a deferrable unique/primary key.  This is per SQL
   13618             :              * spec, and there would be a lot of interesting semantic problems
   13619             :              * if we tried to allow it.
   13620             :              */
   13621        1316 :             if (found && !indexStruct->indimmediate)
   13622             :             {
   13623             :                 /*
   13624             :                  * Remember that we found an otherwise matching index, so that
   13625             :                  * we can generate a more appropriate error message.
   13626             :                  */
   13627           0 :                 found_deferrable = true;
   13628           0 :                 found = false;
   13629             :             }
   13630             : 
   13631             :             /* We need to know whether the index has WITHOUT OVERLAPS */
   13632        1316 :             if (found)
   13633        1258 :                 *pk_has_without_overlaps = indexStruct->indisexclusion;
   13634             :         }
   13635        1438 :         ReleaseSysCache(indexTuple);
   13636        1438 :         if (found)
   13637        1258 :             break;
   13638             :     }
   13639             : 
   13640        1270 :     if (!found)
   13641             :     {
   13642          12 :         if (found_deferrable)
   13643           0 :             ereport(ERROR,
   13644             :                     (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
   13645             :                      errmsg("cannot use a deferrable unique constraint for referenced table \"%s\"",
   13646             :                             RelationGetRelationName(pkrel))));
   13647             :         else
   13648          12 :             ereport(ERROR,
   13649             :                     (errcode(ERRCODE_INVALID_FOREIGN_KEY),
   13650             :                      errmsg("there is no unique constraint matching given keys for referenced table \"%s\"",
   13651             :                             RelationGetRelationName(pkrel))));
   13652             :     }
   13653             : 
   13654        1258 :     list_free(indexoidlist);
   13655             : 
   13656        1258 :     return indexoid;
   13657             : }
   13658             : 
   13659             : /*
   13660             :  * findFkeyCast -
   13661             :  *
   13662             :  *  Wrapper around find_coercion_pathway() for ATAddForeignKeyConstraint().
   13663             :  *  Caller has equal regard for binary coercibility and for an exact match.
   13664             : */
   13665             : static CoercionPathType
   13666          12 : findFkeyCast(Oid targetTypeId, Oid sourceTypeId, Oid *funcid)
   13667             : {
   13668             :     CoercionPathType ret;
   13669             : 
   13670          12 :     if (targetTypeId == sourceTypeId)
   13671             :     {
   13672          12 :         ret = COERCION_PATH_RELABELTYPE;
   13673          12 :         *funcid = InvalidOid;
   13674             :     }
   13675             :     else
   13676             :     {
   13677           0 :         ret = find_coercion_pathway(targetTypeId, sourceTypeId,
   13678             :                                     COERCION_IMPLICIT, funcid);
   13679           0 :         if (ret == COERCION_PATH_NONE)
   13680             :             /* A previously-relied-upon cast is now gone. */
   13681           0 :             elog(ERROR, "could not find cast from %u to %u",
   13682             :                  sourceTypeId, targetTypeId);
   13683             :     }
   13684             : 
   13685          12 :     return ret;
   13686             : }
   13687             : 
   13688             : /*
   13689             :  * Permissions checks on the referenced table for ADD FOREIGN KEY
   13690             :  *
   13691             :  * Note: we have already checked that the user owns the referencing table,
   13692             :  * else we'd have failed much earlier; no additional checks are needed for it.
   13693             :  */
   13694             : static void
   13695        2490 : checkFkeyPermissions(Relation rel, int16 *attnums, int natts)
   13696             : {
   13697        2490 :     Oid         roleid = GetUserId();
   13698             :     AclResult   aclresult;
   13699             :     int         i;
   13700             : 
   13701             :     /* Okay if we have relation-level REFERENCES permission */
   13702        2490 :     aclresult = pg_class_aclcheck(RelationGetRelid(rel), roleid,
   13703             :                                   ACL_REFERENCES);
   13704        2490 :     if (aclresult == ACLCHECK_OK)
   13705        2490 :         return;
   13706             :     /* Else we must have REFERENCES on each column */
   13707           0 :     for (i = 0; i < natts; i++)
   13708             :     {
   13709           0 :         aclresult = pg_attribute_aclcheck(RelationGetRelid(rel), attnums[i],
   13710             :                                           roleid, ACL_REFERENCES);
   13711           0 :         if (aclresult != ACLCHECK_OK)
   13712           0 :             aclcheck_error(aclresult, get_relkind_objtype(rel->rd_rel->relkind),
   13713           0 :                            RelationGetRelationName(rel));
   13714             :     }
   13715             : }
   13716             : 
   13717             : /*
   13718             :  * Scan the existing rows in a table to verify they meet a proposed FK
   13719             :  * constraint.
   13720             :  *
   13721             :  * Caller must have opened and locked both relations appropriately.
   13722             :  */
   13723             : static void
   13724        1178 : validateForeignKeyConstraint(char *conname,
   13725             :                              Relation rel,
   13726             :                              Relation pkrel,
   13727             :                              Oid pkindOid,
   13728             :                              Oid constraintOid,
   13729             :                              bool hasperiod)
   13730             : {
   13731             :     TupleTableSlot *slot;
   13732             :     TableScanDesc scan;
   13733        1178 :     Trigger     trig = {0};
   13734             :     Snapshot    snapshot;
   13735             :     MemoryContext oldcxt;
   13736             :     MemoryContext perTupCxt;
   13737             : 
   13738        1178 :     ereport(DEBUG1,
   13739             :             (errmsg_internal("validating foreign key constraint \"%s\"", conname)));
   13740             : 
   13741             :     /*
   13742             :      * Build a trigger call structure; we'll need it either way.
   13743             :      */
   13744        1178 :     trig.tgoid = InvalidOid;
   13745        1178 :     trig.tgname = conname;
   13746        1178 :     trig.tgenabled = TRIGGER_FIRES_ON_ORIGIN;
   13747        1178 :     trig.tgisinternal = true;
   13748        1178 :     trig.tgconstrrelid = RelationGetRelid(pkrel);
   13749        1178 :     trig.tgconstrindid = pkindOid;
   13750        1178 :     trig.tgconstraint = constraintOid;
   13751        1178 :     trig.tgdeferrable = false;
   13752        1178 :     trig.tginitdeferred = false;
   13753             :     /* we needn't fill in remaining fields */
   13754             : 
   13755             :     /*
   13756             :      * See if we can do it with a single LEFT JOIN query.  A false result
   13757             :      * indicates we must proceed with the fire-the-trigger method. We can't do
   13758             :      * a LEFT JOIN for temporal FKs yet, but we can once we support temporal
   13759             :      * left joins.
   13760             :      */
   13761        1178 :     if (!hasperiod && RI_Initial_Check(&trig, rel, pkrel))
   13762         984 :         return;
   13763             : 
   13764             :     /*
   13765             :      * Scan through each tuple, calling RI_FKey_check_ins (insert trigger) as
   13766             :      * if that tuple had just been inserted.  If any of those fail, it should
   13767             :      * ereport(ERROR) and that's that.
   13768             :      */
   13769         108 :     snapshot = RegisterSnapshot(GetLatestSnapshot());
   13770         108 :     slot = table_slot_create(rel, NULL);
   13771         108 :     scan = table_beginscan(rel, snapshot, 0, NULL);
   13772             : 
   13773         108 :     perTupCxt = AllocSetContextCreate(CurrentMemoryContext,
   13774             :                                       "validateForeignKeyConstraint",
   13775             :                                       ALLOCSET_SMALL_SIZES);
   13776         108 :     oldcxt = MemoryContextSwitchTo(perTupCxt);
   13777             : 
   13778         192 :     while (table_scan_getnextslot(scan, ForwardScanDirection, slot))
   13779             :     {
   13780         102 :         LOCAL_FCINFO(fcinfo, 0);
   13781         102 :         TriggerData trigdata = {0};
   13782             : 
   13783         102 :         CHECK_FOR_INTERRUPTS();
   13784             : 
   13785             :         /*
   13786             :          * Make a call to the trigger function
   13787             :          *
   13788             :          * No parameters are passed, but we do set a context
   13789             :          */
   13790         510 :         MemSet(fcinfo, 0, SizeForFunctionCallInfo(0));
   13791             : 
   13792             :         /*
   13793             :          * We assume RI_FKey_check_ins won't look at flinfo...
   13794             :          */
   13795         102 :         trigdata.type = T_TriggerData;
   13796         102 :         trigdata.tg_event = TRIGGER_EVENT_INSERT | TRIGGER_EVENT_ROW;
   13797         102 :         trigdata.tg_relation = rel;
   13798         102 :         trigdata.tg_trigtuple = ExecFetchSlotHeapTuple(slot, false, NULL);
   13799         102 :         trigdata.tg_trigslot = slot;
   13800         102 :         trigdata.tg_trigger = &trig;
   13801             : 
   13802         102 :         fcinfo->context = (Node *) &trigdata;
   13803             : 
   13804         102 :         RI_FKey_check_ins(fcinfo);
   13805             : 
   13806          84 :         MemoryContextReset(perTupCxt);
   13807             :     }
   13808             : 
   13809          90 :     MemoryContextSwitchTo(oldcxt);
   13810          90 :     MemoryContextDelete(perTupCxt);
   13811          90 :     table_endscan(scan);
   13812          90 :     UnregisterSnapshot(snapshot);
   13813          90 :     ExecDropSingleTupleTableSlot(slot);
   13814             : }
   13815             : 
   13816             : /*
   13817             :  * CreateFKCheckTrigger
   13818             :  *      Creates the insert (on_insert=true) or update "check" trigger that
   13819             :  *      implements a given foreign key
   13820             :  *
   13821             :  * Returns the OID of the so created trigger.
   13822             :  */
   13823             : static Oid
   13824        6064 : CreateFKCheckTrigger(Oid myRelOid, Oid refRelOid, Constraint *fkconstraint,
   13825             :                      Oid constraintOid, Oid indexOid, Oid parentTrigOid,
   13826             :                      bool on_insert)
   13827             : {
   13828             :     ObjectAddress trigAddress;
   13829             :     CreateTrigStmt *fk_trigger;
   13830             : 
   13831             :     /*
   13832             :      * Note: for a self-referential FK (referencing and referenced tables are
   13833             :      * the same), it is important that the ON UPDATE action fires before the
   13834             :      * CHECK action, since both triggers will fire on the same row during an
   13835             :      * UPDATE event; otherwise the CHECK trigger will be checking a non-final
   13836             :      * state of the row.  Triggers fire in name order, so we ensure this by
   13837             :      * using names like "RI_ConstraintTrigger_a_NNNN" for the action triggers
   13838             :      * and "RI_ConstraintTrigger_c_NNNN" for the check triggers.
   13839             :      */
   13840        6064 :     fk_trigger = makeNode(CreateTrigStmt);
   13841        6064 :     fk_trigger->replace = false;
   13842        6064 :     fk_trigger->isconstraint = true;
   13843        6064 :     fk_trigger->trigname = "RI_ConstraintTrigger_c";
   13844        6064 :     fk_trigger->relation = NULL;
   13845             : 
   13846             :     /* Either ON INSERT or ON UPDATE */
   13847        6064 :     if (on_insert)
   13848             :     {
   13849        3032 :         fk_trigger->funcname = SystemFuncName("RI_FKey_check_ins");
   13850        3032 :         fk_trigger->events = TRIGGER_TYPE_INSERT;
   13851             :     }
   13852             :     else
   13853             :     {
   13854        3032 :         fk_trigger->funcname = SystemFuncName("RI_FKey_check_upd");
   13855        3032 :         fk_trigger->events = TRIGGER_TYPE_UPDATE;
   13856             :     }
   13857             : 
   13858        6064 :     fk_trigger->args = NIL;
   13859        6064 :     fk_trigger->row = true;
   13860        6064 :     fk_trigger->timing = TRIGGER_TYPE_AFTER;
   13861        6064 :     fk_trigger->columns = NIL;
   13862        6064 :     fk_trigger->whenClause = NULL;
   13863        6064 :     fk_trigger->transitionRels = NIL;
   13864        6064 :     fk_trigger->deferrable = fkconstraint->deferrable;
   13865        6064 :     fk_trigger->initdeferred = fkconstraint->initdeferred;
   13866        6064 :     fk_trigger->constrrel = NULL;
   13867             : 
   13868        6064 :     trigAddress = CreateTrigger(fk_trigger, NULL, myRelOid, refRelOid,
   13869             :                                 constraintOid, indexOid, InvalidOid,
   13870             :                                 parentTrigOid, NULL, true, false);
   13871             : 
   13872             :     /* Make changes-so-far visible */
   13873        6064 :     CommandCounterIncrement();
   13874             : 
   13875        6064 :     return trigAddress.objectId;
   13876             : }
   13877             : 
   13878             : /*
   13879             :  * createForeignKeyActionTriggers
   13880             :  *      Create the referenced-side "action" triggers that implement a foreign
   13881             :  *      key.
   13882             :  *
   13883             :  * Returns the OIDs of the so created triggers in *deleteTrigOid and
   13884             :  * *updateTrigOid.
   13885             :  */
   13886             : static void
   13887        3486 : createForeignKeyActionTriggers(Oid myRelOid, Oid refRelOid, Constraint *fkconstraint,
   13888             :                                Oid constraintOid, Oid indexOid,
   13889             :                                Oid parentDelTrigger, Oid parentUpdTrigger,
   13890             :                                Oid *deleteTrigOid, Oid *updateTrigOid)
   13891             : {
   13892             :     CreateTrigStmt *fk_trigger;
   13893             :     ObjectAddress trigAddress;
   13894             : 
   13895             :     /*
   13896             :      * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
   13897             :      * DELETE action on the referenced table.
   13898             :      */
   13899        3486 :     fk_trigger = makeNode(CreateTrigStmt);
   13900        3486 :     fk_trigger->replace = false;
   13901        3486 :     fk_trigger->isconstraint = true;
   13902        3486 :     fk_trigger->trigname = "RI_ConstraintTrigger_a";
   13903        3486 :     fk_trigger->relation = NULL;
   13904        3486 :     fk_trigger->args = NIL;
   13905        3486 :     fk_trigger->row = true;
   13906        3486 :     fk_trigger->timing = TRIGGER_TYPE_AFTER;
   13907        3486 :     fk_trigger->events = TRIGGER_TYPE_DELETE;
   13908        3486 :     fk_trigger->columns = NIL;
   13909        3486 :     fk_trigger->whenClause = NULL;
   13910        3486 :     fk_trigger->transitionRels = NIL;
   13911        3486 :     fk_trigger->constrrel = NULL;
   13912             : 
   13913        3486 :     switch (fkconstraint->fk_del_action)
   13914             :     {
   13915        2834 :         case FKCONSTR_ACTION_NOACTION:
   13916        2834 :             fk_trigger->deferrable = fkconstraint->deferrable;
   13917        2834 :             fk_trigger->initdeferred = fkconstraint->initdeferred;
   13918        2834 :             fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_del");
   13919        2834 :             break;
   13920          30 :         case FKCONSTR_ACTION_RESTRICT:
   13921          30 :             fk_trigger->deferrable = false;
   13922          30 :             fk_trigger->initdeferred = false;
   13923          30 :             fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_del");
   13924          30 :             break;
   13925         464 :         case FKCONSTR_ACTION_CASCADE:
   13926         464 :             fk_trigger->deferrable = false;
   13927         464 :             fk_trigger->initdeferred = false;
   13928         464 :             fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_del");
   13929         464 :             break;
   13930          98 :         case FKCONSTR_ACTION_SETNULL:
   13931          98 :             fk_trigger->deferrable = false;
   13932          98 :             fk_trigger->initdeferred = false;
   13933          98 :             fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_del");
   13934          98 :             break;
   13935          60 :         case FKCONSTR_ACTION_SETDEFAULT:
   13936          60 :             fk_trigger->deferrable = false;
   13937          60 :             fk_trigger->initdeferred = false;
   13938          60 :             fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_del");
   13939          60 :             break;
   13940           0 :         default:
   13941           0 :             elog(ERROR, "unrecognized FK action type: %d",
   13942             :                  (int) fkconstraint->fk_del_action);
   13943             :             break;
   13944             :     }
   13945             : 
   13946        3486 :     trigAddress = CreateTrigger(fk_trigger, NULL, refRelOid, myRelOid,
   13947             :                                 constraintOid, indexOid, InvalidOid,
   13948             :                                 parentDelTrigger, NULL, true, false);
   13949        3486 :     if (deleteTrigOid)
   13950        3486 :         *deleteTrigOid = trigAddress.objectId;
   13951             : 
   13952             :     /* Make changes-so-far visible */
   13953        3486 :     CommandCounterIncrement();
   13954             : 
   13955             :     /*
   13956             :      * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
   13957             :      * UPDATE action on the referenced table.
   13958             :      */
   13959        3486 :     fk_trigger = makeNode(CreateTrigStmt);
   13960        3486 :     fk_trigger->replace = false;
   13961        3486 :     fk_trigger->isconstraint = true;
   13962        3486 :     fk_trigger->trigname = "RI_ConstraintTrigger_a";
   13963        3486 :     fk_trigger->relation = NULL;
   13964        3486 :     fk_trigger->args = NIL;
   13965        3486 :     fk_trigger->row = true;
   13966        3486 :     fk_trigger->timing = TRIGGER_TYPE_AFTER;
   13967        3486 :     fk_trigger->events = TRIGGER_TYPE_UPDATE;
   13968        3486 :     fk_trigger->columns = NIL;
   13969        3486 :     fk_trigger->whenClause = NULL;
   13970        3486 :     fk_trigger->transitionRels = NIL;
   13971        3486 :     fk_trigger->constrrel = NULL;
   13972             : 
   13973        3486 :     switch (fkconstraint->fk_upd_action)
   13974             :     {
   13975        3028 :         case FKCONSTR_ACTION_NOACTION:
   13976        3028 :             fk_trigger->deferrable = fkconstraint->deferrable;
   13977        3028 :             fk_trigger->initdeferred = fkconstraint->initdeferred;
   13978        3028 :             fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_upd");
   13979        3028 :             break;
   13980          36 :         case FKCONSTR_ACTION_RESTRICT:
   13981          36 :             fk_trigger->deferrable = false;
   13982          36 :             fk_trigger->initdeferred = false;
   13983          36 :             fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_upd");
   13984          36 :             break;
   13985         318 :         case FKCONSTR_ACTION_CASCADE:
   13986         318 :             fk_trigger->deferrable = false;
   13987         318 :             fk_trigger->initdeferred = false;
   13988         318 :             fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_upd");
   13989         318 :             break;
   13990          62 :         case FKCONSTR_ACTION_SETNULL:
   13991          62 :             fk_trigger->deferrable = false;
   13992          62 :             fk_trigger->initdeferred = false;
   13993          62 :             fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_upd");
   13994          62 :             break;
   13995          42 :         case FKCONSTR_ACTION_SETDEFAULT:
   13996          42 :             fk_trigger->deferrable = false;
   13997          42 :             fk_trigger->initdeferred = false;
   13998          42 :             fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_upd");
   13999          42 :             break;
   14000           0 :         default:
   14001           0 :             elog(ERROR, "unrecognized FK action type: %d",
   14002             :                  (int) fkconstraint->fk_upd_action);
   14003             :             break;
   14004             :     }
   14005             : 
   14006        3486 :     trigAddress = CreateTrigger(fk_trigger, NULL, refRelOid, myRelOid,
   14007             :                                 constraintOid, indexOid, InvalidOid,
   14008             :                                 parentUpdTrigger, NULL, true, false);
   14009        3486 :     if (updateTrigOid)
   14010        3486 :         *updateTrigOid = trigAddress.objectId;
   14011        3486 : }
   14012             : 
   14013             : /*
   14014             :  * createForeignKeyCheckTriggers
   14015             :  *      Create the referencing-side "check" triggers that implement a foreign
   14016             :  *      key.
   14017             :  *
   14018             :  * Returns the OIDs of the so created triggers in *insertTrigOid and
   14019             :  * *updateTrigOid.
   14020             :  */
   14021             : static void
   14022        3032 : createForeignKeyCheckTriggers(Oid myRelOid, Oid refRelOid,
   14023             :                               Constraint *fkconstraint, Oid constraintOid,
   14024             :                               Oid indexOid,
   14025             :                               Oid parentInsTrigger, Oid parentUpdTrigger,
   14026             :                               Oid *insertTrigOid, Oid *updateTrigOid)
   14027             : {
   14028        3032 :     *insertTrigOid = CreateFKCheckTrigger(myRelOid, refRelOid, fkconstraint,
   14029             :                                           constraintOid, indexOid,
   14030             :                                           parentInsTrigger, true);
   14031        3032 :     *updateTrigOid = CreateFKCheckTrigger(myRelOid, refRelOid, fkconstraint,
   14032             :                                           constraintOid, indexOid,
   14033             :                                           parentUpdTrigger, false);
   14034        3032 : }
   14035             : 
   14036             : /*
   14037             :  * ALTER TABLE DROP CONSTRAINT
   14038             :  *
   14039             :  * Like DROP COLUMN, we can't use the normal ALTER TABLE recursion mechanism.
   14040             :  */
   14041             : static void
   14042         812 : ATExecDropConstraint(Relation rel, const char *constrName,
   14043             :                      DropBehavior behavior, bool recurse,
   14044             :                      bool missing_ok, LOCKMODE lockmode)
   14045             : {
   14046             :     Relation    conrel;
   14047             :     SysScanDesc scan;
   14048             :     ScanKeyData skey[3];
   14049             :     HeapTuple   tuple;
   14050         812 :     bool        found = false;
   14051             : 
   14052         812 :     conrel = table_open(ConstraintRelationId, RowExclusiveLock);
   14053             : 
   14054             :     /*
   14055             :      * Find and drop the target constraint
   14056             :      */
   14057         812 :     ScanKeyInit(&skey[0],
   14058             :                 Anum_pg_constraint_conrelid,
   14059             :                 BTEqualStrategyNumber, F_OIDEQ,
   14060             :                 ObjectIdGetDatum(RelationGetRelid(rel)));
   14061         812 :     ScanKeyInit(&skey[1],
   14062             :                 Anum_pg_constraint_contypid,
   14063             :                 BTEqualStrategyNumber, F_OIDEQ,
   14064             :                 ObjectIdGetDatum(InvalidOid));
   14065         812 :     ScanKeyInit(&skey[2],
   14066             :                 Anum_pg_constraint_conname,
   14067             :                 BTEqualStrategyNumber, F_NAMEEQ,
   14068             :                 CStringGetDatum(constrName));
   14069         812 :     scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
   14070             :                               true, NULL, 3, skey);
   14071             : 
   14072             :     /* There can be at most one matching row */
   14073         812 :     if (HeapTupleIsValid(tuple = systable_getnext(scan)))
   14074             :     {
   14075         776 :         dropconstraint_internal(rel, tuple, behavior, recurse, false,
   14076             :                                 missing_ok, lockmode);
   14077         590 :         found = true;
   14078             :     }
   14079             : 
   14080         626 :     systable_endscan(scan);
   14081             : 
   14082         626 :     if (!found)
   14083             :     {
   14084          36 :         if (!missing_ok)
   14085          24 :             ereport(ERROR,
   14086             :                     errcode(ERRCODE_UNDEFINED_OBJECT),
   14087             :                     errmsg("constraint \"%s\" of relation \"%s\" does not exist",
   14088             :                            constrName, RelationGetRelationName(rel)));
   14089             :         else
   14090          12 :             ereport(NOTICE,
   14091             :                     errmsg("constraint \"%s\" of relation \"%s\" does not exist, skipping",
   14092             :                            constrName, RelationGetRelationName(rel)));
   14093             :     }
   14094             : 
   14095         602 :     table_close(conrel, RowExclusiveLock);
   14096         602 : }
   14097             : 
   14098             : /*
   14099             :  * Remove a constraint, using its pg_constraint tuple
   14100             :  *
   14101             :  * Implementation for ALTER TABLE DROP CONSTRAINT and ALTER TABLE ALTER COLUMN
   14102             :  * DROP NOT NULL.
   14103             :  *
   14104             :  * Returns the address of the constraint being removed.
   14105             :  */
   14106             : static ObjectAddress
   14107        1206 : dropconstraint_internal(Relation rel, HeapTuple constraintTup, DropBehavior behavior,
   14108             :                         bool recurse, bool recursing, bool missing_ok,
   14109             :                         LOCKMODE lockmode)
   14110             : {
   14111             :     Relation    conrel;
   14112             :     Form_pg_constraint con;
   14113             :     ObjectAddress conobj;
   14114             :     List       *children;
   14115        1206 :     bool        is_no_inherit_constraint = false;
   14116             :     char       *constrName;
   14117        1206 :     char       *colname = NULL;
   14118             : 
   14119             :     /* Guard against stack overflow due to overly deep inheritance tree. */
   14120        1206 :     check_stack_depth();
   14121             : 
   14122             :     /* At top level, permission check was done in ATPrepCmd, else do it */
   14123        1206 :     if (recursing)
   14124         210 :         ATSimplePermissions(AT_DropConstraint, rel,
   14125             :                             ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
   14126             : 
   14127        1200 :     conrel = table_open(ConstraintRelationId, RowExclusiveLock);
   14128             : 
   14129        1200 :     con = (Form_pg_constraint) GETSTRUCT(constraintTup);
   14130        1200 :     constrName = NameStr(con->conname);
   14131             : 
   14132             :     /* Don't allow drop of inherited constraints */
   14133        1200 :     if (con->coninhcount > 0 && !recursing)
   14134         156 :         ereport(ERROR,
   14135             :                 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
   14136             :                  errmsg("cannot drop inherited constraint \"%s\" of relation \"%s\"",
   14137             :                         constrName, RelationGetRelationName(rel))));
   14138             : 
   14139             :     /*
   14140             :      * Reset pg_constraint.attnotnull, if this is a not-null constraint.
   14141             :      *
   14142             :      * While doing that, we're in a good position to disallow dropping a not-
   14143             :      * null constraint underneath a primary key, a replica identity index, or
   14144             :      * a generated identity column.
   14145             :      */
   14146        1044 :     if (con->contype == CONSTRAINT_NOTNULL)
   14147             :     {
   14148         314 :         Relation    attrel = table_open(AttributeRelationId, RowExclusiveLock);
   14149         314 :         AttrNumber  attnum = extractNotNullColumn(constraintTup);
   14150             :         Bitmapset  *pkattrs;
   14151             :         Bitmapset  *irattrs;
   14152             :         HeapTuple   atttup;
   14153             :         Form_pg_attribute attForm;
   14154             : 
   14155             :         /* save column name for recursion step */
   14156         314 :         colname = get_attname(RelationGetRelid(rel), attnum, false);
   14157             : 
   14158             :         /*
   14159             :          * Disallow if it's in the primary key.  For partitioned tables we
   14160             :          * cannot rely solely on RelationGetIndexAttrBitmap, because it'll
   14161             :          * return NULL if the primary key is invalid; but we still need to
   14162             :          * protect not-null constraints under such a constraint, so check the
   14163             :          * slow way.
   14164             :          */
   14165         314 :         pkattrs = RelationGetIndexAttrBitmap(rel, INDEX_ATTR_BITMAP_PRIMARY_KEY);
   14166             : 
   14167         314 :         if (pkattrs == NULL &&
   14168         278 :             rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
   14169             :         {
   14170          18 :             Oid         pkindex = RelationGetPrimaryKeyIndex(rel, true);
   14171             : 
   14172          18 :             if (OidIsValid(pkindex))
   14173             :             {
   14174           0 :                 Relation    pk = relation_open(pkindex, AccessShareLock);
   14175             : 
   14176           0 :                 pkattrs = NULL;
   14177           0 :                 for (int i = 0; i < pk->rd_index->indnkeyatts; i++)
   14178           0 :                     pkattrs = bms_add_member(pkattrs, pk->rd_index->indkey.values[i]);
   14179             : 
   14180           0 :                 relation_close(pk, AccessShareLock);
   14181             :             }
   14182             :         }
   14183             : 
   14184         350 :         if (pkattrs &&
   14185          36 :             bms_is_member(attnum - FirstLowInvalidHeapAttributeNumber, pkattrs))
   14186          24 :             ereport(ERROR,
   14187             :                     errcode(ERRCODE_INVALID_TABLE_DEFINITION),
   14188             :                     errmsg("column \"%s\" is in a primary key",
   14189             :                            get_attname(RelationGetRelid(rel), attnum, false)));
   14190             : 
   14191             :         /* Disallow if it's in the replica identity */
   14192         290 :         irattrs = RelationGetIndexAttrBitmap(rel, INDEX_ATTR_BITMAP_IDENTITY_KEY);
   14193         290 :         if (bms_is_member(attnum - FirstLowInvalidHeapAttributeNumber, irattrs))
   14194          12 :             ereport(ERROR,
   14195             :                     errcode(ERRCODE_INVALID_TABLE_DEFINITION),
   14196             :                     errmsg("column \"%s\" is in index used as replica identity",
   14197             :                            get_attname(RelationGetRelid(rel), attnum, false)));
   14198             : 
   14199             :         /* Disallow if it's a GENERATED AS IDENTITY column */
   14200         278 :         atttup = SearchSysCacheCopyAttNum(RelationGetRelid(rel), attnum);
   14201         278 :         if (!HeapTupleIsValid(atttup))
   14202           0 :             elog(ERROR, "cache lookup failed for attribute %d of relation %u",
   14203             :                  attnum, RelationGetRelid(rel));
   14204         278 :         attForm = (Form_pg_attribute) GETSTRUCT(atttup);
   14205         278 :         if (attForm->attidentity != '\0')
   14206           0 :             ereport(ERROR,
   14207             :                     errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
   14208             :                     errmsg("column \"%s\" of relation \"%s\" is an identity column",
   14209             :                            get_attname(RelationGetRelid(rel), attnum,
   14210             :                                        false),
   14211             :                            RelationGetRelationName(rel)));
   14212             : 
   14213             :         /* All good -- reset attnotnull if needed */
   14214         278 :         if (attForm->attnotnull)
   14215             :         {
   14216         278 :             attForm->attnotnull = false;
   14217         278 :             CatalogTupleUpdate(attrel, &atttup->t_self, atttup);
   14218             :         }
   14219             : 
   14220         278 :         table_close(attrel, RowExclusiveLock);
   14221             :     }
   14222             : 
   14223        1008 :     is_no_inherit_constraint = con->connoinherit;
   14224             : 
   14225             :     /*
   14226             :      * If it's a foreign-key constraint, we'd better lock the referenced table
   14227             :      * and check that that's not in use, just as we've already done for the
   14228             :      * constrained table (else we might, eg, be dropping a trigger that has
   14229             :      * unfired events).  But we can/must skip that in the self-referential
   14230             :      * case.
   14231             :      */
   14232        1008 :     if (con->contype == CONSTRAINT_FOREIGN &&
   14233         168 :         con->confrelid != RelationGetRelid(rel))
   14234             :     {
   14235             :         Relation    frel;
   14236             : 
   14237             :         /* Must match lock taken by RemoveTriggerById: */
   14238         168 :         frel = table_open(con->confrelid, AccessExclusiveLock);
   14239         168 :         CheckAlterTableIsSafe(frel);
   14240         162 :         table_close(frel, NoLock);
   14241             :     }
   14242             : 
   14243             :     /*
   14244             :      * Perform the actual constraint deletion
   14245             :      */
   14246        1002 :     ObjectAddressSet(conobj, ConstraintRelationId, con->oid);
   14247        1002 :     performDeletion(&conobj, behavior, 0);
   14248             : 
   14249             :     /*
   14250             :      * For partitioned tables, non-CHECK, non-NOT-NULL inherited constraints
   14251             :      * are dropped via the dependency mechanism, so we're done here.
   14252             :      */
   14253         966 :     if (con->contype != CONSTRAINT_CHECK &&
   14254         630 :         con->contype != CONSTRAINT_NOTNULL &&
   14255         352 :         rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
   14256             :     {
   14257          78 :         table_close(conrel, RowExclusiveLock);
   14258          78 :         return conobj;
   14259             :     }
   14260             : 
   14261             :     /*
   14262             :      * Propagate to children as appropriate.  Unlike most other ALTER
   14263             :      * routines, we have to do this one level of recursion at a time; we can't
   14264             :      * use find_all_inheritors to do it in one pass.
   14265             :      */
   14266         888 :     if (!is_no_inherit_constraint)
   14267         602 :         children = find_inheritance_children(RelationGetRelid(rel), lockmode);
   14268             :     else
   14269         286 :         children = NIL;
   14270             : 
   14271        2148 :     foreach_oid(childrelid, children)
   14272             :     {
   14273             :         Relation    childrel;
   14274             :         HeapTuple   tuple;
   14275             :         Form_pg_constraint childcon;
   14276             : 
   14277             :         /* find_inheritance_children already got lock */
   14278         384 :         childrel = table_open(childrelid, NoLock);
   14279         384 :         CheckAlterTableIsSafe(childrel);
   14280             : 
   14281             :         /*
   14282             :          * We search for not-null constraints by column name, and others by
   14283             :          * constraint name.
   14284             :          */
   14285         384 :         if (con->contype == CONSTRAINT_NOTNULL)
   14286             :         {
   14287         148 :             tuple = findNotNullConstraint(childrelid, colname);
   14288         148 :             if (!HeapTupleIsValid(tuple))
   14289           0 :                 elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation %u",
   14290             :                      colname, RelationGetRelid(childrel));
   14291             :         }
   14292             :         else
   14293             :         {
   14294             :             SysScanDesc scan;
   14295             :             ScanKeyData skey[3];
   14296             : 
   14297         236 :             ScanKeyInit(&skey[0],
   14298             :                         Anum_pg_constraint_conrelid,
   14299             :                         BTEqualStrategyNumber, F_OIDEQ,
   14300             :                         ObjectIdGetDatum(childrelid));
   14301         236 :             ScanKeyInit(&skey[1],
   14302             :                         Anum_pg_constraint_contypid,
   14303             :                         BTEqualStrategyNumber, F_OIDEQ,
   14304             :                         ObjectIdGetDatum(InvalidOid));
   14305         236 :             ScanKeyInit(&skey[2],
   14306             :                         Anum_pg_constraint_conname,
   14307             :                         BTEqualStrategyNumber, F_NAMEEQ,
   14308             :                         CStringGetDatum(constrName));
   14309         236 :             scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
   14310             :                                       true, NULL, 3, skey);
   14311             :             /* There can only be one, so no need to loop */
   14312         236 :             tuple = systable_getnext(scan);
   14313         236 :             if (!HeapTupleIsValid(tuple))
   14314           0 :                 ereport(ERROR,
   14315             :                         (errcode(ERRCODE_UNDEFINED_OBJECT),
   14316             :                          errmsg("constraint \"%s\" of relation \"%s\" does not exist",
   14317             :                                 constrName,
   14318             :                                 RelationGetRelationName(childrel))));
   14319         236 :             tuple = heap_copytuple(tuple);
   14320         236 :             systable_endscan(scan);
   14321             :         }
   14322             : 
   14323         384 :         childcon = (Form_pg_constraint) GETSTRUCT(tuple);
   14324             : 
   14325             :         /* Right now only CHECK and not-null constraints can be inherited */
   14326         384 :         if (childcon->contype != CONSTRAINT_CHECK &&
   14327         148 :             childcon->contype != CONSTRAINT_NOTNULL)
   14328           0 :             elog(ERROR, "inherited constraint is not a CHECK or not-null constraint");
   14329             : 
   14330         384 :         if (childcon->coninhcount <= 0) /* shouldn't happen */
   14331           0 :             elog(ERROR, "relation %u has non-inherited constraint \"%s\"",
   14332             :                  childrelid, NameStr(childcon->conname));
   14333             : 
   14334         384 :         if (recurse)
   14335             :         {
   14336             :             /*
   14337             :              * If the child constraint has other definition sources, just
   14338             :              * decrement its inheritance count; if not, recurse to delete it.
   14339             :              */
   14340         282 :             if (childcon->coninhcount == 1 && !childcon->conislocal)
   14341             :             {
   14342             :                 /* Time to delete this child constraint, too */
   14343         210 :                 dropconstraint_internal(childrel, tuple, behavior,
   14344             :                                         recurse, true, missing_ok,
   14345             :                                         lockmode);
   14346             :             }
   14347             :             else
   14348             :             {
   14349             :                 /* Child constraint must survive my deletion */
   14350          72 :                 childcon->coninhcount--;
   14351          72 :                 CatalogTupleUpdate(conrel, &tuple->t_self, tuple);
   14352             : 
   14353             :                 /* Make update visible */
   14354          72 :                 CommandCounterIncrement();
   14355             :             }
   14356             :         }
   14357             :         else
   14358             :         {
   14359             :             /*
   14360             :              * If we were told to drop ONLY in this table (no recursion) and
   14361             :              * there are no further parents for this constraint, we need to
   14362             :              * mark the inheritors' constraints as locally defined rather than
   14363             :              * inherited.
   14364             :              */
   14365         102 :             childcon->coninhcount--;
   14366         102 :             if (childcon->coninhcount == 0)
   14367         102 :                 childcon->conislocal = true;
   14368             : 
   14369         102 :             CatalogTupleUpdate(conrel, &tuple->t_self, tuple);
   14370             : 
   14371             :             /* Make update visible */
   14372         102 :             CommandCounterIncrement();
   14373             :         }
   14374             : 
   14375         378 :         heap_freetuple(tuple);
   14376             : 
   14377         378 :         table_close(childrel, NoLock);
   14378             :     }
   14379             : 
   14380         882 :     table_close(conrel, RowExclusiveLock);
   14381             : 
   14382         882 :     return conobj;
   14383             : }
   14384             : 
   14385             : /*
   14386             :  * ALTER COLUMN TYPE
   14387             :  *
   14388             :  * Unlike other subcommand types, we do parse transformation for ALTER COLUMN
   14389             :  * TYPE during phase 1 --- the AlterTableCmd passed in here is already
   14390             :  * transformed (and must be, because we rely on some transformed fields).
   14391             :  *
   14392             :  * The point of this is that the execution of all ALTER COLUMN TYPEs for a
   14393             :  * table will be done "in parallel" during phase 3, so all the USING
   14394             :  * expressions should be parsed assuming the original column types.  Also,
   14395             :  * this allows a USING expression to refer to a field that will be dropped.
   14396             :  *
   14397             :  * To make this work safely, AT_PASS_DROP then AT_PASS_ALTER_TYPE must be
   14398             :  * the first two execution steps in phase 2; they must not see the effects
   14399             :  * of any other subcommand types, since the USING expressions are parsed
   14400             :  * against the unmodified table's state.
   14401             :  */
   14402             : static void
   14403        1426 : ATPrepAlterColumnType(List **wqueue,
   14404             :                       AlteredTableInfo *tab, Relation rel,
   14405             :                       bool recurse, bool recursing,
   14406             :                       AlterTableCmd *cmd, LOCKMODE lockmode,
   14407             :                       AlterTableUtilityContext *context)
   14408             : {
   14409        1426 :     char       *colName = cmd->name;
   14410        1426 :     ColumnDef  *def = (ColumnDef *) cmd->def;
   14411        1426 :     TypeName   *typeName = def->typeName;
   14412        1426 :     Node       *transform = def->cooked_default;
   14413             :     HeapTuple   tuple;
   14414             :     Form_pg_attribute attTup;
   14415             :     AttrNumber  attnum;
   14416             :     Oid         targettype;
   14417             :     int32       targettypmod;
   14418             :     Oid         targetcollid;
   14419             :     NewColumnValue *newval;
   14420        1426 :     ParseState *pstate = make_parsestate(NULL);
   14421             :     AclResult   aclresult;
   14422             :     bool        is_expr;
   14423             : 
   14424        1426 :     pstate->p_sourcetext = context->queryString;
   14425             : 
   14426        1426 :     if (rel->rd_rel->reloftype && !recursing)
   14427           6 :         ereport(ERROR,
   14428             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   14429             :                  errmsg("cannot alter column type of typed table"),
   14430             :                  parser_errposition(pstate, def->location)));
   14431             : 
   14432             :     /* lookup the attribute so we can check inheritance status */
   14433        1420 :     tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
   14434        1420 :     if (!HeapTupleIsValid(tuple))
   14435           0 :         ereport(ERROR,
   14436             :                 (errcode(ERRCODE_UNDEFINED_COLUMN),
   14437             :                  errmsg("column \"%s\" of relation \"%s\" does not exist",
   14438             :                         colName, RelationGetRelationName(rel)),
   14439             :                  parser_errposition(pstate, def->location)));
   14440        1420 :     attTup = (Form_pg_attribute) GETSTRUCT(tuple);
   14441        1420 :     attnum = attTup->attnum;
   14442             : 
   14443             :     /* Can't alter a system attribute */
   14444        1420 :     if (attnum <= 0)
   14445           6 :         ereport(ERROR,
   14446             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   14447             :                  errmsg("cannot alter system column \"%s\"", colName),
   14448             :                  parser_errposition(pstate, def->location)));
   14449             : 
   14450             :     /*
   14451             :      * Cannot specify USING when altering type of a generated column, because
   14452             :      * that would violate the generation expression.
   14453             :      */
   14454        1414 :     if (attTup->attgenerated && def->cooked_default)
   14455          12 :         ereport(ERROR,
   14456             :                 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
   14457             :                  errmsg("cannot specify USING when altering type of generated column"),
   14458             :                  errdetail("Column \"%s\" is a generated column.", colName),
   14459             :                  parser_errposition(pstate, def->location)));
   14460             : 
   14461             :     /*
   14462             :      * Don't alter inherited columns.  At outer level, there had better not be
   14463             :      * any inherited definition; when recursing, we assume this was checked at
   14464             :      * the parent level (see below).
   14465             :      */
   14466        1402 :     if (attTup->attinhcount > 0 && !recursing)
   14467           6 :         ereport(ERROR,
   14468             :                 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
   14469             :                  errmsg("cannot alter inherited column \"%s\"", colName),
   14470             :                  parser_errposition(pstate, def->location)));
   14471             : 
   14472             :     /* Don't alter columns used in the partition key */
   14473        1396 :     if (has_partition_attrs(rel,
   14474             :                             bms_make_singleton(attnum - FirstLowInvalidHeapAttributeNumber),
   14475             :                             &is_expr))
   14476          18 :         ereport(ERROR,
   14477             :                 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
   14478             :                  errmsg("cannot alter column \"%s\" because it is part of the partition key of relation \"%s\"",
   14479             :                         colName, RelationGetRelationName(rel)),
   14480             :                  parser_errposition(pstate, def->location)));
   14481             : 
   14482             :     /* Look up the target type */
   14483        1378 :     typenameTypeIdAndMod(pstate, typeName, &targettype, &targettypmod);
   14484             : 
   14485        1372 :     aclresult = object_aclcheck(TypeRelationId, targettype, GetUserId(), ACL_USAGE);
   14486        1372 :     if (aclresult != ACLCHECK_OK)
   14487          12 :         aclcheck_error_type(aclresult, targettype);
   14488             : 
   14489             :     /* And the collation */
   14490        1360 :     targetcollid = GetColumnDefCollation(pstate, def, targettype);
   14491             : 
   14492             :     /* make sure datatype is legal for a column */
   14493        2708 :     CheckAttributeType(colName, targettype, targetcollid,
   14494        1354 :                        list_make1_oid(rel->rd_rel->reltype),
   14495        1354 :                        (attTup->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL ? CHKATYPE_IS_VIRTUAL : 0));
   14496             : 
   14497        1342 :     if (attTup->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
   14498             :     {
   14499             :         /* do nothing */
   14500             :     }
   14501        1306 :     else if (tab->relkind == RELKIND_RELATION ||
   14502         202 :              tab->relkind == RELKIND_PARTITIONED_TABLE)
   14503             :     {
   14504             :         /*
   14505             :          * Set up an expression to transform the old data value to the new
   14506             :          * type. If a USING option was given, use the expression as
   14507             :          * transformed by transformAlterTableStmt, else just take the old
   14508             :          * value and try to coerce it.  We do this first so that type
   14509             :          * incompatibility can be detected before we waste effort, and because
   14510             :          * we need the expression to be parsed against the original table row
   14511             :          * type.
   14512             :          */
   14513        1170 :         if (!transform)
   14514             :         {
   14515         942 :             transform = (Node *) makeVar(1, attnum,
   14516             :                                          attTup->atttypid, attTup->atttypmod,
   14517             :                                          attTup->attcollation,
   14518             :                                          0);
   14519             :         }
   14520             : 
   14521        1170 :         transform = coerce_to_target_type(pstate,
   14522             :                                           transform, exprType(transform),
   14523             :                                           targettype, targettypmod,
   14524             :                                           COERCION_ASSIGNMENT,
   14525             :                                           COERCE_IMPLICIT_CAST,
   14526             :                                           -1);
   14527        1170 :         if (transform == NULL)
   14528             :         {
   14529             :             /* error text depends on whether USING was specified or not */
   14530          24 :             if (def->cooked_default != NULL)
   14531           6 :                 ereport(ERROR,
   14532             :                         (errcode(ERRCODE_DATATYPE_MISMATCH),
   14533             :                          errmsg("result of USING clause for column \"%s\""
   14534             :                                 " cannot be cast automatically to type %s",
   14535             :                                 colName, format_type_be(targettype)),
   14536             :                          errhint("You might need to add an explicit cast.")));
   14537             :             else
   14538          18 :                 ereport(ERROR,
   14539             :                         (errcode(ERRCODE_DATATYPE_MISMATCH),
   14540             :                          errmsg("column \"%s\" cannot be cast automatically to type %s",
   14541             :                                 colName, format_type_be(targettype)),
   14542             :                          !attTup->attgenerated ?
   14543             :                 /* translator: USING is SQL, don't translate it */
   14544             :                          errhint("You might need to specify \"USING %s::%s\".",
   14545             :                                  quote_identifier(colName),
   14546             :                                  format_type_with_typemod(targettype,
   14547             :                                                           targettypmod)) : 0));
   14548             :         }
   14549             : 
   14550             :         /* Fix collations after all else */
   14551        1146 :         assign_expr_collations(pstate, transform);
   14552             : 
   14553             :         /* Expand virtual generated columns in the expr. */
   14554        1146 :         transform = expand_generated_columns_in_expr(transform, rel, 1);
   14555             : 
   14556             :         /* Plan the expr now so we can accurately assess the need to rewrite. */
   14557        1146 :         transform = (Node *) expression_planner((Expr *) transform);
   14558             : 
   14559             :         /*
   14560             :          * Add a work queue item to make ATRewriteTable update the column
   14561             :          * contents.
   14562             :          */
   14563        1146 :         newval = palloc0_object(NewColumnValue);
   14564        1146 :         newval->attnum = attnum;
   14565        1146 :         newval->expr = (Expr *) transform;
   14566        1146 :         newval->is_generated = false;
   14567             : 
   14568        1146 :         tab->newvals = lappend(tab->newvals, newval);
   14569        1146 :         if (ATColumnChangeRequiresRewrite(transform, attnum))
   14570         944 :             tab->rewrite |= AT_REWRITE_COLUMN_REWRITE;
   14571             :     }
   14572         136 :     else if (transform)
   14573          12 :         ereport(ERROR,
   14574             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   14575             :                  errmsg("\"%s\" is not a table",
   14576             :                         RelationGetRelationName(rel))));
   14577             : 
   14578        1306 :     if (!RELKIND_HAS_STORAGE(tab->relkind) || attTup->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
   14579             :     {
   14580             :         /*
   14581             :          * For relations or columns without storage, do this check now.
   14582             :          * Regular tables will check it later when the table is being
   14583             :          * rewritten.
   14584             :          */
   14585         226 :         find_composite_type_dependencies(rel->rd_rel->reltype, rel, NULL);
   14586             :     }
   14587             : 
   14588        1258 :     ReleaseSysCache(tuple);
   14589             : 
   14590             :     /*
   14591             :      * Recurse manually by queueing a new command for each child, if
   14592             :      * necessary. We cannot apply ATSimpleRecursion here because we need to
   14593             :      * remap attribute numbers in the USING expression, if any.
   14594             :      *
   14595             :      * If we are told not to recurse, there had better not be any child
   14596             :      * tables; else the alter would put them out of step.
   14597             :      */
   14598        1258 :     if (recurse)
   14599             :     {
   14600        1000 :         Oid         relid = RelationGetRelid(rel);
   14601             :         List       *child_oids,
   14602             :                    *child_numparents;
   14603             :         ListCell   *lo,
   14604             :                    *li;
   14605             : 
   14606        1000 :         child_oids = find_all_inheritors(relid, lockmode,
   14607             :                                          &child_numparents);
   14608             : 
   14609             :         /*
   14610             :          * find_all_inheritors does the recursive search of the inheritance
   14611             :          * hierarchy, so all we have to do is process all of the relids in the
   14612             :          * list that it returns.
   14613             :          */
   14614        2208 :         forboth(lo, child_oids, li, child_numparents)
   14615             :         {
   14616        1232 :             Oid         childrelid = lfirst_oid(lo);
   14617        1232 :             int         numparents = lfirst_int(li);
   14618             :             Relation    childrel;
   14619             :             HeapTuple   childtuple;
   14620             :             Form_pg_attribute childattTup;
   14621             : 
   14622        1232 :             if (childrelid == relid)
   14623        1000 :                 continue;
   14624             : 
   14625             :             /* find_all_inheritors already got lock */
   14626         232 :             childrel = relation_open(childrelid, NoLock);
   14627         232 :             CheckAlterTableIsSafe(childrel);
   14628             : 
   14629             :             /*
   14630             :              * Verify that the child doesn't have any inherited definitions of
   14631             :              * this column that came from outside this inheritance hierarchy.
   14632             :              * (renameatt makes a similar test, though in a different way
   14633             :              * because of its different recursion mechanism.)
   14634             :              */
   14635         232 :             childtuple = SearchSysCacheAttName(RelationGetRelid(childrel),
   14636             :                                                colName);
   14637         232 :             if (!HeapTupleIsValid(childtuple))
   14638           0 :                 ereport(ERROR,
   14639             :                         (errcode(ERRCODE_UNDEFINED_COLUMN),
   14640             :                          errmsg("column \"%s\" of relation \"%s\" does not exist",
   14641             :                                 colName, RelationGetRelationName(childrel))));
   14642         232 :             childattTup = (Form_pg_attribute) GETSTRUCT(childtuple);
   14643             : 
   14644         232 :             if (childattTup->attinhcount > numparents)
   14645           6 :                 ereport(ERROR,
   14646             :                         (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
   14647             :                          errmsg("cannot alter inherited column \"%s\" of relation \"%s\"",
   14648             :                                 colName, RelationGetRelationName(childrel))));
   14649             : 
   14650         226 :             ReleaseSysCache(childtuple);
   14651             : 
   14652             :             /*
   14653             :              * Remap the attribute numbers.  If no USING expression was
   14654             :              * specified, there is no need for this step.
   14655             :              */
   14656         226 :             if (def->cooked_default)
   14657             :             {
   14658             :                 AttrMap    *attmap;
   14659             :                 bool        found_whole_row;
   14660             : 
   14661             :                 /* create a copy to scribble on */
   14662          78 :                 cmd = copyObject(cmd);
   14663             : 
   14664          78 :                 attmap = build_attrmap_by_name(RelationGetDescr(childrel),
   14665             :                                                RelationGetDescr(rel),
   14666             :                                                false);
   14667         156 :                 ((ColumnDef *) cmd->def)->cooked_default =
   14668          78 :                     map_variable_attnos(def->cooked_default,
   14669             :                                         1, 0,
   14670             :                                         attmap,
   14671             :                                         InvalidOid, &found_whole_row);
   14672          78 :                 if (found_whole_row)
   14673           6 :                     ereport(ERROR,
   14674             :                             (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   14675             :                              errmsg("cannot convert whole-row table reference"),
   14676             :                              errdetail("USING expression contains a whole-row table reference.")));
   14677          72 :                 pfree(attmap);
   14678             :             }
   14679         220 :             ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode, context);
   14680         208 :             relation_close(childrel, NoLock);
   14681             :         }
   14682             :     }
   14683         308 :     else if (!recursing &&
   14684          50 :              find_inheritance_children(RelationGetRelid(rel), NoLock) != NIL)
   14685           0 :         ereport(ERROR,
   14686             :                 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
   14687             :                  errmsg("type of inherited column \"%s\" must be changed in child tables too",
   14688             :                         colName)));
   14689             : 
   14690        1234 :     if (tab->relkind == RELKIND_COMPOSITE_TYPE)
   14691          50 :         ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
   14692        1228 : }
   14693             : 
   14694             : /*
   14695             :  * When the data type of a column is changed, a rewrite might not be required
   14696             :  * if the new type is sufficiently identical to the old one, and the USING
   14697             :  * clause isn't trying to insert some other value.  It's safe to skip the
   14698             :  * rewrite in these cases:
   14699             :  *
   14700             :  * - the old type is binary coercible to the new type
   14701             :  * - the new type is an unconstrained domain over the old type
   14702             :  * - {NEW,OLD} or {OLD,NEW} is {timestamptz,timestamp} and the timezone is UTC
   14703             :  *
   14704             :  * In the case of a constrained domain, we could get by with scanning the
   14705             :  * table and checking the constraint rather than actually rewriting it, but we
   14706             :  * don't currently try to do that.
   14707             :  */
   14708             : static bool
   14709        1146 : ATColumnChangeRequiresRewrite(Node *expr, AttrNumber varattno)
   14710             : {
   14711             :     Assert(expr != NULL);
   14712             : 
   14713             :     for (;;)
   14714             :     {
   14715             :         /* only one varno, so no need to check that */
   14716        1264 :         if (IsA(expr, Var) && ((Var *) expr)->varattno == varattno)
   14717         202 :             return false;
   14718        1062 :         else if (IsA(expr, RelabelType))
   14719         106 :             expr = (Node *) ((RelabelType *) expr)->arg;
   14720         956 :         else if (IsA(expr, CoerceToDomain))
   14721             :         {
   14722           0 :             CoerceToDomain *d = (CoerceToDomain *) expr;
   14723             : 
   14724           0 :             if (DomainHasConstraints(d->resulttype))
   14725           0 :                 return true;
   14726           0 :             expr = (Node *) d->arg;
   14727             :         }
   14728         956 :         else if (IsA(expr, FuncExpr))
   14729             :         {
   14730         750 :             FuncExpr   *f = (FuncExpr *) expr;
   14731             : 
   14732         750 :             switch (f->funcid)
   14733             :             {
   14734          18 :                 case F_TIMESTAMPTZ_TIMESTAMP:
   14735             :                 case F_TIMESTAMP_TIMESTAMPTZ:
   14736          18 :                     if (TimestampTimestampTzRequiresRewrite())
   14737           6 :                         return true;
   14738             :                     else
   14739          12 :                         expr = linitial(f->args);
   14740          12 :                     break;
   14741         732 :                 default:
   14742         732 :                     return true;
   14743             :             }
   14744             :         }
   14745             :         else
   14746         206 :             return true;
   14747             :     }
   14748             : }
   14749             : 
   14750             : /*
   14751             :  * ALTER COLUMN .. SET DATA TYPE
   14752             :  *
   14753             :  * Return the address of the modified column.
   14754             :  */
   14755             : static ObjectAddress
   14756        1192 : ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
   14757             :                       AlterTableCmd *cmd, LOCKMODE lockmode)
   14758             : {
   14759        1192 :     char       *colName = cmd->name;
   14760        1192 :     ColumnDef  *def = (ColumnDef *) cmd->def;
   14761        1192 :     TypeName   *typeName = def->typeName;
   14762             :     HeapTuple   heapTup;
   14763             :     Form_pg_attribute attTup,
   14764             :                 attOldTup;
   14765             :     AttrNumber  attnum;
   14766             :     HeapTuple   typeTuple;
   14767             :     Form_pg_type tform;
   14768             :     Oid         targettype;
   14769             :     int32       targettypmod;
   14770             :     Oid         targetcollid;
   14771             :     Node       *defaultexpr;
   14772             :     Relation    attrelation;
   14773             :     Relation    depRel;
   14774             :     ScanKeyData key[3];
   14775             :     SysScanDesc scan;
   14776             :     HeapTuple   depTup;
   14777             :     ObjectAddress address;
   14778             : 
   14779             :     /*
   14780             :      * Clear all the missing values if we're rewriting the table, since this
   14781             :      * renders them pointless.
   14782             :      */
   14783        1192 :     if (tab->rewrite)
   14784             :     {
   14785             :         Relation    newrel;
   14786             : 
   14787         884 :         newrel = table_open(RelationGetRelid(rel), NoLock);
   14788         884 :         RelationClearMissing(newrel);
   14789         884 :         relation_close(newrel, NoLock);
   14790             :         /* make sure we don't conflict with later attribute modifications */
   14791         884 :         CommandCounterIncrement();
   14792             :     }
   14793             : 
   14794        1192 :     attrelation = table_open(AttributeRelationId, RowExclusiveLock);
   14795             : 
   14796             :     /* Look up the target column */
   14797        1192 :     heapTup = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
   14798        1192 :     if (!HeapTupleIsValid(heapTup)) /* shouldn't happen */
   14799           0 :         ereport(ERROR,
   14800             :                 (errcode(ERRCODE_UNDEFINED_COLUMN),
   14801             :                  errmsg("column \"%s\" of relation \"%s\" does not exist",
   14802             :                         colName, RelationGetRelationName(rel))));
   14803        1192 :     attTup = (Form_pg_attribute) GETSTRUCT(heapTup);
   14804        1192 :     attnum = attTup->attnum;
   14805        1192 :     attOldTup = TupleDescAttr(tab->oldDesc, attnum - 1);
   14806             : 
   14807             :     /* Check for multiple ALTER TYPE on same column --- can't cope */
   14808        1192 :     if (attTup->atttypid != attOldTup->atttypid ||
   14809        1192 :         attTup->atttypmod != attOldTup->atttypmod)
   14810           0 :         ereport(ERROR,
   14811             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   14812             :                  errmsg("cannot alter type of column \"%s\" twice",
   14813             :                         colName)));
   14814             : 
   14815             :     /* Look up the target type (should not fail, since prep found it) */
   14816        1192 :     typeTuple = typenameType(NULL, typeName, &targettypmod);
   14817        1192 :     tform = (Form_pg_type) GETSTRUCT(typeTuple);
   14818        1192 :     targettype = tform->oid;
   14819             :     /* And the collation */
   14820        1192 :     targetcollid = GetColumnDefCollation(NULL, def, targettype);
   14821             : 
   14822             :     /*
   14823             :      * If there is a default expression for the column, get it and ensure we
   14824             :      * can coerce it to the new datatype.  (We must do this before changing
   14825             :      * the column type, because build_column_default itself will try to
   14826             :      * coerce, and will not issue the error message we want if it fails.)
   14827             :      *
   14828             :      * We remove any implicit coercion steps at the top level of the old
   14829             :      * default expression; this has been agreed to satisfy the principle of
   14830             :      * least surprise.  (The conversion to the new column type should act like
   14831             :      * it started from what the user sees as the stored expression, and the
   14832             :      * implicit coercions aren't going to be shown.)
   14833             :      */
   14834        1192 :     if (attTup->atthasdef)
   14835             :     {
   14836          92 :         defaultexpr = build_column_default(rel, attnum);
   14837             :         Assert(defaultexpr);
   14838          92 :         defaultexpr = strip_implicit_coercions(defaultexpr);
   14839          92 :         defaultexpr = coerce_to_target_type(NULL,   /* no UNKNOWN params */
   14840             :                                             defaultexpr, exprType(defaultexpr),
   14841             :                                             targettype, targettypmod,
   14842             :                                             COERCION_ASSIGNMENT,
   14843             :                                             COERCE_IMPLICIT_CAST,
   14844             :                                             -1);
   14845          92 :         if (defaultexpr == NULL)
   14846             :         {
   14847           6 :             if (attTup->attgenerated)
   14848           0 :                 ereport(ERROR,
   14849             :                         (errcode(ERRCODE_DATATYPE_MISMATCH),
   14850             :                          errmsg("generation expression for column \"%s\" cannot be cast automatically to type %s",
   14851             :                                 colName, format_type_be(targettype))));
   14852             :             else
   14853           6 :                 ereport(ERROR,
   14854             :                         (errcode(ERRCODE_DATATYPE_MISMATCH),
   14855             :                          errmsg("default for column \"%s\" cannot be cast automatically to type %s",
   14856             :                                 colName, format_type_be(targettype))));
   14857             :         }
   14858             :     }
   14859             :     else
   14860        1100 :         defaultexpr = NULL;
   14861             : 
   14862             :     /*
   14863             :      * Find everything that depends on the column (constraints, indexes, etc),
   14864             :      * and record enough information to let us recreate the objects.
   14865             :      *
   14866             :      * The actual recreation does not happen here, but only after we have
   14867             :      * performed all the individual ALTER TYPE operations.  We have to save
   14868             :      * the info before executing ALTER TYPE, though, else the deparser will
   14869             :      * get confused.
   14870             :      */
   14871        1186 :     RememberAllDependentForRebuilding(tab, AT_AlterColumnType, rel, attnum, colName);
   14872             : 
   14873             :     /*
   14874             :      * Now scan for dependencies of this column on other things.  The only
   14875             :      * things we should find are the dependency on the column datatype and
   14876             :      * possibly a collation dependency.  Those can be removed.
   14877             :      */
   14878        1150 :     depRel = table_open(DependRelationId, RowExclusiveLock);
   14879             : 
   14880        1150 :     ScanKeyInit(&key[0],
   14881             :                 Anum_pg_depend_classid,
   14882             :                 BTEqualStrategyNumber, F_OIDEQ,
   14883             :                 ObjectIdGetDatum(RelationRelationId));
   14884        1150 :     ScanKeyInit(&key[1],
   14885             :                 Anum_pg_depend_objid,
   14886             :                 BTEqualStrategyNumber, F_OIDEQ,
   14887             :                 ObjectIdGetDatum(RelationGetRelid(rel)));
   14888        1150 :     ScanKeyInit(&key[2],
   14889             :                 Anum_pg_depend_objsubid,
   14890             :                 BTEqualStrategyNumber, F_INT4EQ,
   14891             :                 Int32GetDatum((int32) attnum));
   14892             : 
   14893        1150 :     scan = systable_beginscan(depRel, DependDependerIndexId, true,
   14894             :                               NULL, 3, key);
   14895             : 
   14896        1154 :     while (HeapTupleIsValid(depTup = systable_getnext(scan)))
   14897             :     {
   14898           4 :         Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(depTup);
   14899             :         ObjectAddress foundObject;
   14900             : 
   14901           4 :         foundObject.classId = foundDep->refclassid;
   14902           4 :         foundObject.objectId = foundDep->refobjid;
   14903           4 :         foundObject.objectSubId = foundDep->refobjsubid;
   14904             : 
   14905           4 :         if (foundDep->deptype != DEPENDENCY_NORMAL)
   14906           0 :             elog(ERROR, "found unexpected dependency type '%c'",
   14907             :                  foundDep->deptype);
   14908           4 :         if (!(foundDep->refclassid == TypeRelationId &&
   14909           4 :               foundDep->refobjid == attTup->atttypid) &&
   14910           0 :             !(foundDep->refclassid == CollationRelationId &&
   14911           0 :               foundDep->refobjid == attTup->attcollation))
   14912           0 :             elog(ERROR, "found unexpected dependency for column: %s",
   14913             :                  getObjectDescription(&foundObject, false));
   14914             : 
   14915           4 :         CatalogTupleDelete(depRel, &depTup->t_self);
   14916             :     }
   14917             : 
   14918        1150 :     systable_endscan(scan);
   14919             : 
   14920        1150 :     table_close(depRel, RowExclusiveLock);
   14921             : 
   14922             :     /*
   14923             :      * Here we go --- change the recorded column type and collation.  (Note
   14924             :      * heapTup is a copy of the syscache entry, so okay to scribble on.) First
   14925             :      * fix up the missing value if any.
   14926             :      */
   14927        1150 :     if (attTup->atthasmissing)
   14928             :     {
   14929             :         Datum       missingval;
   14930             :         bool        missingNull;
   14931             : 
   14932             :         /* if rewrite is true the missing value should already be cleared */
   14933             :         Assert(tab->rewrite == 0);
   14934             : 
   14935             :         /* Get the missing value datum */
   14936           6 :         missingval = heap_getattr(heapTup,
   14937             :                                   Anum_pg_attribute_attmissingval,
   14938             :                                   attrelation->rd_att,
   14939             :                                   &missingNull);
   14940             : 
   14941             :         /* if it's a null array there is nothing to do */
   14942             : 
   14943           6 :         if (!missingNull)
   14944             :         {
   14945             :             /*
   14946             :              * Get the datum out of the array and repack it in a new array
   14947             :              * built with the new type data. We assume that since the table
   14948             :              * doesn't need rewriting, the actual Datum doesn't need to be
   14949             :              * changed, only the array metadata.
   14950             :              */
   14951             : 
   14952           6 :             int         one = 1;
   14953             :             bool        isNull;
   14954           6 :             Datum       valuesAtt[Natts_pg_attribute] = {0};
   14955           6 :             bool        nullsAtt[Natts_pg_attribute] = {0};
   14956           6 :             bool        replacesAtt[Natts_pg_attribute] = {0};
   14957             :             HeapTuple   newTup;
   14958             : 
   14959          12 :             missingval = array_get_element(missingval,
   14960             :                                            1,
   14961             :                                            &one,
   14962             :                                            0,
   14963           6 :                                            attTup->attlen,
   14964           6 :                                            attTup->attbyval,
   14965           6 :                                            attTup->attalign,
   14966             :                                            &isNull);
   14967           6 :             missingval = PointerGetDatum(construct_array(&missingval,
   14968             :                                                          1,
   14969             :                                                          targettype,
   14970           6 :                                                          tform->typlen,
   14971           6 :                                                          tform->typbyval,
   14972           6 :                                                          tform->typalign));
   14973             : 
   14974           6 :             valuesAtt[Anum_pg_attribute_attmissingval - 1] = missingval;
   14975           6 :             replacesAtt[Anum_pg_attribute_attmissingval - 1] = true;
   14976           6 :             nullsAtt[Anum_pg_attribute_attmissingval - 1] = false;
   14977             : 
   14978           6 :             newTup = heap_modify_tuple(heapTup, RelationGetDescr(attrelation),
   14979             :                                        valuesAtt, nullsAtt, replacesAtt);
   14980           6 :             heap_freetuple(heapTup);
   14981           6 :             heapTup = newTup;
   14982           6 :             attTup = (Form_pg_attribute) GETSTRUCT(heapTup);
   14983             :         }
   14984             :     }
   14985             : 
   14986        1150 :     attTup->atttypid = targettype;
   14987        1150 :     attTup->atttypmod = targettypmod;
   14988        1150 :     attTup->attcollation = targetcollid;
   14989        1150 :     if (list_length(typeName->arrayBounds) > PG_INT16_MAX)
   14990           0 :         ereport(ERROR,
   14991             :                 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
   14992             :                 errmsg("too many array dimensions"));
   14993        1150 :     attTup->attndims = list_length(typeName->arrayBounds);
   14994        1150 :     attTup->attlen = tform->typlen;
   14995        1150 :     attTup->attbyval = tform->typbyval;
   14996        1150 :     attTup->attalign = tform->typalign;
   14997        1150 :     attTup->attstorage = tform->typstorage;
   14998        1150 :     attTup->attcompression = InvalidCompressionMethod;
   14999             : 
   15000        1150 :     ReleaseSysCache(typeTuple);
   15001             : 
   15002        1150 :     CatalogTupleUpdate(attrelation, &heapTup->t_self, heapTup);
   15003             : 
   15004        1150 :     table_close(attrelation, RowExclusiveLock);
   15005             : 
   15006             :     /* Install dependencies on new datatype and collation */
   15007        1150 :     add_column_datatype_dependency(RelationGetRelid(rel), attnum, targettype);
   15008        1150 :     add_column_collation_dependency(RelationGetRelid(rel), attnum, targetcollid);
   15009             : 
   15010             :     /*
   15011             :      * Drop any pg_statistic entry for the column, since it's now wrong type
   15012             :      */
   15013        1150 :     RemoveStatistics(RelationGetRelid(rel), attnum);
   15014             : 
   15015        1150 :     InvokeObjectPostAlterHook(RelationRelationId,
   15016             :                               RelationGetRelid(rel), attnum);
   15017             : 
   15018             :     /*
   15019             :      * Update the default, if present, by brute force --- remove and re-add
   15020             :      * the default.  Probably unsafe to take shortcuts, since the new version
   15021             :      * may well have additional dependencies.  (It's okay to do this now,
   15022             :      * rather than after other ALTER TYPE commands, since the default won't
   15023             :      * depend on other column types.)
   15024             :      */
   15025        1150 :     if (defaultexpr)
   15026             :     {
   15027             :         /*
   15028             :          * If it's a GENERATED default, drop its dependency records, in
   15029             :          * particular its INTERNAL dependency on the column, which would
   15030             :          * otherwise cause dependency.c to refuse to perform the deletion.
   15031             :          */
   15032          86 :         if (attTup->attgenerated)
   15033             :         {
   15034          36 :             Oid         attrdefoid = GetAttrDefaultOid(RelationGetRelid(rel), attnum);
   15035             : 
   15036          36 :             if (!OidIsValid(attrdefoid))
   15037           0 :                 elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
   15038             :                      RelationGetRelid(rel), attnum);
   15039          36 :             (void) deleteDependencyRecordsFor(AttrDefaultRelationId, attrdefoid, false);
   15040             :         }
   15041             : 
   15042             :         /*
   15043             :          * Make updates-so-far visible, particularly the new pg_attribute row
   15044             :          * which will be updated again.
   15045             :          */
   15046          86 :         CommandCounterIncrement();
   15047             : 
   15048             :         /*
   15049             :          * We use RESTRICT here for safety, but at present we do not expect
   15050             :          * anything to depend on the default.
   15051             :          */
   15052          86 :         RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT, true,
   15053             :                           true);
   15054             : 
   15055          86 :         (void) StoreAttrDefault(rel, attnum, defaultexpr, true);
   15056             :     }
   15057             : 
   15058        1150 :     ObjectAddressSubSet(address, RelationRelationId,
   15059             :                         RelationGetRelid(rel), attnum);
   15060             : 
   15061             :     /* Cleanup */
   15062        1150 :     heap_freetuple(heapTup);
   15063             : 
   15064        1150 :     return address;
   15065             : }
   15066             : 
   15067             : /*
   15068             :  * Subroutine for ATExecAlterColumnType and ATExecSetExpression: Find everything
   15069             :  * that depends on the column (constraints, indexes, etc), and record enough
   15070             :  * information to let us recreate the objects.
   15071             :  */
   15072             : static void
   15073        1290 : RememberAllDependentForRebuilding(AlteredTableInfo *tab, AlterTableType subtype,
   15074             :                                   Relation rel, AttrNumber attnum, const char *colName)
   15075             : {
   15076             :     Relation    depRel;
   15077             :     ScanKeyData key[3];
   15078             :     SysScanDesc scan;
   15079             :     HeapTuple   depTup;
   15080             : 
   15081             :     Assert(subtype == AT_AlterColumnType || subtype == AT_SetExpression);
   15082             : 
   15083        1290 :     depRel = table_open(DependRelationId, RowExclusiveLock);
   15084             : 
   15085        1290 :     ScanKeyInit(&key[0],
   15086             :                 Anum_pg_depend_refclassid,
   15087             :                 BTEqualStrategyNumber, F_OIDEQ,
   15088             :                 ObjectIdGetDatum(RelationRelationId));
   15089        1290 :     ScanKeyInit(&key[1],
   15090             :                 Anum_pg_depend_refobjid,
   15091             :                 BTEqualStrategyNumber, F_OIDEQ,
   15092             :                 ObjectIdGetDatum(RelationGetRelid(rel)));
   15093        1290 :     ScanKeyInit(&key[2],
   15094             :                 Anum_pg_depend_refobjsubid,
   15095             :                 BTEqualStrategyNumber, F_INT4EQ,
   15096             :                 Int32GetDatum((int32) attnum));
   15097             : 
   15098        1290 :     scan = systable_beginscan(depRel, DependReferenceIndexId, true,
   15099             :                               NULL, 3, key);
   15100             : 
   15101        2526 :     while (HeapTupleIsValid(depTup = systable_getnext(scan)))
   15102             :     {
   15103        1272 :         Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(depTup);
   15104             :         ObjectAddress foundObject;
   15105             : 
   15106        1272 :         foundObject.classId = foundDep->classid;
   15107        1272 :         foundObject.objectId = foundDep->objid;
   15108        1272 :         foundObject.objectSubId = foundDep->objsubid;
   15109             : 
   15110        1272 :         switch (foundObject.classId)
   15111             :         {
   15112         286 :             case RelationRelationId:
   15113             :                 {
   15114         286 :                     char        relKind = get_rel_relkind(foundObject.objectId);
   15115             : 
   15116         286 :                     if (relKind == RELKIND_INDEX ||
   15117             :                         relKind == RELKIND_PARTITIONED_INDEX)
   15118             :                     {
   15119             :                         Assert(foundObject.objectSubId == 0);
   15120         248 :                         RememberIndexForRebuilding(foundObject.objectId, tab);
   15121             :                     }
   15122          38 :                     else if (relKind == RELKIND_SEQUENCE)
   15123             :                     {
   15124             :                         /*
   15125             :                          * This must be a SERIAL column's sequence.  We need
   15126             :                          * not do anything to it.
   15127             :                          */
   15128             :                         Assert(foundObject.objectSubId == 0);
   15129             :                     }
   15130             :                     else
   15131             :                     {
   15132             :                         /* Not expecting any other direct dependencies... */
   15133           0 :                         elog(ERROR, "unexpected object depending on column: %s",
   15134             :                              getObjectDescription(&foundObject, false));
   15135             :                     }
   15136         286 :                     break;
   15137             :                 }
   15138             : 
   15139         686 :             case ConstraintRelationId:
   15140             :                 Assert(foundObject.objectSubId == 0);
   15141         686 :                 RememberConstraintForRebuilding(foundObject.objectId, tab);
   15142         686 :                 break;
   15143             : 
   15144           0 :             case ProcedureRelationId:
   15145             : 
   15146             :                 /*
   15147             :                  * A new-style SQL function can depend on a column, if that
   15148             :                  * column is referenced in the parsed function body.  Ideally
   15149             :                  * we'd automatically update the function by deparsing and
   15150             :                  * reparsing it, but that's risky and might well fail anyhow.
   15151             :                  * FIXME someday.
   15152             :                  *
   15153             :                  * This is only a problem for AT_AlterColumnType, not
   15154             :                  * AT_SetExpression.
   15155             :                  */
   15156           0 :                 if (subtype == AT_AlterColumnType)
   15157           0 :                     ereport(ERROR,
   15158             :                             (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   15159             :                              errmsg("cannot alter type of a column used by a function or procedure"),
   15160             :                              errdetail("%s depends on column \"%s\"",
   15161             :                                        getObjectDescription(&foundObject, false),
   15162             :                                        colName)));
   15163           0 :                 break;
   15164             : 
   15165          12 :             case RewriteRelationId:
   15166             : 
   15167             :                 /*
   15168             :                  * View/rule bodies have pretty much the same issues as
   15169             :                  * function bodies.  FIXME someday.
   15170             :                  */
   15171          12 :                 if (subtype == AT_AlterColumnType)
   15172          12 :                     ereport(ERROR,
   15173             :                             (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   15174             :                              errmsg("cannot alter type of a column used by a view or rule"),
   15175             :                              errdetail("%s depends on column \"%s\"",
   15176             :                                        getObjectDescription(&foundObject, false),
   15177             :                                        colName)));
   15178           0 :                 break;
   15179             : 
   15180           0 :             case TriggerRelationId:
   15181             : 
   15182             :                 /*
   15183             :                  * A trigger can depend on a column because the column is
   15184             :                  * specified as an update target, or because the column is
   15185             :                  * used in the trigger's WHEN condition.  The first case would
   15186             :                  * not require any extra work, but the second case would
   15187             :                  * require updating the WHEN expression, which has the same
   15188             :                  * issues as above.  Since we can't easily tell which case
   15189             :                  * applies, we punt for both.  FIXME someday.
   15190             :                  */
   15191           0 :                 if (subtype == AT_AlterColumnType)
   15192           0 :                     ereport(ERROR,
   15193             :                             (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   15194             :                              errmsg("cannot alter type of a column used in a trigger definition"),
   15195             :                              errdetail("%s depends on column \"%s\"",
   15196             :                                        getObjectDescription(&foundObject, false),
   15197             :                                        colName)));
   15198           0 :                 break;
   15199             : 
   15200           0 :             case PolicyRelationId:
   15201             : 
   15202             :                 /*
   15203             :                  * A policy can depend on a column because the column is
   15204             :                  * specified in the policy's USING or WITH CHECK qual
   15205             :                  * expressions.  It might be possible to rewrite and recheck
   15206             :                  * the policy expression, but punt for now.  It's certainly
   15207             :                  * easy enough to remove and recreate the policy; still, FIXME
   15208             :                  * someday.
   15209             :                  */
   15210           0 :                 if (subtype == AT_AlterColumnType)
   15211           0 :                     ereport(ERROR,
   15212             :                             (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   15213             :                              errmsg("cannot alter type of a column used in a policy definition"),
   15214             :                              errdetail("%s depends on column \"%s\"",
   15215             :                                        getObjectDescription(&foundObject, false),
   15216             :                                        colName)));
   15217           0 :                 break;
   15218             : 
   15219         214 :             case AttrDefaultRelationId:
   15220             :                 {
   15221         214 :                     ObjectAddress col = GetAttrDefaultColumnAddress(foundObject.objectId);
   15222             : 
   15223         214 :                     if (col.objectId == RelationGetRelid(rel) &&
   15224         214 :                         col.objectSubId == attnum)
   15225             :                     {
   15226             :                         /*
   15227             :                          * Ignore the column's own default expression.  The
   15228             :                          * caller deals with it.
   15229             :                          */
   15230             :                     }
   15231             :                     else
   15232             :                     {
   15233             :                         /*
   15234             :                          * This must be a reference from the expression of a
   15235             :                          * generated column elsewhere in the same table.
   15236             :                          * Changing the type/generated expression of a column
   15237             :                          * that is used by a generated column is not allowed
   15238             :                          * by SQL standard, so just punt for now.  It might be
   15239             :                          * doable with some thinking and effort.
   15240             :                          */
   15241          24 :                         if (subtype == AT_AlterColumnType)
   15242          24 :                             ereport(ERROR,
   15243             :                                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   15244             :                                      errmsg("cannot alter type of a column used by a generated column"),
   15245             :                                      errdetail("Column \"%s\" is used by generated column \"%s\".",
   15246             :                                                colName,
   15247             :                                                get_attname(col.objectId,
   15248             :                                                            col.objectSubId,
   15249             :                                                            false))));
   15250             :                     }
   15251         190 :                     break;
   15252             :                 }
   15253             : 
   15254          74 :             case StatisticExtRelationId:
   15255             : 
   15256             :                 /*
   15257             :                  * Give the extended-stats machinery a chance to fix anything
   15258             :                  * that this column type change would break.
   15259             :                  */
   15260          74 :                 RememberStatisticsForRebuilding(foundObject.objectId, tab);
   15261          74 :                 break;
   15262             : 
   15263           0 :             case PublicationRelRelationId:
   15264             : 
   15265             :                 /*
   15266             :                  * Column reference in a PUBLICATION ... FOR TABLE ... WHERE
   15267             :                  * clause.  Same issues as above.  FIXME someday.
   15268             :                  */
   15269           0 :                 if (subtype == AT_AlterColumnType)
   15270           0 :                     ereport(ERROR,
   15271             :                             (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   15272             :                              errmsg("cannot alter type of a column used by a publication WHERE clause"),
   15273             :                              errdetail("%s depends on column \"%s\"",
   15274             :                                        getObjectDescription(&foundObject, false),
   15275             :                                        colName)));
   15276           0 :                 break;
   15277             : 
   15278           0 :             default:
   15279             : 
   15280             :                 /*
   15281             :                  * We don't expect any other sorts of objects to depend on a
   15282             :                  * column.
   15283             :                  */
   15284           0 :                 elog(ERROR, "unexpected object depending on column: %s",
   15285             :                      getObjectDescription(&foundObject, false));
   15286             :                 break;
   15287             :         }
   15288             :     }
   15289             : 
   15290        1254 :     systable_endscan(scan);
   15291        1254 :     table_close(depRel, NoLock);
   15292        1254 : }
   15293             : 
   15294             : /*
   15295             :  * Subroutine for ATExecAlterColumnType: remember that a replica identity
   15296             :  * needs to be reset.
   15297             :  */
   15298             : static void
   15299         456 : RememberReplicaIdentityForRebuilding(Oid indoid, AlteredTableInfo *tab)
   15300             : {
   15301         456 :     if (!get_index_isreplident(indoid))
   15302         438 :         return;
   15303             : 
   15304          18 :     if (tab->replicaIdentityIndex)
   15305           0 :         elog(ERROR, "relation %u has multiple indexes marked as replica identity", tab->relid);
   15306             : 
   15307          18 :     tab->replicaIdentityIndex = get_rel_name(indoid);
   15308             : }
   15309             : 
   15310             : /*
   15311             :  * Subroutine for ATExecAlterColumnType: remember any clustered index.
   15312             :  */
   15313             : static void
   15314         456 : RememberClusterOnForRebuilding(Oid indoid, AlteredTableInfo *tab)
   15315             : {
   15316         456 :     if (!get_index_isclustered(indoid))
   15317         438 :         return;
   15318             : 
   15319          18 :     if (tab->clusterOnIndex)
   15320           0 :         elog(ERROR, "relation %u has multiple clustered indexes", tab->relid);
   15321             : 
   15322          18 :     tab->clusterOnIndex = get_rel_name(indoid);
   15323             : }
   15324             : 
   15325             : /*
   15326             :  * Subroutine for ATExecAlterColumnType: remember that a constraint needs
   15327             :  * to be rebuilt (which we might already know).
   15328             :  */
   15329             : static void
   15330         698 : RememberConstraintForRebuilding(Oid conoid, AlteredTableInfo *tab)
   15331             : {
   15332             :     /*
   15333             :      * This de-duplication check is critical for two independent reasons: we
   15334             :      * mustn't try to recreate the same constraint twice, and if a constraint
   15335             :      * depends on more than one column whose type is to be altered, we must
   15336             :      * capture its definition string before applying any of the column type
   15337             :      * changes.  ruleutils.c will get confused if we ask again later.
   15338             :      */
   15339         698 :     if (!list_member_oid(tab->changedConstraintOids, conoid))
   15340             :     {
   15341             :         /* OK, capture the constraint's existing definition string */
   15342         608 :         char       *defstring = pg_get_constraintdef_command(conoid);
   15343             :         Oid         indoid;
   15344             : 
   15345             :         /*
   15346             :          * It is critical to create not-null constraints ahead of primary key
   15347             :          * indexes; otherwise, the not-null constraint would be created by the
   15348             :          * primary key, and the constraint name would be wrong.
   15349             :          */
   15350         608 :         if (get_constraint_type(conoid) == CONSTRAINT_NOTNULL)
   15351             :         {
   15352         198 :             tab->changedConstraintOids = lcons_oid(conoid,
   15353             :                                                    tab->changedConstraintOids);
   15354         198 :             tab->changedConstraintDefs = lcons(defstring,
   15355             :                                                tab->changedConstraintDefs);
   15356             :         }
   15357             :         else
   15358             :         {
   15359             : 
   15360         410 :             tab->changedConstraintOids = lappend_oid(tab->changedConstraintOids,
   15361             :                                                      conoid);
   15362         410 :             tab->changedConstraintDefs = lappend(tab->changedConstraintDefs,
   15363             :                                                  defstring);
   15364             :         }
   15365             : 
   15366             :         /*
   15367             :          * For the index of a constraint, if any, remember if it is used for
   15368             :          * the table's replica identity or if it is a clustered index, so that
   15369             :          * ATPostAlterTypeCleanup() can queue up commands necessary to restore
   15370             :          * those properties.
   15371             :          */
   15372         608 :         indoid = get_constraint_index(conoid);
   15373         608 :         if (OidIsValid(indoid))
   15374             :         {
   15375         228 :             RememberReplicaIdentityForRebuilding(indoid, tab);
   15376         228 :             RememberClusterOnForRebuilding(indoid, tab);
   15377             :         }
   15378             :     }
   15379         698 : }
   15380             : 
   15381             : /*
   15382             :  * Subroutine for ATExecAlterColumnType: remember that an index needs
   15383             :  * to be rebuilt (which we might already know).
   15384             :  */
   15385             : static void
   15386         248 : RememberIndexForRebuilding(Oid indoid, AlteredTableInfo *tab)
   15387             : {
   15388             :     /*
   15389             :      * This de-duplication check is critical for two independent reasons: we
   15390             :      * mustn't try to recreate the same index twice, and if an index depends
   15391             :      * on more than one column whose type is to be altered, we must capture
   15392             :      * its definition string before applying any of the column type changes.
   15393             :      * ruleutils.c will get confused if we ask again later.
   15394             :      */
   15395         248 :     if (!list_member_oid(tab->changedIndexOids, indoid))
   15396             :     {
   15397             :         /*
   15398             :          * Before adding it as an index-to-rebuild, we'd better see if it
   15399             :          * belongs to a constraint, and if so rebuild the constraint instead.
   15400             :          * Typically this check fails, because constraint indexes normally
   15401             :          * have only dependencies on their constraint.  But it's possible for
   15402             :          * such an index to also have direct dependencies on table columns,
   15403             :          * for example with a partial exclusion constraint.
   15404             :          */
   15405         240 :         Oid         conoid = get_index_constraint(indoid);
   15406             : 
   15407         240 :         if (OidIsValid(conoid))
   15408             :         {
   15409          12 :             RememberConstraintForRebuilding(conoid, tab);
   15410             :         }
   15411             :         else
   15412             :         {
   15413             :             /* OK, capture the index's existing definition string */
   15414         228 :             char       *defstring = pg_get_indexdef_string(indoid);
   15415             : 
   15416         228 :             tab->changedIndexOids = lappend_oid(tab->changedIndexOids,
   15417             :                                                 indoid);
   15418         228 :             tab->changedIndexDefs = lappend(tab->changedIndexDefs,
   15419             :                                             defstring);
   15420             : 
   15421             :             /*
   15422             :              * Remember if this index is used for the table's replica identity
   15423             :              * or if it is a clustered index, so that ATPostAlterTypeCleanup()
   15424             :              * can queue up commands necessary to restore those properties.
   15425             :              */
   15426         228 :             RememberReplicaIdentityForRebuilding(indoid, tab);
   15427         228 :             RememberClusterOnForRebuilding(indoid, tab);
   15428             :         }
   15429             :     }
   15430         248 : }
   15431             : 
   15432             : /*
   15433             :  * Subroutine for ATExecAlterColumnType: remember that a statistics object
   15434             :  * needs to be rebuilt (which we might already know).
   15435             :  */
   15436             : static void
   15437          74 : RememberStatisticsForRebuilding(Oid stxoid, AlteredTableInfo *tab)
   15438             : {
   15439             :     /*
   15440             :      * This de-duplication check is critical for two independent reasons: we
   15441             :      * mustn't try to recreate the same statistics object twice, and if the
   15442             :      * statistics object depends on more than one column whose type is to be
   15443             :      * altered, we must capture its definition string before applying any of
   15444             :      * the type changes. ruleutils.c will get confused if we ask again later.
   15445             :      */
   15446          74 :     if (!list_member_oid(tab->changedStatisticsOids, stxoid))
   15447             :     {
   15448             :         /* OK, capture the statistics object's existing definition string */
   15449          74 :         char       *defstring = pg_get_statisticsobjdef_string(stxoid);
   15450             : 
   15451          74 :         tab->changedStatisticsOids = lappend_oid(tab->changedStatisticsOids,
   15452             :                                                  stxoid);
   15453          74 :         tab->changedStatisticsDefs = lappend(tab->changedStatisticsDefs,
   15454             :                                              defstring);
   15455             :     }
   15456          74 : }
   15457             : 
   15458             : /*
   15459             :  * Cleanup after we've finished all the ALTER TYPE or SET EXPRESSION
   15460             :  * operations for a particular relation.  We have to drop and recreate all the
   15461             :  * indexes and constraints that depend on the altered columns.  We do the
   15462             :  * actual dropping here, but re-creation is managed by adding work queue
   15463             :  * entries to do those steps later.
   15464             :  */
   15465             : static void
   15466        1302 : ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab, LOCKMODE lockmode)
   15467             : {
   15468             :     ObjectAddress obj;
   15469             :     ObjectAddresses *objects;
   15470             :     ListCell   *def_item;
   15471             :     ListCell   *oid_item;
   15472             : 
   15473             :     /*
   15474             :      * Collect all the constraints and indexes to drop so we can process them
   15475             :      * in a single call.  That way we don't have to worry about dependencies
   15476             :      * among them.
   15477             :      */
   15478        1302 :     objects = new_object_addresses();
   15479             : 
   15480             :     /*
   15481             :      * Re-parse the index and constraint definitions, and attach them to the
   15482             :      * appropriate work queue entries.  We do this before dropping because in
   15483             :      * the case of a constraint on another table, we might not yet have
   15484             :      * exclusive lock on the table the constraint is attached to, and we need
   15485             :      * to get that before reparsing/dropping.  (That's possible at least for
   15486             :      * FOREIGN KEY, CHECK, and EXCLUSION constraints; in non-FK cases it
   15487             :      * requires a dependency on the target table's composite type in the other
   15488             :      * table's constraint expressions.)
   15489             :      *
   15490             :      * We can't rely on the output of deparsing to tell us which relation to
   15491             :      * operate on, because concurrent activity might have made the name
   15492             :      * resolve differently.  Instead, we've got to use the OID of the
   15493             :      * constraint or index we're processing to figure out which relation to
   15494             :      * operate on.
   15495             :      */
   15496        1910 :     forboth(oid_item, tab->changedConstraintOids,
   15497             :             def_item, tab->changedConstraintDefs)
   15498             :     {
   15499         608 :         Oid         oldId = lfirst_oid(oid_item);
   15500             :         HeapTuple   tup;
   15501             :         Form_pg_constraint con;
   15502             :         Oid         relid;
   15503             :         Oid         confrelid;
   15504             :         bool        conislocal;
   15505             : 
   15506         608 :         tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(oldId));
   15507         608 :         if (!HeapTupleIsValid(tup)) /* should not happen */
   15508           0 :             elog(ERROR, "cache lookup failed for constraint %u", oldId);
   15509         608 :         con = (Form_pg_constraint) GETSTRUCT(tup);
   15510         608 :         if (OidIsValid(con->conrelid))
   15511         594 :             relid = con->conrelid;
   15512             :         else
   15513             :         {
   15514             :             /* must be a domain constraint */
   15515          14 :             relid = get_typ_typrelid(getBaseType(con->contypid));
   15516          14 :             if (!OidIsValid(relid))
   15517           0 :                 elog(ERROR, "could not identify relation associated with constraint %u", oldId);
   15518             :         }
   15519         608 :         confrelid = con->confrelid;
   15520         608 :         conislocal = con->conislocal;
   15521         608 :         ReleaseSysCache(tup);
   15522             : 
   15523         608 :         ObjectAddressSet(obj, ConstraintRelationId, oldId);
   15524         608 :         add_exact_object_address(&obj, objects);
   15525             : 
   15526             :         /*
   15527             :          * If the constraint is inherited (only), we don't want to inject a
   15528             :          * new definition here; it'll get recreated when
   15529             :          * ATAddCheckNNConstraint recurses from adding the parent table's
   15530             :          * constraint.  But we had to carry the info this far so that we can
   15531             :          * drop the constraint below.
   15532             :          */
   15533         608 :         if (!conislocal)
   15534          28 :             continue;
   15535             : 
   15536             :         /*
   15537             :          * When rebuilding another table's constraint that references the
   15538             :          * table we're modifying, we might not yet have any lock on the other
   15539             :          * table, so get one now.  We'll need AccessExclusiveLock for the DROP
   15540             :          * CONSTRAINT step, so there's no value in asking for anything weaker.
   15541             :          */
   15542         580 :         if (relid != tab->relid)
   15543          48 :             LockRelationOid(relid, AccessExclusiveLock);
   15544             : 
   15545         580 :         ATPostAlterTypeParse(oldId, relid, confrelid,
   15546         580 :                              (char *) lfirst(def_item),
   15547         580 :                              wqueue, lockmode, tab->rewrite);
   15548             :     }
   15549        1530 :     forboth(oid_item, tab->changedIndexOids,
   15550             :             def_item, tab->changedIndexDefs)
   15551             :     {
   15552         228 :         Oid         oldId = lfirst_oid(oid_item);
   15553             :         Oid         relid;
   15554             : 
   15555         228 :         relid = IndexGetRelation(oldId, false);
   15556             : 
   15557             :         /*
   15558             :          * As above, make sure we have lock on the index's table if it's not
   15559             :          * the same table.
   15560             :          */
   15561         228 :         if (relid != tab->relid)
   15562          12 :             LockRelationOid(relid, AccessExclusiveLock);
   15563             : 
   15564         228 :         ATPostAlterTypeParse(oldId, relid, InvalidOid,
   15565         228 :                              (char *) lfirst(def_item),
   15566         228 :                              wqueue, lockmode, tab->rewrite);
   15567             : 
   15568         228 :         ObjectAddressSet(obj, RelationRelationId, oldId);
   15569         228 :         add_exact_object_address(&obj, objects);
   15570             :     }
   15571             : 
   15572             :     /* add dependencies for new statistics */
   15573        1376 :     forboth(oid_item, tab->changedStatisticsOids,
   15574             :             def_item, tab->changedStatisticsDefs)
   15575             :     {
   15576          74 :         Oid         oldId = lfirst_oid(oid_item);
   15577             :         Oid         relid;
   15578             : 
   15579          74 :         relid = StatisticsGetRelation(oldId, false);
   15580             : 
   15581             :         /*
   15582             :          * As above, make sure we have lock on the statistics object's table
   15583             :          * if it's not the same table.  However, we take
   15584             :          * ShareUpdateExclusiveLock here, aligning with the lock level used in
   15585             :          * CreateStatistics and RemoveStatisticsById.
   15586             :          *
   15587             :          * CAUTION: this should be done after all cases that grab
   15588             :          * AccessExclusiveLock, else we risk causing deadlock due to needing
   15589             :          * to promote our table lock.
   15590             :          */
   15591          74 :         if (relid != tab->relid)
   15592          12 :             LockRelationOid(relid, ShareUpdateExclusiveLock);
   15593             : 
   15594          74 :         ATPostAlterTypeParse(oldId, relid, InvalidOid,
   15595          74 :                              (char *) lfirst(def_item),
   15596          74 :                              wqueue, lockmode, tab->rewrite);
   15597             : 
   15598          74 :         ObjectAddressSet(obj, StatisticExtRelationId, oldId);
   15599          74 :         add_exact_object_address(&obj, objects);
   15600             :     }
   15601             : 
   15602             :     /*
   15603             :      * Queue up command to restore replica identity index marking
   15604             :      */
   15605        1302 :     if (tab->replicaIdentityIndex)
   15606             :     {
   15607          18 :         AlterTableCmd *cmd = makeNode(AlterTableCmd);
   15608          18 :         ReplicaIdentityStmt *subcmd = makeNode(ReplicaIdentityStmt);
   15609             : 
   15610          18 :         subcmd->identity_type = REPLICA_IDENTITY_INDEX;
   15611          18 :         subcmd->name = tab->replicaIdentityIndex;
   15612          18 :         cmd->subtype = AT_ReplicaIdentity;
   15613          18 :         cmd->def = (Node *) subcmd;
   15614             : 
   15615             :         /* do it after indexes and constraints */
   15616          18 :         tab->subcmds[AT_PASS_OLD_CONSTR] =
   15617          18 :             lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
   15618             :     }
   15619             : 
   15620             :     /*
   15621             :      * Queue up command to restore marking of index used for cluster.
   15622             :      */
   15623        1302 :     if (tab->clusterOnIndex)
   15624             :     {
   15625          18 :         AlterTableCmd *cmd = makeNode(AlterTableCmd);
   15626             : 
   15627          18 :         cmd->subtype = AT_ClusterOn;
   15628          18 :         cmd->name = tab->clusterOnIndex;
   15629             : 
   15630             :         /* do it after indexes and constraints */
   15631          18 :         tab->subcmds[AT_PASS_OLD_CONSTR] =
   15632          18 :             lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
   15633             :     }
   15634             : 
   15635             :     /*
   15636             :      * It should be okay to use DROP_RESTRICT here, since nothing else should
   15637             :      * be depending on these objects.
   15638             :      */
   15639        1302 :     performMultipleDeletions(objects, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
   15640             : 
   15641        1302 :     free_object_addresses(objects);
   15642             : 
   15643             :     /*
   15644             :      * The objects will get recreated during subsequent passes over the work
   15645             :      * queue.
   15646             :      */
   15647        1302 : }
   15648             : 
   15649             : /*
   15650             :  * Parse the previously-saved definition string for a constraint, index or
   15651             :  * statistics object against the newly-established column data type(s), and
   15652             :  * queue up the resulting command parsetrees for execution.
   15653             :  *
   15654             :  * This might fail if, for example, you have a WHERE clause that uses an
   15655             :  * operator that's not available for the new column type.
   15656             :  */
   15657             : static void
   15658         882 : ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
   15659             :                      List **wqueue, LOCKMODE lockmode, bool rewrite)
   15660             : {
   15661             :     List       *raw_parsetree_list;
   15662             :     List       *querytree_list;
   15663             :     ListCell   *list_item;
   15664             :     Relation    rel;
   15665             : 
   15666             :     /*
   15667             :      * We expect that we will get only ALTER TABLE and CREATE INDEX
   15668             :      * statements. Hence, there is no need to pass them through
   15669             :      * parse_analyze_*() or the rewriter, but instead we need to pass them
   15670             :      * through parse_utilcmd.c to make them ready for execution.
   15671             :      */
   15672         882 :     raw_parsetree_list = raw_parser(cmd, RAW_PARSE_DEFAULT);
   15673         882 :     querytree_list = NIL;
   15674        1764 :     foreach(list_item, raw_parsetree_list)
   15675             :     {
   15676         882 :         RawStmt    *rs = lfirst_node(RawStmt, list_item);
   15677         882 :         Node       *stmt = rs->stmt;
   15678             : 
   15679         882 :         if (IsA(stmt, IndexStmt))
   15680         228 :             querytree_list = lappend(querytree_list,
   15681         228 :                                      transformIndexStmt(oldRelId,
   15682             :                                                         (IndexStmt *) stmt,
   15683             :                                                         cmd));
   15684         654 :         else if (IsA(stmt, AlterTableStmt))
   15685             :         {
   15686             :             List       *beforeStmts;
   15687             :             List       *afterStmts;
   15688             : 
   15689         566 :             stmt = (Node *) transformAlterTableStmt(oldRelId,
   15690             :                                                     (AlterTableStmt *) stmt,
   15691             :                                                     cmd,
   15692             :                                                     &beforeStmts,
   15693             :                                                     &afterStmts);
   15694         566 :             querytree_list = list_concat(querytree_list, beforeStmts);
   15695         566 :             querytree_list = lappend(querytree_list, stmt);
   15696         566 :             querytree_list = list_concat(querytree_list, afterStmts);
   15697             :         }
   15698          88 :         else if (IsA(stmt, CreateStatsStmt))
   15699          74 :             querytree_list = lappend(querytree_list,
   15700          74 :                                      transformStatsStmt(oldRelId,
   15701             :                                                         (CreateStatsStmt *) stmt,
   15702             :                                                         cmd));
   15703             :         else
   15704          14 :             querytree_list = lappend(querytree_list, stmt);
   15705             :     }
   15706             : 
   15707             :     /* Caller should already have acquired whatever lock we need. */
   15708         882 :     rel = relation_open(oldRelId, NoLock);
   15709             : 
   15710             :     /*
   15711             :      * Attach each generated command to the proper place in the work queue.
   15712             :      * Note this could result in creation of entirely new work-queue entries.
   15713             :      *
   15714             :      * Also note that we have to tweak the command subtypes, because it turns
   15715             :      * out that re-creation of indexes and constraints has to act a bit
   15716             :      * differently from initial creation.
   15717             :      */
   15718        1764 :     foreach(list_item, querytree_list)
   15719             :     {
   15720         882 :         Node       *stm = (Node *) lfirst(list_item);
   15721             :         AlteredTableInfo *tab;
   15722             : 
   15723         882 :         tab = ATGetQueueEntry(wqueue, rel);
   15724             : 
   15725         882 :         if (IsA(stm, IndexStmt))
   15726             :         {
   15727         228 :             IndexStmt  *stmt = (IndexStmt *) stm;
   15728             :             AlterTableCmd *newcmd;
   15729             : 
   15730         228 :             if (!rewrite)
   15731          56 :                 TryReuseIndex(oldId, stmt);
   15732         228 :             stmt->reset_default_tblspc = true;
   15733             :             /* keep the index's comment */
   15734         228 :             stmt->idxcomment = GetComment(oldId, RelationRelationId, 0);
   15735             : 
   15736         228 :             newcmd = makeNode(AlterTableCmd);
   15737         228 :             newcmd->subtype = AT_ReAddIndex;
   15738         228 :             newcmd->def = (Node *) stmt;
   15739         228 :             tab->subcmds[AT_PASS_OLD_INDEX] =
   15740         228 :                 lappend(tab->subcmds[AT_PASS_OLD_INDEX], newcmd);
   15741             :         }
   15742         654 :         else if (IsA(stm, AlterTableStmt))
   15743             :         {
   15744         566 :             AlterTableStmt *stmt = (AlterTableStmt *) stm;
   15745             :             ListCell   *lcmd;
   15746             : 
   15747        1132 :             foreach(lcmd, stmt->cmds)
   15748             :             {
   15749         566 :                 AlterTableCmd *cmd = lfirst_node(AlterTableCmd, lcmd);
   15750             : 
   15751         566 :                 if (cmd->subtype == AT_AddIndex)
   15752             :                 {
   15753             :                     IndexStmt  *indstmt;
   15754             :                     Oid         indoid;
   15755             : 
   15756         228 :                     indstmt = castNode(IndexStmt, cmd->def);
   15757         228 :                     indoid = get_constraint_index(oldId);
   15758             : 
   15759         228 :                     if (!rewrite)
   15760          48 :                         TryReuseIndex(indoid, indstmt);
   15761             :                     /* keep any comment on the index */
   15762         228 :                     indstmt->idxcomment = GetComment(indoid,
   15763             :                                                      RelationRelationId, 0);
   15764         228 :                     indstmt->reset_default_tblspc = true;
   15765             : 
   15766         228 :                     cmd->subtype = AT_ReAddIndex;
   15767         228 :                     tab->subcmds[AT_PASS_OLD_INDEX] =
   15768         228 :                         lappend(tab->subcmds[AT_PASS_OLD_INDEX], cmd);
   15769             : 
   15770             :                     /* recreate any comment on the constraint */
   15771         228 :                     RebuildConstraintComment(tab,
   15772             :                                              AT_PASS_OLD_INDEX,
   15773             :                                              oldId,
   15774             :                                              rel,
   15775             :                                              NIL,
   15776         228 :                                              indstmt->idxname);
   15777             :                 }
   15778         338 :                 else if (cmd->subtype == AT_AddConstraint)
   15779             :                 {
   15780         338 :                     Constraint *con = castNode(Constraint, cmd->def);
   15781             : 
   15782         338 :                     con->old_pktable_oid = refRelId;
   15783             :                     /* rewriting neither side of a FK */
   15784         338 :                     if (con->contype == CONSTR_FOREIGN &&
   15785          72 :                         !rewrite && tab->rewrite == 0)
   15786           6 :                         TryReuseForeignKey(oldId, con);
   15787         338 :                     con->reset_default_tblspc = true;
   15788         338 :                     cmd->subtype = AT_ReAddConstraint;
   15789         338 :                     tab->subcmds[AT_PASS_OLD_CONSTR] =
   15790         338 :                         lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
   15791             : 
   15792             :                     /*
   15793             :                      * Recreate any comment on the constraint.  If we have
   15794             :                      * recreated a primary key, then transformTableConstraint
   15795             :                      * has added an unnamed not-null constraint here; skip
   15796             :                      * this in that case.
   15797             :                      */
   15798         338 :                     if (con->conname)
   15799         338 :                         RebuildConstraintComment(tab,
   15800             :                                                  AT_PASS_OLD_CONSTR,
   15801             :                                                  oldId,
   15802             :                                                  rel,
   15803             :                                                  NIL,
   15804         338 :                                                  con->conname);
   15805             :                     else
   15806             :                         Assert(con->contype == CONSTR_NOTNULL);
   15807             :                 }
   15808             :                 else
   15809           0 :                     elog(ERROR, "unexpected statement subtype: %d",
   15810             :                          (int) cmd->subtype);
   15811             :             }
   15812             :         }
   15813          88 :         else if (IsA(stm, AlterDomainStmt))
   15814             :         {
   15815          14 :             AlterDomainStmt *stmt = (AlterDomainStmt *) stm;
   15816             : 
   15817          14 :             if (stmt->subtype == AD_AddConstraint)
   15818             :             {
   15819          14 :                 Constraint *con = castNode(Constraint, stmt->def);
   15820          14 :                 AlterTableCmd *cmd = makeNode(AlterTableCmd);
   15821             : 
   15822          14 :                 cmd->subtype = AT_ReAddDomainConstraint;
   15823          14 :                 cmd->def = (Node *) stmt;
   15824          14 :                 tab->subcmds[AT_PASS_OLD_CONSTR] =
   15825          14 :                     lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
   15826             : 
   15827             :                 /* recreate any comment on the constraint */
   15828          14 :                 RebuildConstraintComment(tab,
   15829             :                                          AT_PASS_OLD_CONSTR,
   15830             :                                          oldId,
   15831             :                                          NULL,
   15832             :                                          stmt->typeName,
   15833          14 :                                          con->conname);
   15834             :             }
   15835             :             else
   15836           0 :                 elog(ERROR, "unexpected statement subtype: %d",
   15837             :                      (int) stmt->subtype);
   15838             :         }
   15839          74 :         else if (IsA(stm, CreateStatsStmt))
   15840             :         {
   15841          74 :             CreateStatsStmt *stmt = (CreateStatsStmt *) stm;
   15842             :             AlterTableCmd *newcmd;
   15843             : 
   15844             :             /* keep the statistics object's comment */
   15845          74 :             stmt->stxcomment = GetComment(oldId, StatisticExtRelationId, 0);
   15846             : 
   15847          74 :             newcmd = makeNode(AlterTableCmd);
   15848          74 :             newcmd->subtype = AT_ReAddStatistics;
   15849          74 :             newcmd->def = (Node *) stmt;
   15850          74 :             tab->subcmds[AT_PASS_MISC] =
   15851          74 :                 lappend(tab->subcmds[AT_PASS_MISC], newcmd);
   15852             :         }
   15853             :         else
   15854           0 :             elog(ERROR, "unexpected statement type: %d",
   15855             :                  (int) nodeTag(stm));
   15856             :     }
   15857             : 
   15858         882 :     relation_close(rel, NoLock);
   15859         882 : }
   15860             : 
   15861             : /*
   15862             :  * Subroutine for ATPostAlterTypeParse() to recreate any existing comment
   15863             :  * for a table or domain constraint that is being rebuilt.
   15864             :  *
   15865             :  * objid is the OID of the constraint.
   15866             :  * Pass "rel" for a table constraint, or "domname" (domain's qualified name
   15867             :  * as a string list) for a domain constraint.
   15868             :  * (We could dig that info, as well as the conname, out of the pg_constraint
   15869             :  * entry; but callers already have them so might as well pass them.)
   15870             :  */
   15871             : static void
   15872         580 : RebuildConstraintComment(AlteredTableInfo *tab, AlterTablePass pass, Oid objid,
   15873             :                          Relation rel, List *domname,
   15874             :                          const char *conname)
   15875             : {
   15876             :     CommentStmt *cmd;
   15877             :     char       *comment_str;
   15878             :     AlterTableCmd *newcmd;
   15879             : 
   15880             :     /* Look for comment for object wanted, and leave if none */
   15881         580 :     comment_str = GetComment(objid, ConstraintRelationId, 0);
   15882         580 :     if (comment_str == NULL)
   15883         490 :         return;
   15884             : 
   15885             :     /* Build CommentStmt node, copying all input data for safety */
   15886          90 :     cmd = makeNode(CommentStmt);
   15887          90 :     if (rel)
   15888             :     {
   15889          78 :         cmd->objtype = OBJECT_TABCONSTRAINT;
   15890          78 :         cmd->object = (Node *)
   15891          78 :             list_make3(makeString(get_namespace_name(RelationGetNamespace(rel))),
   15892             :                        makeString(pstrdup(RelationGetRelationName(rel))),
   15893             :                        makeString(pstrdup(conname)));
   15894             :     }
   15895             :     else
   15896             :     {
   15897          12 :         cmd->objtype = OBJECT_DOMCONSTRAINT;
   15898          12 :         cmd->object = (Node *)
   15899          12 :             list_make2(makeTypeNameFromNameList(copyObject(domname)),
   15900             :                        makeString(pstrdup(conname)));
   15901             :     }
   15902          90 :     cmd->comment = comment_str;
   15903             : 
   15904             :     /* Append it to list of commands */
   15905          90 :     newcmd = makeNode(AlterTableCmd);
   15906          90 :     newcmd->subtype = AT_ReAddComment;
   15907          90 :     newcmd->def = (Node *) cmd;
   15908          90 :     tab->subcmds[pass] = lappend(tab->subcmds[pass], newcmd);
   15909             : }
   15910             : 
   15911             : /*
   15912             :  * Subroutine for ATPostAlterTypeParse().  Calls out to CheckIndexCompatible()
   15913             :  * for the real analysis, then mutates the IndexStmt based on that verdict.
   15914             :  */
   15915             : static void
   15916         104 : TryReuseIndex(Oid oldId, IndexStmt *stmt)
   15917             : {
   15918         104 :     if (CheckIndexCompatible(oldId,
   15919         104 :                              stmt->accessMethod,
   15920         104 :                              stmt->indexParams,
   15921         104 :                              stmt->excludeOpNames,
   15922         104 :                              stmt->iswithoutoverlaps))
   15923             :     {
   15924         104 :         Relation    irel = index_open(oldId, NoLock);
   15925             : 
   15926             :         /* If it's a partitioned index, there is no storage to share. */
   15927         104 :         if (irel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
   15928             :         {
   15929          74 :             stmt->oldNumber = irel->rd_locator.relNumber;
   15930          74 :             stmt->oldCreateSubid = irel->rd_createSubid;
   15931          74 :             stmt->oldFirstRelfilelocatorSubid = irel->rd_firstRelfilelocatorSubid;
   15932             :         }
   15933         104 :         index_close(irel, NoLock);
   15934             :     }
   15935         104 : }
   15936             : 
   15937             : /*
   15938             :  * Subroutine for ATPostAlterTypeParse().
   15939             :  *
   15940             :  * Stash the old P-F equality operator into the Constraint node, for possible
   15941             :  * use by ATAddForeignKeyConstraint() in determining whether revalidation of
   15942             :  * this constraint can be skipped.
   15943             :  */
   15944             : static void
   15945           6 : TryReuseForeignKey(Oid oldId, Constraint *con)
   15946             : {
   15947             :     HeapTuple   tup;
   15948             :     Datum       adatum;
   15949             :     ArrayType  *arr;
   15950             :     Oid        *rawarr;
   15951             :     int         numkeys;
   15952             :     int         i;
   15953             : 
   15954             :     Assert(con->contype == CONSTR_FOREIGN);
   15955             :     Assert(con->old_conpfeqop == NIL);   /* already prepared this node */
   15956             : 
   15957           6 :     tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(oldId));
   15958           6 :     if (!HeapTupleIsValid(tup)) /* should not happen */
   15959           0 :         elog(ERROR, "cache lookup failed for constraint %u", oldId);
   15960             : 
   15961           6 :     adatum = SysCacheGetAttrNotNull(CONSTROID, tup,
   15962             :                                     Anum_pg_constraint_conpfeqop);
   15963           6 :     arr = DatumGetArrayTypeP(adatum);   /* ensure not toasted */
   15964           6 :     numkeys = ARR_DIMS(arr)[0];
   15965             :     /* test follows the one in ri_FetchConstraintInfo() */
   15966           6 :     if (ARR_NDIM(arr) != 1 ||
   15967           6 :         ARR_HASNULL(arr) ||
   15968           6 :         ARR_ELEMTYPE(arr) != OIDOID)
   15969           0 :         elog(ERROR, "conpfeqop is not a 1-D Oid array");
   15970           6 :     rawarr = (Oid *) ARR_DATA_PTR(arr);
   15971             : 
   15972             :     /* stash a List of the operator Oids in our Constraint node */
   15973          12 :     for (i = 0; i < numkeys; i++)
   15974           6 :         con->old_conpfeqop = lappend_oid(con->old_conpfeqop, rawarr[i]);
   15975             : 
   15976           6 :     ReleaseSysCache(tup);
   15977           6 : }
   15978             : 
   15979             : /*
   15980             :  * ALTER COLUMN .. OPTIONS ( ... )
   15981             :  *
   15982             :  * Returns the address of the modified column
   15983             :  */
   15984             : static ObjectAddress
   15985         172 : ATExecAlterColumnGenericOptions(Relation rel,
   15986             :                                 const char *colName,
   15987             :                                 List *options,
   15988             :                                 LOCKMODE lockmode)
   15989             : {
   15990             :     Relation    ftrel;
   15991             :     Relation    attrel;
   15992             :     ForeignServer *server;
   15993             :     ForeignDataWrapper *fdw;
   15994             :     HeapTuple   tuple;
   15995             :     HeapTuple   newtuple;
   15996             :     bool        isnull;
   15997             :     Datum       repl_val[Natts_pg_attribute];
   15998             :     bool        repl_null[Natts_pg_attribute];
   15999             :     bool        repl_repl[Natts_pg_attribute];
   16000             :     Datum       datum;
   16001             :     Form_pg_foreign_table fttableform;
   16002             :     Form_pg_attribute atttableform;
   16003             :     AttrNumber  attnum;
   16004             :     ObjectAddress address;
   16005             : 
   16006         172 :     if (options == NIL)
   16007           0 :         return InvalidObjectAddress;
   16008             : 
   16009             :     /* First, determine FDW validator associated to the foreign table. */
   16010         172 :     ftrel = table_open(ForeignTableRelationId, AccessShareLock);
   16011         172 :     tuple = SearchSysCache1(FOREIGNTABLEREL, ObjectIdGetDatum(rel->rd_id));
   16012         172 :     if (!HeapTupleIsValid(tuple))
   16013           0 :         ereport(ERROR,
   16014             :                 (errcode(ERRCODE_UNDEFINED_OBJECT),
   16015             :                  errmsg("foreign table \"%s\" does not exist",
   16016             :                         RelationGetRelationName(rel))));
   16017         172 :     fttableform = (Form_pg_foreign_table) GETSTRUCT(tuple);
   16018         172 :     server = GetForeignServer(fttableform->ftserver);
   16019         172 :     fdw = GetForeignDataWrapper(server->fdwid);
   16020             : 
   16021         172 :     table_close(ftrel, AccessShareLock);
   16022         172 :     ReleaseSysCache(tuple);
   16023             : 
   16024         172 :     attrel = table_open(AttributeRelationId, RowExclusiveLock);
   16025         172 :     tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
   16026         172 :     if (!HeapTupleIsValid(tuple))
   16027           0 :         ereport(ERROR,
   16028             :                 (errcode(ERRCODE_UNDEFINED_COLUMN),
   16029             :                  errmsg("column \"%s\" of relation \"%s\" does not exist",
   16030             :                         colName, RelationGetRelationName(rel))));
   16031             : 
   16032             :     /* Prevent them from altering a system attribute */
   16033         172 :     atttableform = (Form_pg_attribute) GETSTRUCT(tuple);
   16034         172 :     attnum = atttableform->attnum;
   16035         172 :     if (attnum <= 0)
   16036           6 :         ereport(ERROR,
   16037             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   16038             :                  errmsg("cannot alter system column \"%s\"", colName)));
   16039             : 
   16040             : 
   16041             :     /* Initialize buffers for new tuple values */
   16042         166 :     memset(repl_val, 0, sizeof(repl_val));
   16043         166 :     memset(repl_null, false, sizeof(repl_null));
   16044         166 :     memset(repl_repl, false, sizeof(repl_repl));
   16045             : 
   16046             :     /* Extract the current options */
   16047         166 :     datum = SysCacheGetAttr(ATTNAME,
   16048             :                             tuple,
   16049             :                             Anum_pg_attribute_attfdwoptions,
   16050             :                             &isnull);
   16051         166 :     if (isnull)
   16052         156 :         datum = PointerGetDatum(NULL);
   16053             : 
   16054             :     /* Transform the options */
   16055         166 :     datum = transformGenericOptions(AttributeRelationId,
   16056             :                                     datum,
   16057             :                                     options,
   16058             :                                     fdw->fdwvalidator);
   16059             : 
   16060         166 :     if (DatumGetPointer(datum) != NULL)
   16061         166 :         repl_val[Anum_pg_attribute_attfdwoptions - 1] = datum;
   16062             :     else
   16063           0 :         repl_null[Anum_pg_attribute_attfdwoptions - 1] = true;
   16064             : 
   16065         166 :     repl_repl[Anum_pg_attribute_attfdwoptions - 1] = true;
   16066             : 
   16067             :     /* Everything looks good - update the tuple */
   16068             : 
   16069         166 :     newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrel),
   16070             :                                  repl_val, repl_null, repl_repl);
   16071             : 
   16072         166 :     CatalogTupleUpdate(attrel, &newtuple->t_self, newtuple);
   16073             : 
   16074         166 :     InvokeObjectPostAlterHook(RelationRelationId,
   16075             :                               RelationGetRelid(rel),
   16076             :                               atttableform->attnum);
   16077         166 :     ObjectAddressSubSet(address, RelationRelationId,
   16078             :                         RelationGetRelid(rel), attnum);
   16079             : 
   16080         166 :     ReleaseSysCache(tuple);
   16081             : 
   16082         166 :     table_close(attrel, RowExclusiveLock);
   16083             : 
   16084         166 :     heap_freetuple(newtuple);
   16085             : 
   16086         166 :     return address;
   16087             : }
   16088             : 
   16089             : /*
   16090             :  * ALTER TABLE OWNER
   16091             :  *
   16092             :  * recursing is true if we are recursing from a table to its indexes,
   16093             :  * sequences, or toast table.  We don't allow the ownership of those things to
   16094             :  * be changed separately from the parent table.  Also, we can skip permission
   16095             :  * checks (this is necessary not just an optimization, else we'd fail to
   16096             :  * handle toast tables properly).
   16097             :  *
   16098             :  * recursing is also true if ALTER TYPE OWNER is calling us to fix up a
   16099             :  * free-standing composite type.
   16100             :  */
   16101             : void
   16102        2284 : ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing, LOCKMODE lockmode)
   16103             : {
   16104             :     Relation    target_rel;
   16105             :     Relation    class_rel;
   16106             :     HeapTuple   tuple;
   16107             :     Form_pg_class tuple_class;
   16108             : 
   16109             :     /*
   16110             :      * Get exclusive lock till end of transaction on the target table. Use
   16111             :      * relation_open so that we can work on indexes and sequences.
   16112             :      */
   16113        2284 :     target_rel = relation_open(relationOid, lockmode);
   16114             : 
   16115             :     /* Get its pg_class tuple, too */
   16116        2284 :     class_rel = table_open(RelationRelationId, RowExclusiveLock);
   16117             : 
   16118        2284 :     tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relationOid));
   16119        2284 :     if (!HeapTupleIsValid(tuple))
   16120           0 :         elog(ERROR, "cache lookup failed for relation %u", relationOid);
   16121        2284 :     tuple_class = (Form_pg_class) GETSTRUCT(tuple);
   16122             : 
   16123             :     /* Can we change the ownership of this tuple? */
   16124        2284 :     switch (tuple_class->relkind)
   16125             :     {
   16126        2000 :         case RELKIND_RELATION:
   16127             :         case RELKIND_VIEW:
   16128             :         case RELKIND_MATVIEW:
   16129             :         case RELKIND_FOREIGN_TABLE:
   16130             :         case RELKIND_PARTITIONED_TABLE:
   16131             :             /* ok to change owner */
   16132        2000 :             break;
   16133          96 :         case RELKIND_INDEX:
   16134          96 :             if (!recursing)
   16135             :             {
   16136             :                 /*
   16137             :                  * Because ALTER INDEX OWNER used to be allowed, and in fact
   16138             :                  * is generated by old versions of pg_dump, we give a warning
   16139             :                  * and do nothing rather than erroring out.  Also, to avoid
   16140             :                  * unnecessary chatter while restoring those old dumps, say
   16141             :                  * nothing at all if the command would be a no-op anyway.
   16142             :                  */
   16143           0 :                 if (tuple_class->relowner != newOwnerId)
   16144           0 :                     ereport(WARNING,
   16145             :                             (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   16146             :                              errmsg("cannot change owner of index \"%s\"",
   16147             :                                     NameStr(tuple_class->relname)),
   16148             :                              errhint("Change the ownership of the index's table instead.")));
   16149             :                 /* quick hack to exit via the no-op path */
   16150           0 :                 newOwnerId = tuple_class->relowner;
   16151             :             }
   16152          96 :             break;
   16153          20 :         case RELKIND_PARTITIONED_INDEX:
   16154          20 :             if (recursing)
   16155          20 :                 break;
   16156           0 :             ereport(ERROR,
   16157             :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   16158             :                      errmsg("cannot change owner of index \"%s\"",
   16159             :                             NameStr(tuple_class->relname)),
   16160             :                      errhint("Change the ownership of the index's table instead.")));
   16161             :             break;
   16162         118 :         case RELKIND_SEQUENCE:
   16163         118 :             if (!recursing &&
   16164          70 :                 tuple_class->relowner != newOwnerId)
   16165             :             {
   16166             :                 /* if it's an owned sequence, disallow changing it by itself */
   16167             :                 Oid         tableId;
   16168             :                 int32       colId;
   16169             : 
   16170           0 :                 if (sequenceIsOwned(relationOid, DEPENDENCY_AUTO, &tableId, &colId) ||
   16171           0 :                     sequenceIsOwned(relationOid, DEPENDENCY_INTERNAL, &tableId, &colId))
   16172           0 :                     ereport(ERROR,
   16173             :                             (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   16174             :                              errmsg("cannot change owner of sequence \"%s\"",
   16175             :                                     NameStr(tuple_class->relname)),
   16176             :                              errdetail("Sequence \"%s\" is linked to table \"%s\".",
   16177             :                                        NameStr(tuple_class->relname),
   16178             :                                        get_rel_name(tableId))));
   16179             :             }
   16180         118 :             break;
   16181           8 :         case RELKIND_COMPOSITE_TYPE:
   16182           8 :             if (recursing)
   16183           8 :                 break;
   16184           0 :             ereport(ERROR,
   16185             :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   16186             :                      errmsg("\"%s\" is a composite type",
   16187             :                             NameStr(tuple_class->relname)),
   16188             :             /* translator: %s is an SQL ALTER command */
   16189             :                      errhint("Use %s instead.",
   16190             :                              "ALTER TYPE")));
   16191             :             break;
   16192          42 :         case RELKIND_TOASTVALUE:
   16193          42 :             if (recursing)
   16194          42 :                 break;
   16195             :             /* FALL THRU */
   16196             :         default:
   16197           0 :             ereport(ERROR,
   16198             :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   16199             :                      errmsg("cannot change owner of relation \"%s\"",
   16200             :                             NameStr(tuple_class->relname)),
   16201             :                      errdetail_relkind_not_supported(tuple_class->relkind)));
   16202             :     }
   16203             : 
   16204             :     /*
   16205             :      * If the new owner is the same as the existing owner, consider the
   16206             :      * command to have succeeded.  This is for dump restoration purposes.
   16207             :      */
   16208        2284 :     if (tuple_class->relowner != newOwnerId)
   16209             :     {
   16210             :         Datum       repl_val[Natts_pg_class];
   16211             :         bool        repl_null[Natts_pg_class];
   16212             :         bool        repl_repl[Natts_pg_class];
   16213             :         Acl        *newAcl;
   16214             :         Datum       aclDatum;
   16215             :         bool        isNull;
   16216             :         HeapTuple   newtuple;
   16217             : 
   16218             :         /* skip permission checks when recursing to index or toast table */
   16219         546 :         if (!recursing)
   16220             :         {
   16221             :             /* Superusers can always do it */
   16222         328 :             if (!superuser())
   16223             :             {
   16224          42 :                 Oid         namespaceOid = tuple_class->relnamespace;
   16225             :                 AclResult   aclresult;
   16226             : 
   16227             :                 /* Otherwise, must be owner of the existing object */
   16228          42 :                 if (!object_ownercheck(RelationRelationId, relationOid, GetUserId()))
   16229           0 :                     aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relationOid)),
   16230           0 :                                    RelationGetRelationName(target_rel));
   16231             : 
   16232             :                 /* Must be able to become new owner */
   16233          42 :                 check_can_set_role(GetUserId(), newOwnerId);
   16234             : 
   16235             :                 /* New owner must have CREATE privilege on namespace */
   16236          30 :                 aclresult = object_aclcheck(NamespaceRelationId, namespaceOid, newOwnerId,
   16237             :                                             ACL_CREATE);
   16238          30 :                 if (aclresult != ACLCHECK_OK)
   16239           0 :                     aclcheck_error(aclresult, OBJECT_SCHEMA,
   16240           0 :                                    get_namespace_name(namespaceOid));
   16241             :             }
   16242             :         }
   16243             : 
   16244         534 :         memset(repl_null, false, sizeof(repl_null));
   16245         534 :         memset(repl_repl, false, sizeof(repl_repl));
   16246             : 
   16247         534 :         repl_repl[Anum_pg_class_relowner - 1] = true;
   16248         534 :         repl_val[Anum_pg_class_relowner - 1] = ObjectIdGetDatum(newOwnerId);
   16249             : 
   16250             :         /*
   16251             :          * Determine the modified ACL for the new owner.  This is only
   16252             :          * necessary when the ACL is non-null.
   16253             :          */
   16254         534 :         aclDatum = SysCacheGetAttr(RELOID, tuple,
   16255             :                                    Anum_pg_class_relacl,
   16256             :                                    &isNull);
   16257         534 :         if (!isNull)
   16258             :         {
   16259          46 :             newAcl = aclnewowner(DatumGetAclP(aclDatum),
   16260             :                                  tuple_class->relowner, newOwnerId);
   16261          46 :             repl_repl[Anum_pg_class_relacl - 1] = true;
   16262          46 :             repl_val[Anum_pg_class_relacl - 1] = PointerGetDatum(newAcl);
   16263             :         }
   16264             : 
   16265         534 :         newtuple = heap_modify_tuple(tuple, RelationGetDescr(class_rel), repl_val, repl_null, repl_repl);
   16266             : 
   16267         534 :         CatalogTupleUpdate(class_rel, &newtuple->t_self, newtuple);
   16268             : 
   16269         534 :         heap_freetuple(newtuple);
   16270             : 
   16271             :         /*
   16272             :          * We must similarly update any per-column ACLs to reflect the new
   16273             :          * owner; for neatness reasons that's split out as a subroutine.
   16274             :          */
   16275         534 :         change_owner_fix_column_acls(relationOid,
   16276             :                                      tuple_class->relowner,
   16277             :                                      newOwnerId);
   16278             : 
   16279             :         /*
   16280             :          * Update owner dependency reference, if any.  A composite type has
   16281             :          * none, because it's tracked for the pg_type entry instead of here;
   16282             :          * indexes and TOAST tables don't have their own entries either.
   16283             :          */
   16284         534 :         if (tuple_class->relkind != RELKIND_COMPOSITE_TYPE &&
   16285         526 :             tuple_class->relkind != RELKIND_INDEX &&
   16286         430 :             tuple_class->relkind != RELKIND_PARTITIONED_INDEX &&
   16287         410 :             tuple_class->relkind != RELKIND_TOASTVALUE)
   16288         368 :             changeDependencyOnOwner(RelationRelationId, relationOid,
   16289             :                                     newOwnerId);
   16290             : 
   16291             :         /*
   16292             :          * Also change the ownership of the table's row type, if it has one
   16293             :          */
   16294         534 :         if (OidIsValid(tuple_class->reltype))
   16295         342 :             AlterTypeOwnerInternal(tuple_class->reltype, newOwnerId);
   16296             : 
   16297             :         /*
   16298             :          * If we are operating on a table or materialized view, also change
   16299             :          * the ownership of any indexes and sequences that belong to the
   16300             :          * relation, as well as its toast table (if it has one).
   16301             :          */
   16302         534 :         if (tuple_class->relkind == RELKIND_RELATION ||
   16303         274 :             tuple_class->relkind == RELKIND_PARTITIONED_TABLE ||
   16304         224 :             tuple_class->relkind == RELKIND_MATVIEW ||
   16305         224 :             tuple_class->relkind == RELKIND_TOASTVALUE)
   16306             :         {
   16307             :             List       *index_oid_list;
   16308             :             ListCell   *i;
   16309             : 
   16310             :             /* Find all the indexes belonging to this relation */
   16311         352 :             index_oid_list = RelationGetIndexList(target_rel);
   16312             : 
   16313             :             /* For each index, recursively change its ownership */
   16314         468 :             foreach(i, index_oid_list)
   16315         116 :                 ATExecChangeOwner(lfirst_oid(i), newOwnerId, true, lockmode);
   16316             : 
   16317         352 :             list_free(index_oid_list);
   16318             :         }
   16319             : 
   16320             :         /* If it has a toast table, recurse to change its ownership */
   16321         534 :         if (tuple_class->reltoastrelid != InvalidOid)
   16322          42 :             ATExecChangeOwner(tuple_class->reltoastrelid, newOwnerId,
   16323             :                               true, lockmode);
   16324             : 
   16325             :         /* If it has dependent sequences, recurse to change them too */
   16326         534 :         change_owner_recurse_to_sequences(relationOid, newOwnerId, lockmode);
   16327             :     }
   16328             : 
   16329        2272 :     InvokeObjectPostAlterHook(RelationRelationId, relationOid, 0);
   16330             : 
   16331        2272 :     ReleaseSysCache(tuple);
   16332        2272 :     table_close(class_rel, RowExclusiveLock);
   16333        2272 :     relation_close(target_rel, NoLock);
   16334        2272 : }
   16335             : 
   16336             : /*
   16337             :  * change_owner_fix_column_acls
   16338             :  *
   16339             :  * Helper function for ATExecChangeOwner.  Scan the columns of the table
   16340             :  * and fix any non-null column ACLs to reflect the new owner.
   16341             :  */
   16342             : static void
   16343         534 : change_owner_fix_column_acls(Oid relationOid, Oid oldOwnerId, Oid newOwnerId)
   16344             : {
   16345             :     Relation    attRelation;
   16346             :     SysScanDesc scan;
   16347             :     ScanKeyData key[1];
   16348             :     HeapTuple   attributeTuple;
   16349             : 
   16350         534 :     attRelation = table_open(AttributeRelationId, RowExclusiveLock);
   16351         534 :     ScanKeyInit(&key[0],
   16352             :                 Anum_pg_attribute_attrelid,
   16353             :                 BTEqualStrategyNumber, F_OIDEQ,
   16354             :                 ObjectIdGetDatum(relationOid));
   16355         534 :     scan = systable_beginscan(attRelation, AttributeRelidNumIndexId,
   16356             :                               true, NULL, 1, key);
   16357        3768 :     while (HeapTupleIsValid(attributeTuple = systable_getnext(scan)))
   16358             :     {
   16359        3234 :         Form_pg_attribute att = (Form_pg_attribute) GETSTRUCT(attributeTuple);
   16360             :         Datum       repl_val[Natts_pg_attribute];
   16361             :         bool        repl_null[Natts_pg_attribute];
   16362             :         bool        repl_repl[Natts_pg_attribute];
   16363             :         Acl        *newAcl;
   16364             :         Datum       aclDatum;
   16365             :         bool        isNull;
   16366             :         HeapTuple   newtuple;
   16367             : 
   16368             :         /* Ignore dropped columns */
   16369        3234 :         if (att->attisdropped)
   16370        3232 :             continue;
   16371             : 
   16372        3234 :         aclDatum = heap_getattr(attributeTuple,
   16373             :                                 Anum_pg_attribute_attacl,
   16374             :                                 RelationGetDescr(attRelation),
   16375             :                                 &isNull);
   16376             :         /* Null ACLs do not require changes */
   16377        3234 :         if (isNull)
   16378        3232 :             continue;
   16379             : 
   16380           2 :         memset(repl_null, false, sizeof(repl_null));
   16381           2 :         memset(repl_repl, false, sizeof(repl_repl));
   16382             : 
   16383           2 :         newAcl = aclnewowner(DatumGetAclP(aclDatum),
   16384             :                              oldOwnerId, newOwnerId);
   16385           2 :         repl_repl[Anum_pg_attribute_attacl - 1] = true;
   16386           2 :         repl_val[Anum_pg_attribute_attacl - 1] = PointerGetDatum(newAcl);
   16387             : 
   16388           2 :         newtuple = heap_modify_tuple(attributeTuple,
   16389             :                                      RelationGetDescr(attRelation),
   16390             :                                      repl_val, repl_null, repl_repl);
   16391             : 
   16392           2 :         CatalogTupleUpdate(attRelation, &newtuple->t_self, newtuple);
   16393             : 
   16394           2 :         heap_freetuple(newtuple);
   16395             :     }
   16396         534 :     systable_endscan(scan);
   16397         534 :     table_close(attRelation, RowExclusiveLock);
   16398         534 : }
   16399             : 
   16400             : /*
   16401             :  * change_owner_recurse_to_sequences
   16402             :  *
   16403             :  * Helper function for ATExecChangeOwner.  Examines pg_depend searching
   16404             :  * for sequences that are dependent on serial columns, and changes their
   16405             :  * ownership.
   16406             :  */
   16407             : static void
   16408         534 : change_owner_recurse_to_sequences(Oid relationOid, Oid newOwnerId, LOCKMODE lockmode)
   16409             : {
   16410             :     Relation    depRel;
   16411             :     SysScanDesc scan;
   16412             :     ScanKeyData key[2];
   16413             :     HeapTuple   tup;
   16414             : 
   16415             :     /*
   16416             :      * SERIAL sequences are those having an auto dependency on one of the
   16417             :      * table's columns (we don't care *which* column, exactly).
   16418             :      */
   16419         534 :     depRel = table_open(DependRelationId, AccessShareLock);
   16420             : 
   16421         534 :     ScanKeyInit(&key[0],
   16422             :                 Anum_pg_depend_refclassid,
   16423             :                 BTEqualStrategyNumber, F_OIDEQ,
   16424             :                 ObjectIdGetDatum(RelationRelationId));
   16425         534 :     ScanKeyInit(&key[1],
   16426             :                 Anum_pg_depend_refobjid,
   16427             :                 BTEqualStrategyNumber, F_OIDEQ,
   16428             :                 ObjectIdGetDatum(relationOid));
   16429             :     /* we leave refobjsubid unspecified */
   16430             : 
   16431         534 :     scan = systable_beginscan(depRel, DependReferenceIndexId, true,
   16432             :                               NULL, 2, key);
   16433             : 
   16434        1512 :     while (HeapTupleIsValid(tup = systable_getnext(scan)))
   16435             :     {
   16436         978 :         Form_pg_depend depForm = (Form_pg_depend) GETSTRUCT(tup);
   16437             :         Relation    seqRel;
   16438             : 
   16439             :         /* skip dependencies other than auto dependencies on columns */
   16440         978 :         if (depForm->refobjsubid == 0 ||
   16441         364 :             depForm->classid != RelationRelationId ||
   16442         142 :             depForm->objsubid != 0 ||
   16443         142 :             !(depForm->deptype == DEPENDENCY_AUTO || depForm->deptype == DEPENDENCY_INTERNAL))
   16444         836 :             continue;
   16445             : 
   16446             :         /* Use relation_open just in case it's an index */
   16447         142 :         seqRel = relation_open(depForm->objid, lockmode);
   16448             : 
   16449             :         /* skip non-sequence relations */
   16450         142 :         if (RelationGetForm(seqRel)->relkind != RELKIND_SEQUENCE)
   16451             :         {
   16452             :             /* No need to keep the lock */
   16453         116 :             relation_close(seqRel, lockmode);
   16454         116 :             continue;
   16455             :         }
   16456             : 
   16457             :         /* We don't need to close the sequence while we alter it. */
   16458          26 :         ATExecChangeOwner(depForm->objid, newOwnerId, true, lockmode);
   16459             : 
   16460             :         /* Now we can close it.  Keep the lock till end of transaction. */
   16461          26 :         relation_close(seqRel, NoLock);
   16462             :     }
   16463             : 
   16464         534 :     systable_endscan(scan);
   16465             : 
   16466         534 :     relation_close(depRel, AccessShareLock);
   16467         534 : }
   16468             : 
   16469             : /*
   16470             :  * ALTER TABLE CLUSTER ON
   16471             :  *
   16472             :  * The only thing we have to do is to change the indisclustered bits.
   16473             :  *
   16474             :  * Return the address of the new clustering index.
   16475             :  */
   16476             : static ObjectAddress
   16477          64 : ATExecClusterOn(Relation rel, const char *indexName, LOCKMODE lockmode)
   16478             : {
   16479             :     Oid         indexOid;
   16480             :     ObjectAddress address;
   16481             : 
   16482          64 :     indexOid = get_relname_relid(indexName, rel->rd_rel->relnamespace);
   16483             : 
   16484          64 :     if (!OidIsValid(indexOid))
   16485           0 :         ereport(ERROR,
   16486             :                 (errcode(ERRCODE_UNDEFINED_OBJECT),
   16487             :                  errmsg("index \"%s\" for table \"%s\" does not exist",
   16488             :                         indexName, RelationGetRelationName(rel))));
   16489             : 
   16490             :     /* Check index is valid to cluster on */
   16491          64 :     check_index_is_clusterable(rel, indexOid, lockmode);
   16492             : 
   16493             :     /* And do the work */
   16494          64 :     mark_index_clustered(rel, indexOid, false);
   16495             : 
   16496          58 :     ObjectAddressSet(address,
   16497             :                      RelationRelationId, indexOid);
   16498             : 
   16499          58 :     return address;
   16500             : }
   16501             : 
   16502             : /*
   16503             :  * ALTER TABLE SET WITHOUT CLUSTER
   16504             :  *
   16505             :  * We have to find any indexes on the table that have indisclustered bit
   16506             :  * set and turn it off.
   16507             :  */
   16508             : static void
   16509          18 : ATExecDropCluster(Relation rel, LOCKMODE lockmode)
   16510             : {
   16511          18 :     mark_index_clustered(rel, InvalidOid, false);
   16512          12 : }
   16513             : 
   16514             : /*
   16515             :  * Preparation phase for SET ACCESS METHOD
   16516             :  *
   16517             :  * Check that the access method exists and determine whether a change is
   16518             :  * actually needed.
   16519             :  */
   16520             : static void
   16521         110 : ATPrepSetAccessMethod(AlteredTableInfo *tab, Relation rel, const char *amname)
   16522             : {
   16523             :     Oid         amoid;
   16524             : 
   16525             :     /*
   16526             :      * Look up the access method name and check that it differs from the
   16527             :      * table's current AM.  If DEFAULT was specified for a partitioned table
   16528             :      * (amname is NULL), set it to InvalidOid to reset the catalogued AM.
   16529             :      */
   16530         110 :     if (amname != NULL)
   16531          74 :         amoid = get_table_am_oid(amname, false);
   16532          36 :     else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
   16533          18 :         amoid = InvalidOid;
   16534             :     else
   16535          18 :         amoid = get_table_am_oid(default_table_access_method, false);
   16536             : 
   16537             :     /* if it's a match, phase 3 doesn't need to do anything */
   16538         110 :     if (rel->rd_rel->relam == amoid)
   16539          12 :         return;
   16540             : 
   16541             :     /* Save info for Phase 3 to do the real work */
   16542          98 :     tab->rewrite |= AT_REWRITE_ACCESS_METHOD;
   16543          98 :     tab->newAccessMethod = amoid;
   16544          98 :     tab->chgAccessMethod = true;
   16545             : }
   16546             : 
   16547             : /*
   16548             :  * Special handling of ALTER TABLE SET ACCESS METHOD for relations with no
   16549             :  * storage that have an interest in preserving AM.
   16550             :  *
   16551             :  * Since these have no storage, setting the access method is a catalog only
   16552             :  * operation.
   16553             :  */
   16554             : static void
   16555          44 : ATExecSetAccessMethodNoStorage(Relation rel, Oid newAccessMethodId)
   16556             : {
   16557             :     Relation    pg_class;
   16558             :     Oid         oldAccessMethodId;
   16559             :     HeapTuple   tuple;
   16560             :     Form_pg_class rd_rel;
   16561          44 :     Oid         reloid = RelationGetRelid(rel);
   16562             : 
   16563             :     /*
   16564             :      * Shouldn't be called on relations having storage; these are processed in
   16565             :      * phase 3.
   16566             :      */
   16567             :     Assert(!RELKIND_HAS_STORAGE(rel->rd_rel->relkind));
   16568             : 
   16569             :     /* Get a modifiable copy of the relation's pg_class row. */
   16570          44 :     pg_class = table_open(RelationRelationId, RowExclusiveLock);
   16571             : 
   16572          44 :     tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(reloid));
   16573          44 :     if (!HeapTupleIsValid(tuple))
   16574           0 :         elog(ERROR, "cache lookup failed for relation %u", reloid);
   16575          44 :     rd_rel = (Form_pg_class) GETSTRUCT(tuple);
   16576             : 
   16577             :     /* Update the pg_class row. */
   16578          44 :     oldAccessMethodId = rd_rel->relam;
   16579          44 :     rd_rel->relam = newAccessMethodId;
   16580             : 
   16581             :     /* Leave if no update required */
   16582          44 :     if (rd_rel->relam == oldAccessMethodId)
   16583             :     {
   16584           0 :         heap_freetuple(tuple);
   16585           0 :         table_close(pg_class, RowExclusiveLock);
   16586           0 :         return;
   16587             :     }
   16588             : 
   16589          44 :     CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
   16590             : 
   16591             :     /*
   16592             :      * Update the dependency on the new access method.  No dependency is added
   16593             :      * if the new access method is InvalidOid (default case).  Be very careful
   16594             :      * that this has to compare the previous value stored in pg_class with the
   16595             :      * new one.
   16596             :      */
   16597          44 :     if (!OidIsValid(oldAccessMethodId) && OidIsValid(rd_rel->relam))
   16598          20 :     {
   16599             :         ObjectAddress relobj,
   16600             :                     referenced;
   16601             : 
   16602             :         /*
   16603             :          * New access method is defined and there was no dependency
   16604             :          * previously, so record a new one.
   16605             :          */
   16606          20 :         ObjectAddressSet(relobj, RelationRelationId, reloid);
   16607          20 :         ObjectAddressSet(referenced, AccessMethodRelationId, rd_rel->relam);
   16608          20 :         recordDependencyOn(&relobj, &referenced, DEPENDENCY_NORMAL);
   16609             :     }
   16610          24 :     else if (OidIsValid(oldAccessMethodId) &&
   16611          24 :              !OidIsValid(rd_rel->relam))
   16612             :     {
   16613             :         /*
   16614             :          * There was an access method defined, and no new one, so just remove
   16615             :          * the existing dependency.
   16616             :          */
   16617          12 :         deleteDependencyRecordsForClass(RelationRelationId, reloid,
   16618             :                                         AccessMethodRelationId,
   16619             :                                         DEPENDENCY_NORMAL);
   16620             :     }
   16621             :     else
   16622             :     {
   16623             :         Assert(OidIsValid(oldAccessMethodId) &&
   16624             :                OidIsValid(rd_rel->relam));
   16625             : 
   16626             :         /* Both are valid, so update the dependency */
   16627          12 :         changeDependencyFor(RelationRelationId, reloid,
   16628             :                             AccessMethodRelationId,
   16629             :                             oldAccessMethodId, rd_rel->relam);
   16630             :     }
   16631             : 
   16632             :     /* make the relam and dependency changes visible */
   16633          44 :     CommandCounterIncrement();
   16634             : 
   16635          44 :     InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
   16636             : 
   16637          44 :     heap_freetuple(tuple);
   16638          44 :     table_close(pg_class, RowExclusiveLock);
   16639             : }
   16640             : 
   16641             : /*
   16642             :  * ALTER TABLE SET TABLESPACE
   16643             :  */
   16644             : static void
   16645         164 : ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel, const char *tablespacename, LOCKMODE lockmode)
   16646             : {
   16647             :     Oid         tablespaceId;
   16648             : 
   16649             :     /* Check that the tablespace exists */
   16650         164 :     tablespaceId = get_tablespace_oid(tablespacename, false);
   16651             : 
   16652             :     /* Check permissions except when moving to database's default */
   16653         164 :     if (OidIsValid(tablespaceId) && tablespaceId != MyDatabaseTableSpace)
   16654             :     {
   16655             :         AclResult   aclresult;
   16656             : 
   16657          66 :         aclresult = object_aclcheck(TableSpaceRelationId, tablespaceId, GetUserId(), ACL_CREATE);
   16658          66 :         if (aclresult != ACLCHECK_OK)
   16659           0 :             aclcheck_error(aclresult, OBJECT_TABLESPACE, tablespacename);
   16660             :     }
   16661             : 
   16662             :     /* Save info for Phase 3 to do the real work */
   16663         164 :     if (OidIsValid(tab->newTableSpace))
   16664           0 :         ereport(ERROR,
   16665             :                 (errcode(ERRCODE_SYNTAX_ERROR),
   16666             :                  errmsg("cannot have multiple SET TABLESPACE subcommands")));
   16667             : 
   16668         164 :     tab->newTableSpace = tablespaceId;
   16669         164 : }
   16670             : 
   16671             : /*
   16672             :  * Set, reset, or replace reloptions.
   16673             :  */
   16674             : static void
   16675         960 : ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation,
   16676             :                     LOCKMODE lockmode)
   16677             : {
   16678             :     Oid         relid;
   16679             :     Relation    pgclass;
   16680             :     HeapTuple   tuple;
   16681             :     HeapTuple   newtuple;
   16682             :     Datum       datum;
   16683             :     Datum       newOptions;
   16684             :     Datum       repl_val[Natts_pg_class];
   16685             :     bool        repl_null[Natts_pg_class];
   16686             :     bool        repl_repl[Natts_pg_class];
   16687         960 :     const char *const validnsps[] = HEAP_RELOPT_NAMESPACES;
   16688             : 
   16689         960 :     if (defList == NIL && operation != AT_ReplaceRelOptions)
   16690           0 :         return;                 /* nothing to do */
   16691             : 
   16692         960 :     pgclass = table_open(RelationRelationId, RowExclusiveLock);
   16693             : 
   16694             :     /* Fetch heap tuple */
   16695         960 :     relid = RelationGetRelid(rel);
   16696         960 :     tuple = SearchSysCacheLocked1(RELOID, ObjectIdGetDatum(relid));
   16697         960 :     if (!HeapTupleIsValid(tuple))
   16698           0 :         elog(ERROR, "cache lookup failed for relation %u", relid);
   16699             : 
   16700         960 :     if (operation == AT_ReplaceRelOptions)
   16701             :     {
   16702             :         /*
   16703             :          * If we're supposed to replace the reloptions list, we just pretend
   16704             :          * there were none before.
   16705             :          */
   16706         194 :         datum = (Datum) 0;
   16707             :     }
   16708             :     else
   16709             :     {
   16710             :         bool        isnull;
   16711             : 
   16712             :         /* Get the old reloptions */
   16713         766 :         datum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions,
   16714             :                                 &isnull);
   16715         766 :         if (isnull)
   16716         478 :             datum = (Datum) 0;
   16717             :     }
   16718             : 
   16719             :     /* Generate new proposed reloptions (text array) */
   16720         960 :     newOptions = transformRelOptions(datum, defList, NULL, validnsps, false,
   16721             :                                      operation == AT_ResetRelOptions);
   16722             : 
   16723             :     /* Validate */
   16724         954 :     switch (rel->rd_rel->relkind)
   16725             :     {
   16726         536 :         case RELKIND_RELATION:
   16727             :         case RELKIND_MATVIEW:
   16728         536 :             (void) heap_reloptions(rel->rd_rel->relkind, newOptions, true);
   16729         536 :             break;
   16730           6 :         case RELKIND_PARTITIONED_TABLE:
   16731           6 :             (void) partitioned_table_reloptions(newOptions, true);
   16732           0 :             break;
   16733         296 :         case RELKIND_VIEW:
   16734         296 :             (void) view_reloptions(newOptions, true);
   16735         278 :             break;
   16736         116 :         case RELKIND_INDEX:
   16737             :         case RELKIND_PARTITIONED_INDEX:
   16738         116 :             (void) index_reloptions(rel->rd_indam->amoptions, newOptions, true);
   16739          94 :             break;
   16740           0 :         case RELKIND_TOASTVALUE:
   16741             :             /* fall through to error -- shouldn't ever get here */
   16742             :         default:
   16743           0 :             ereport(ERROR,
   16744             :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   16745             :                      errmsg("cannot set options for relation \"%s\"",
   16746             :                             RelationGetRelationName(rel)),
   16747             :                      errdetail_relkind_not_supported(rel->rd_rel->relkind)));
   16748             :             break;
   16749             :     }
   16750             : 
   16751             :     /* Special-case validation of view options */
   16752         908 :     if (rel->rd_rel->relkind == RELKIND_VIEW)
   16753             :     {
   16754         278 :         Query      *view_query = get_view_query(rel);
   16755         278 :         List       *view_options = untransformRelOptions(newOptions);
   16756             :         ListCell   *cell;
   16757         278 :         bool        check_option = false;
   16758             : 
   16759         380 :         foreach(cell, view_options)
   16760             :         {
   16761         102 :             DefElem    *defel = (DefElem *) lfirst(cell);
   16762             : 
   16763         102 :             if (strcmp(defel->defname, "check_option") == 0)
   16764          24 :                 check_option = true;
   16765             :         }
   16766             : 
   16767             :         /*
   16768             :          * If the check option is specified, look to see if the view is
   16769             :          * actually auto-updatable or not.
   16770             :          */
   16771         278 :         if (check_option)
   16772             :         {
   16773             :             const char *view_updatable_error =
   16774          24 :                 view_query_is_auto_updatable(view_query, true);
   16775             : 
   16776          24 :             if (view_updatable_error)
   16777           0 :                 ereport(ERROR,
   16778             :                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   16779             :                          errmsg("WITH CHECK OPTION is supported only on automatically updatable views"),
   16780             :                          errhint("%s", _(view_updatable_error))));
   16781             :         }
   16782             :     }
   16783             : 
   16784             :     /*
   16785             :      * All we need do here is update the pg_class row; the new options will be
   16786             :      * propagated into relcaches during post-commit cache inval.
   16787             :      */
   16788         908 :     memset(repl_val, 0, sizeof(repl_val));
   16789         908 :     memset(repl_null, false, sizeof(repl_null));
   16790         908 :     memset(repl_repl, false, sizeof(repl_repl));
   16791             : 
   16792         908 :     if (newOptions != (Datum) 0)
   16793         614 :         repl_val[Anum_pg_class_reloptions - 1] = newOptions;
   16794             :     else
   16795         294 :         repl_null[Anum_pg_class_reloptions - 1] = true;
   16796             : 
   16797         908 :     repl_repl[Anum_pg_class_reloptions - 1] = true;
   16798             : 
   16799         908 :     newtuple = heap_modify_tuple(tuple, RelationGetDescr(pgclass),
   16800             :                                  repl_val, repl_null, repl_repl);
   16801             : 
   16802         908 :     CatalogTupleUpdate(pgclass, &newtuple->t_self, newtuple);
   16803         908 :     UnlockTuple(pgclass, &tuple->t_self, InplaceUpdateTupleLock);
   16804             : 
   16805         908 :     InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
   16806             : 
   16807         908 :     heap_freetuple(newtuple);
   16808             : 
   16809         908 :     ReleaseSysCache(tuple);
   16810             : 
   16811             :     /* repeat the whole exercise for the toast table, if there's one */
   16812         908 :     if (OidIsValid(rel->rd_rel->reltoastrelid))
   16813             :     {
   16814             :         Relation    toastrel;
   16815         268 :         Oid         toastid = rel->rd_rel->reltoastrelid;
   16816             : 
   16817         268 :         toastrel = table_open(toastid, lockmode);
   16818             : 
   16819             :         /* Fetch heap tuple */
   16820         268 :         tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(toastid));
   16821         268 :         if (!HeapTupleIsValid(tuple))
   16822           0 :             elog(ERROR, "cache lookup failed for relation %u", toastid);
   16823             : 
   16824         268 :         if (operation == AT_ReplaceRelOptions)
   16825             :         {
   16826             :             /*
   16827             :              * If we're supposed to replace the reloptions list, we just
   16828             :              * pretend there were none before.
   16829             :              */
   16830           0 :             datum = (Datum) 0;
   16831             :         }
   16832             :         else
   16833             :         {
   16834             :             bool        isnull;
   16835             : 
   16836             :             /* Get the old reloptions */
   16837         268 :             datum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions,
   16838             :                                     &isnull);
   16839         268 :             if (isnull)
   16840         232 :                 datum = (Datum) 0;
   16841             :         }
   16842             : 
   16843         268 :         newOptions = transformRelOptions(datum, defList, "toast", validnsps,
   16844             :                                          false, operation == AT_ResetRelOptions);
   16845             : 
   16846         268 :         (void) heap_reloptions(RELKIND_TOASTVALUE, newOptions, true);
   16847             : 
   16848         268 :         memset(repl_val, 0, sizeof(repl_val));
   16849         268 :         memset(repl_null, false, sizeof(repl_null));
   16850         268 :         memset(repl_repl, false, sizeof(repl_repl));
   16851             : 
   16852         268 :         if (newOptions != (Datum) 0)
   16853          42 :             repl_val[Anum_pg_class_reloptions - 1] = newOptions;
   16854             :         else
   16855         226 :             repl_null[Anum_pg_class_reloptions - 1] = true;
   16856             : 
   16857         268 :         repl_repl[Anum_pg_class_reloptions - 1] = true;
   16858             : 
   16859         268 :         newtuple = heap_modify_tuple(tuple, RelationGetDescr(pgclass),
   16860             :                                      repl_val, repl_null, repl_repl);
   16861             : 
   16862         268 :         CatalogTupleUpdate(pgclass, &newtuple->t_self, newtuple);
   16863             : 
   16864         268 :         InvokeObjectPostAlterHookArg(RelationRelationId,
   16865             :                                      RelationGetRelid(toastrel), 0,
   16866             :                                      InvalidOid, true);
   16867             : 
   16868         268 :         heap_freetuple(newtuple);
   16869             : 
   16870         268 :         ReleaseSysCache(tuple);
   16871             : 
   16872         268 :         table_close(toastrel, NoLock);
   16873             :     }
   16874             : 
   16875         908 :     table_close(pgclass, RowExclusiveLock);
   16876             : }
   16877             : 
   16878             : /*
   16879             :  * Execute ALTER TABLE SET TABLESPACE for cases where there is no tuple
   16880             :  * rewriting to be done, so we just want to copy the data as fast as possible.
   16881             :  */
   16882             : static void
   16883         168 : ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode)
   16884             : {
   16885             :     Relation    rel;
   16886             :     Oid         reltoastrelid;
   16887             :     RelFileNumber newrelfilenumber;
   16888             :     RelFileLocator newrlocator;
   16889         168 :     List       *reltoastidxids = NIL;
   16890             :     ListCell   *lc;
   16891             : 
   16892             :     /*
   16893             :      * Need lock here in case we are recursing to toast table or index
   16894             :      */
   16895         168 :     rel = relation_open(tableOid, lockmode);
   16896             : 
   16897             :     /* Check first if relation can be moved to new tablespace */
   16898         168 :     if (!CheckRelationTableSpaceMove(rel, newTableSpace))
   16899             :     {
   16900           8 :         InvokeObjectPostAlterHook(RelationRelationId,
   16901             :                                   RelationGetRelid(rel), 0);
   16902           8 :         relation_close(rel, NoLock);
   16903           8 :         return;
   16904             :     }
   16905             : 
   16906         160 :     reltoastrelid = rel->rd_rel->reltoastrelid;
   16907             :     /* Fetch the list of indexes on toast relation if necessary */
   16908         160 :     if (OidIsValid(reltoastrelid))
   16909             :     {
   16910          20 :         Relation    toastRel = relation_open(reltoastrelid, lockmode);
   16911             : 
   16912          20 :         reltoastidxids = RelationGetIndexList(toastRel);
   16913          20 :         relation_close(toastRel, lockmode);
   16914             :     }
   16915             : 
   16916             :     /*
   16917             :      * Relfilenumbers are not unique in databases across tablespaces, so we
   16918             :      * need to allocate a new one in the new tablespace.
   16919             :      */
   16920         160 :     newrelfilenumber = GetNewRelFileNumber(newTableSpace, NULL,
   16921         160 :                                            rel->rd_rel->relpersistence);
   16922             : 
   16923             :     /* Open old and new relation */
   16924         160 :     newrlocator = rel->rd_locator;
   16925         160 :     newrlocator.relNumber = newrelfilenumber;
   16926         160 :     newrlocator.spcOid = newTableSpace;
   16927             : 
   16928             :     /* hand off to AM to actually create new rel storage and copy the data */
   16929         160 :     if (rel->rd_rel->relkind == RELKIND_INDEX)
   16930             :     {
   16931          62 :         index_copy_data(rel, newrlocator);
   16932             :     }
   16933             :     else
   16934             :     {
   16935             :         Assert(RELKIND_HAS_TABLE_AM(rel->rd_rel->relkind));
   16936          98 :         table_relation_copy_data(rel, &newrlocator);
   16937             :     }
   16938             : 
   16939             :     /*
   16940             :      * Update the pg_class row.
   16941             :      *
   16942             :      * NB: This wouldn't work if ATExecSetTableSpace() were allowed to be
   16943             :      * executed on pg_class or its indexes (the above copy wouldn't contain
   16944             :      * the updated pg_class entry), but that's forbidden with
   16945             :      * CheckRelationTableSpaceMove().
   16946             :      */
   16947         160 :     SetRelationTableSpace(rel, newTableSpace, newrelfilenumber);
   16948             : 
   16949         160 :     InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
   16950             : 
   16951         160 :     RelationAssumeNewRelfilelocator(rel);
   16952             : 
   16953         160 :     relation_close(rel, NoLock);
   16954             : 
   16955             :     /* Make sure the reltablespace change is visible */
   16956         160 :     CommandCounterIncrement();
   16957             : 
   16958             :     /* Move associated toast relation and/or indexes, too */
   16959         160 :     if (OidIsValid(reltoastrelid))
   16960          20 :         ATExecSetTableSpace(reltoastrelid, newTableSpace, lockmode);
   16961         180 :     foreach(lc, reltoastidxids)
   16962          20 :         ATExecSetTableSpace(lfirst_oid(lc), newTableSpace, lockmode);
   16963             : 
   16964             :     /* Clean up */
   16965         160 :     list_free(reltoastidxids);
   16966             : }
   16967             : 
   16968             : /*
   16969             :  * Special handling of ALTER TABLE SET TABLESPACE for relations with no
   16970             :  * storage that have an interest in preserving tablespace.
   16971             :  *
   16972             :  * Since these have no storage the tablespace can be updated with a simple
   16973             :  * metadata only operation to update the tablespace.
   16974             :  */
   16975             : static void
   16976          36 : ATExecSetTableSpaceNoStorage(Relation rel, Oid newTableSpace)
   16977             : {
   16978             :     /*
   16979             :      * Shouldn't be called on relations having storage; these are processed in
   16980             :      * phase 3.
   16981             :      */
   16982             :     Assert(!RELKIND_HAS_STORAGE(rel->rd_rel->relkind));
   16983             : 
   16984             :     /* check if relation can be moved to its new tablespace */
   16985          36 :     if (!CheckRelationTableSpaceMove(rel, newTableSpace))
   16986             :     {
   16987           0 :         InvokeObjectPostAlterHook(RelationRelationId,
   16988             :                                   RelationGetRelid(rel),
   16989             :                                   0);
   16990           0 :         return;
   16991             :     }
   16992             : 
   16993             :     /* Update can be done, so change reltablespace */
   16994          30 :     SetRelationTableSpace(rel, newTableSpace, InvalidOid);
   16995             : 
   16996          30 :     InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
   16997             : 
   16998             :     /* Make sure the reltablespace change is visible */
   16999          30 :     CommandCounterIncrement();
   17000             : }
   17001             : 
   17002             : /*
   17003             :  * Alter Table ALL ... SET TABLESPACE
   17004             :  *
   17005             :  * Allows a user to move all objects of some type in a given tablespace in the
   17006             :  * current database to another tablespace.  Objects can be chosen based on the
   17007             :  * owner of the object also, to allow users to move only their objects.
   17008             :  * The user must have CREATE rights on the new tablespace, as usual.   The main
   17009             :  * permissions handling is done by the lower-level table move function.
   17010             :  *
   17011             :  * All to-be-moved objects are locked first. If NOWAIT is specified and the
   17012             :  * lock can't be acquired then we ereport(ERROR).
   17013             :  */
   17014             : Oid
   17015          30 : AlterTableMoveAll(AlterTableMoveAllStmt *stmt)
   17016             : {
   17017          30 :     List       *relations = NIL;
   17018             :     ListCell   *l;
   17019             :     ScanKeyData key[1];
   17020             :     Relation    rel;
   17021             :     TableScanDesc scan;
   17022             :     HeapTuple   tuple;
   17023             :     Oid         orig_tablespaceoid;
   17024             :     Oid         new_tablespaceoid;
   17025          30 :     List       *role_oids = roleSpecsToIds(stmt->roles);
   17026             : 
   17027             :     /* Ensure we were not asked to move something we can't */
   17028          30 :     if (stmt->objtype != OBJECT_TABLE && stmt->objtype != OBJECT_INDEX &&
   17029          12 :         stmt->objtype != OBJECT_MATVIEW)
   17030           0 :         ereport(ERROR,
   17031             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
   17032             :                  errmsg("only tables, indexes, and materialized views exist in tablespaces")));
   17033             : 
   17034             :     /* Get the orig and new tablespace OIDs */
   17035          30 :     orig_tablespaceoid = get_tablespace_oid(stmt->orig_tablespacename, false);
   17036          30 :     new_tablespaceoid = get_tablespace_oid(stmt->new_tablespacename, false);
   17037             : 
   17038             :     /* Can't move shared relations in to or out of pg_global */
   17039             :     /* This is also checked by ATExecSetTableSpace, but nice to stop earlier */
   17040          30 :     if (orig_tablespaceoid == GLOBALTABLESPACE_OID ||
   17041             :         new_tablespaceoid == GLOBALTABLESPACE_OID)
   17042           0 :         ereport(ERROR,
   17043             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
   17044             :                  errmsg("cannot move relations in to or out of pg_global tablespace")));
   17045             : 
   17046             :     /*
   17047             :      * Must have CREATE rights on the new tablespace, unless it is the
   17048             :      * database default tablespace (which all users implicitly have CREATE
   17049             :      * rights on).
   17050             :      */
   17051          30 :     if (OidIsValid(new_tablespaceoid) && new_tablespaceoid != MyDatabaseTableSpace)
   17052             :     {
   17053             :         AclResult   aclresult;
   17054             : 
   17055           0 :         aclresult = object_aclcheck(TableSpaceRelationId, new_tablespaceoid, GetUserId(),
   17056             :                                     ACL_CREATE);
   17057           0 :         if (aclresult != ACLCHECK_OK)
   17058           0 :             aclcheck_error(aclresult, OBJECT_TABLESPACE,
   17059           0 :                            get_tablespace_name(new_tablespaceoid));
   17060             :     }
   17061             : 
   17062             :     /*
   17063             :      * Now that the checks are done, check if we should set either to
   17064             :      * InvalidOid because it is our database's default tablespace.
   17065             :      */
   17066          30 :     if (orig_tablespaceoid == MyDatabaseTableSpace)
   17067           0 :         orig_tablespaceoid = InvalidOid;
   17068             : 
   17069          30 :     if (new_tablespaceoid == MyDatabaseTableSpace)
   17070          30 :         new_tablespaceoid = InvalidOid;
   17071             : 
   17072             :     /* no-op */
   17073          30 :     if (orig_tablespaceoid == new_tablespaceoid)
   17074           0 :         return new_tablespaceoid;
   17075             : 
   17076             :     /*
   17077             :      * Walk the list of objects in the tablespace and move them. This will
   17078             :      * only find objects in our database, of course.
   17079             :      */
   17080          30 :     ScanKeyInit(&key[0],
   17081             :                 Anum_pg_class_reltablespace,
   17082             :                 BTEqualStrategyNumber, F_OIDEQ,
   17083             :                 ObjectIdGetDatum(orig_tablespaceoid));
   17084             : 
   17085          30 :     rel = table_open(RelationRelationId, AccessShareLock);
   17086          30 :     scan = table_beginscan_catalog(rel, 1, key);
   17087         132 :     while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
   17088             :     {
   17089         102 :         Form_pg_class relForm = (Form_pg_class) GETSTRUCT(tuple);
   17090         102 :         Oid         relOid = relForm->oid;
   17091             : 
   17092             :         /*
   17093             :          * Do not move objects in pg_catalog as part of this, if an admin
   17094             :          * really wishes to do so, they can issue the individual ALTER
   17095             :          * commands directly.
   17096             :          *
   17097             :          * Also, explicitly avoid any shared tables, temp tables, or TOAST
   17098             :          * (TOAST will be moved with the main table).
   17099             :          */
   17100         102 :         if (IsCatalogNamespace(relForm->relnamespace) ||
   17101         204 :             relForm->relisshared ||
   17102         204 :             isAnyTempNamespace(relForm->relnamespace) ||
   17103         102 :             IsToastNamespace(relForm->relnamespace))
   17104           0 :             continue;
   17105             : 
   17106             :         /* Only move the object type requested */
   17107         102 :         if ((stmt->objtype == OBJECT_TABLE &&
   17108          60 :              relForm->relkind != RELKIND_RELATION &&
   17109          36 :              relForm->relkind != RELKIND_PARTITIONED_TABLE) ||
   17110          66 :             (stmt->objtype == OBJECT_INDEX &&
   17111          36 :              relForm->relkind != RELKIND_INDEX &&
   17112           6 :              relForm->relkind != RELKIND_PARTITIONED_INDEX) ||
   17113          60 :             (stmt->objtype == OBJECT_MATVIEW &&
   17114           6 :              relForm->relkind != RELKIND_MATVIEW))
   17115          42 :             continue;
   17116             : 
   17117             :         /* Check if we are only moving objects owned by certain roles */
   17118          60 :         if (role_oids != NIL && !list_member_oid(role_oids, relForm->relowner))
   17119           0 :             continue;
   17120             : 
   17121             :         /*
   17122             :          * Handle permissions-checking here since we are locking the tables
   17123             :          * and also to avoid doing a bunch of work only to fail part-way. Note
   17124             :          * that permissions will also be checked by AlterTableInternal().
   17125             :          *
   17126             :          * Caller must be considered an owner on the table to move it.
   17127             :          */
   17128          60 :         if (!object_ownercheck(RelationRelationId, relOid, GetUserId()))
   17129           0 :             aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relOid)),
   17130           0 :                            NameStr(relForm->relname));
   17131             : 
   17132          60 :         if (stmt->nowait &&
   17133           0 :             !ConditionalLockRelationOid(relOid, AccessExclusiveLock))
   17134           0 :             ereport(ERROR,
   17135             :                     (errcode(ERRCODE_OBJECT_IN_USE),
   17136             :                      errmsg("aborting because lock on relation \"%s.%s\" is not available",
   17137             :                             get_namespace_name(relForm->relnamespace),
   17138             :                             NameStr(relForm->relname))));
   17139             :         else
   17140          60 :             LockRelationOid(relOid, AccessExclusiveLock);
   17141             : 
   17142             :         /* Add to our list of objects to move */
   17143          60 :         relations = lappend_oid(relations, relOid);
   17144             :     }
   17145             : 
   17146          30 :     table_endscan(scan);
   17147          30 :     table_close(rel, AccessShareLock);
   17148             : 
   17149          30 :     if (relations == NIL)
   17150          12 :         ereport(NOTICE,
   17151             :                 (errcode(ERRCODE_NO_DATA_FOUND),
   17152             :                  errmsg("no matching relations in tablespace \"%s\" found",
   17153             :                         orig_tablespaceoid == InvalidOid ? "(database default)" :
   17154             :                         get_tablespace_name(orig_tablespaceoid))));
   17155             : 
   17156             :     /* Everything is locked, loop through and move all of the relations. */
   17157          90 :     foreach(l, relations)
   17158             :     {
   17159          60 :         List       *cmds = NIL;
   17160          60 :         AlterTableCmd *cmd = makeNode(AlterTableCmd);
   17161             : 
   17162          60 :         cmd->subtype = AT_SetTableSpace;
   17163          60 :         cmd->name = stmt->new_tablespacename;
   17164             : 
   17165          60 :         cmds = lappend(cmds, cmd);
   17166             : 
   17167          60 :         EventTriggerAlterTableStart((Node *) stmt);
   17168             :         /* OID is set by AlterTableInternal */
   17169          60 :         AlterTableInternal(lfirst_oid(l), cmds, false);
   17170          60 :         EventTriggerAlterTableEnd();
   17171             :     }
   17172             : 
   17173          30 :     return new_tablespaceoid;
   17174             : }
   17175             : 
   17176             : static void
   17177          62 : index_copy_data(Relation rel, RelFileLocator newrlocator)
   17178             : {
   17179             :     SMgrRelation dstrel;
   17180             : 
   17181             :     /*
   17182             :      * Since we copy the file directly without looking at the shared buffers,
   17183             :      * we'd better first flush out any pages of the source relation that are
   17184             :      * in shared buffers.  We assume no new changes will be made while we are
   17185             :      * holding exclusive lock on the rel.
   17186             :      */
   17187          62 :     FlushRelationBuffers(rel);
   17188             : 
   17189             :     /*
   17190             :      * Create and copy all forks of the relation, and schedule unlinking of
   17191             :      * old physical files.
   17192             :      *
   17193             :      * NOTE: any conflict in relfilenumber value will be caught in
   17194             :      * RelationCreateStorage().
   17195             :      */
   17196          62 :     dstrel = RelationCreateStorage(newrlocator, rel->rd_rel->relpersistence, true);
   17197             : 
   17198             :     /* copy main fork */
   17199          62 :     RelationCopyStorage(RelationGetSmgr(rel), dstrel, MAIN_FORKNUM,
   17200          62 :                         rel->rd_rel->relpersistence);
   17201             : 
   17202             :     /* copy those extra forks that exist */
   17203          62 :     for (ForkNumber forkNum = MAIN_FORKNUM + 1;
   17204         248 :          forkNum <= MAX_FORKNUM; forkNum++)
   17205             :     {
   17206         186 :         if (smgrexists(RelationGetSmgr(rel), forkNum))
   17207             :         {
   17208           0 :             smgrcreate(dstrel, forkNum, false);
   17209             : 
   17210             :             /*
   17211             :              * WAL log creation if the relation is persistent, or this is the
   17212             :              * init fork of an unlogged relation.
   17213             :              */
   17214           0 :             if (RelationIsPermanent(rel) ||
   17215           0 :                 (rel->rd_rel->relpersistence == RELPERSISTENCE_UNLOGGED &&
   17216             :                  forkNum == INIT_FORKNUM))
   17217           0 :                 log_smgrcreate(&newrlocator, forkNum);
   17218           0 :             RelationCopyStorage(RelationGetSmgr(rel), dstrel, forkNum,
   17219           0 :                                 rel->rd_rel->relpersistence);
   17220             :         }
   17221             :     }
   17222             : 
   17223             :     /* drop old relation, and close new one */
   17224          62 :     RelationDropStorage(rel);
   17225          62 :     smgrclose(dstrel);
   17226          62 : }
   17227             : 
   17228             : /*
   17229             :  * ALTER TABLE ENABLE/DISABLE TRIGGER
   17230             :  *
   17231             :  * We just pass this off to trigger.c.
   17232             :  */
   17233             : static void
   17234         342 : ATExecEnableDisableTrigger(Relation rel, const char *trigname,
   17235             :                            char fires_when, bool skip_system, bool recurse,
   17236             :                            LOCKMODE lockmode)
   17237             : {
   17238         342 :     EnableDisableTrigger(rel, trigname, InvalidOid,
   17239             :                          fires_when, skip_system, recurse,
   17240             :                          lockmode);
   17241             : 
   17242         342 :     InvokeObjectPostAlterHook(RelationRelationId,
   17243             :                               RelationGetRelid(rel), 0);
   17244         342 : }
   17245             : 
   17246             : /*
   17247             :  * ALTER TABLE ENABLE/DISABLE RULE
   17248             :  *
   17249             :  * We just pass this off to rewriteDefine.c.
   17250             :  */
   17251             : static void
   17252          46 : ATExecEnableDisableRule(Relation rel, const char *rulename,
   17253             :                         char fires_when, LOCKMODE lockmode)
   17254             : {
   17255          46 :     EnableDisableRule(rel, rulename, fires_when);
   17256             : 
   17257          46 :     InvokeObjectPostAlterHook(RelationRelationId,
   17258             :                               RelationGetRelid(rel), 0);
   17259          46 : }
   17260             : 
   17261             : /*
   17262             :  * ALTER TABLE INHERIT
   17263             :  *
   17264             :  * Add a parent to the child's parents. This verifies that all the columns and
   17265             :  * check constraints of the parent appear in the child and that they have the
   17266             :  * same data types and expressions.
   17267             :  */
   17268             : static void
   17269         464 : ATPrepAddInherit(Relation child_rel)
   17270             : {
   17271         464 :     if (child_rel->rd_rel->reloftype)
   17272           6 :         ereport(ERROR,
   17273             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   17274             :                  errmsg("cannot change inheritance of typed table")));
   17275             : 
   17276         458 :     if (child_rel->rd_rel->relispartition)
   17277           6 :         ereport(ERROR,
   17278             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   17279             :                  errmsg("cannot change inheritance of a partition")));
   17280             : 
   17281         452 :     if (child_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
   17282           6 :         ereport(ERROR,
   17283             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   17284             :                  errmsg("cannot change inheritance of partitioned table")));
   17285         446 : }
   17286             : 
   17287             : /*
   17288             :  * Return the address of the new parent relation.
   17289             :  */
   17290             : static ObjectAddress
   17291         446 : ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode)
   17292             : {
   17293             :     Relation    parent_rel;
   17294             :     List       *children;
   17295             :     ObjectAddress address;
   17296             :     const char *trigger_name;
   17297             : 
   17298             :     /*
   17299             :      * A self-exclusive lock is needed here.  See the similar case in
   17300             :      * MergeAttributes() for a full explanation.
   17301             :      */
   17302         446 :     parent_rel = table_openrv(parent, ShareUpdateExclusiveLock);
   17303             : 
   17304             :     /*
   17305             :      * Must be owner of both parent and child -- child was checked by
   17306             :      * ATSimplePermissions call in ATPrepCmd
   17307             :      */
   17308         446 :     ATSimplePermissions(AT_AddInherit, parent_rel,
   17309             :                         ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
   17310             : 
   17311             :     /* Permanent rels cannot inherit from temporary ones */
   17312         446 :     if (parent_rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
   17313           6 :         child_rel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
   17314           0 :         ereport(ERROR,
   17315             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   17316             :                  errmsg("cannot inherit from temporary relation \"%s\"",
   17317             :                         RelationGetRelationName(parent_rel))));
   17318             : 
   17319             :     /* If parent rel is temp, it must belong to this session */
   17320         446 :     if (RELATION_IS_OTHER_TEMP(parent_rel))
   17321           0 :         ereport(ERROR,
   17322             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   17323             :                  errmsg("cannot inherit from temporary relation of another session")));
   17324             : 
   17325             :     /* Ditto for the child */
   17326         446 :     if (RELATION_IS_OTHER_TEMP(child_rel))
   17327           0 :         ereport(ERROR,
   17328             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   17329             :                  errmsg("cannot inherit to temporary relation of another session")));
   17330             : 
   17331             :     /* Prevent partitioned tables from becoming inheritance parents */
   17332         446 :     if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
   17333           6 :         ereport(ERROR,
   17334             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   17335             :                  errmsg("cannot inherit from partitioned table \"%s\"",
   17336             :                         parent->relname)));
   17337             : 
   17338             :     /* Likewise for partitions */
   17339         440 :     if (parent_rel->rd_rel->relispartition)
   17340           6 :         ereport(ERROR,
   17341             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   17342             :                  errmsg("cannot inherit from a partition")));
   17343             : 
   17344             :     /*
   17345             :      * Prevent circularity by seeing if proposed parent inherits from child.
   17346             :      * (In particular, this disallows making a rel inherit from itself.)
   17347             :      *
   17348             :      * This is not completely bulletproof because of race conditions: in
   17349             :      * multi-level inheritance trees, someone else could concurrently be
   17350             :      * making another inheritance link that closes the loop but does not join
   17351             :      * either of the rels we have locked.  Preventing that seems to require
   17352             :      * exclusive locks on the entire inheritance tree, which is a cure worse
   17353             :      * than the disease.  find_all_inheritors() will cope with circularity
   17354             :      * anyway, so don't sweat it too much.
   17355             :      *
   17356             :      * We use weakest lock we can on child's children, namely AccessShareLock.
   17357             :      */
   17358         434 :     children = find_all_inheritors(RelationGetRelid(child_rel),
   17359             :                                    AccessShareLock, NULL);
   17360             : 
   17361         434 :     if (list_member_oid(children, RelationGetRelid(parent_rel)))
   17362          12 :         ereport(ERROR,
   17363             :                 (errcode(ERRCODE_DUPLICATE_TABLE),
   17364             :                  errmsg("circular inheritance not allowed"),
   17365             :                  errdetail("\"%s\" is already a child of \"%s\".",
   17366             :                            parent->relname,
   17367             :                            RelationGetRelationName(child_rel))));
   17368             : 
   17369             :     /*
   17370             :      * If child_rel has row-level triggers with transition tables, we
   17371             :      * currently don't allow it to become an inheritance child.  See also
   17372             :      * prohibitions in ATExecAttachPartition() and CreateTrigger().
   17373             :      */
   17374         422 :     trigger_name = FindTriggerIncompatibleWithInheritance(child_rel->trigdesc);
   17375         422 :     if (trigger_name != NULL)
   17376           6 :         ereport(ERROR,
   17377             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   17378             :                  errmsg("trigger \"%s\" prevents table \"%s\" from becoming an inheritance child",
   17379             :                         trigger_name, RelationGetRelationName(child_rel)),
   17380             :                  errdetail("ROW triggers with transition tables are not supported in inheritance hierarchies.")));
   17381             : 
   17382             :     /* OK to create inheritance */
   17383         416 :     CreateInheritance(child_rel, parent_rel, false);
   17384             : 
   17385         326 :     ObjectAddressSet(address, RelationRelationId,
   17386             :                      RelationGetRelid(parent_rel));
   17387             : 
   17388             :     /* keep our lock on the parent relation until commit */
   17389         326 :     table_close(parent_rel, NoLock);
   17390             : 
   17391         326 :     return address;
   17392             : }
   17393             : 
   17394             : /*
   17395             :  * CreateInheritance
   17396             :  *      Catalog manipulation portion of creating inheritance between a child
   17397             :  *      table and a parent table.
   17398             :  *
   17399             :  * Common to ATExecAddInherit() and ATExecAttachPartition().
   17400             :  */
   17401             : static void
   17402        3432 : CreateInheritance(Relation child_rel, Relation parent_rel, bool ispartition)
   17403             : {
   17404             :     Relation    catalogRelation;
   17405             :     SysScanDesc scan;
   17406             :     ScanKeyData key;
   17407             :     HeapTuple   inheritsTuple;
   17408             :     int32       inhseqno;
   17409             : 
   17410             :     /* Note: get RowExclusiveLock because we will write pg_inherits below. */
   17411        3432 :     catalogRelation = table_open(InheritsRelationId, RowExclusiveLock);
   17412             : 
   17413             :     /*
   17414             :      * Check for duplicates in the list of parents, and determine the highest
   17415             :      * inhseqno already present; we'll use the next one for the new parent.
   17416             :      * Also, if proposed child is a partition, it cannot already be
   17417             :      * inheriting.
   17418             :      *
   17419             :      * Note: we do not reject the case where the child already inherits from
   17420             :      * the parent indirectly; CREATE TABLE doesn't reject comparable cases.
   17421             :      */
   17422        3432 :     ScanKeyInit(&key,
   17423             :                 Anum_pg_inherits_inhrelid,
   17424             :                 BTEqualStrategyNumber, F_OIDEQ,
   17425             :                 ObjectIdGetDatum(RelationGetRelid(child_rel)));
   17426        3432 :     scan = systable_beginscan(catalogRelation, InheritsRelidSeqnoIndexId,
   17427             :                               true, NULL, 1, &key);
   17428             : 
   17429             :     /* inhseqno sequences start at 1 */
   17430        3432 :     inhseqno = 0;
   17431        3502 :     while (HeapTupleIsValid(inheritsTuple = systable_getnext(scan)))
   17432             :     {
   17433          76 :         Form_pg_inherits inh = (Form_pg_inherits) GETSTRUCT(inheritsTuple);
   17434             : 
   17435          76 :         if (inh->inhparent == RelationGetRelid(parent_rel))
   17436           6 :             ereport(ERROR,
   17437             :                     (errcode(ERRCODE_DUPLICATE_TABLE),
   17438             :                      errmsg("relation \"%s\" would be inherited from more than once",
   17439             :                             RelationGetRelationName(parent_rel))));
   17440             : 
   17441          70 :         if (inh->inhseqno > inhseqno)
   17442          70 :             inhseqno = inh->inhseqno;
   17443             :     }
   17444        3426 :     systable_endscan(scan);
   17445             : 
   17446             :     /* Match up the columns and bump attinhcount as needed */
   17447        3426 :     MergeAttributesIntoExisting(child_rel, parent_rel, ispartition);
   17448             : 
   17449             :     /* Match up the constraints and bump coninhcount as needed */
   17450        3294 :     MergeConstraintsIntoExisting(child_rel, parent_rel);
   17451             : 
   17452             :     /*
   17453             :      * OK, it looks valid.  Make the catalog entries that show inheritance.
   17454             :      */
   17455        3234 :     StoreCatalogInheritance1(RelationGetRelid(child_rel),
   17456             :                              RelationGetRelid(parent_rel),
   17457             :                              inhseqno + 1,
   17458             :                              catalogRelation,
   17459        3234 :                              parent_rel->rd_rel->relkind ==
   17460             :                              RELKIND_PARTITIONED_TABLE);
   17461             : 
   17462             :     /* Now we're done with pg_inherits */
   17463        3234 :     table_close(catalogRelation, RowExclusiveLock);
   17464        3234 : }
   17465             : 
   17466             : /*
   17467             :  * Obtain the source-text form of the constraint expression for a check
   17468             :  * constraint, given its pg_constraint tuple
   17469             :  */
   17470             : static char *
   17471         388 : decompile_conbin(HeapTuple contup, TupleDesc tupdesc)
   17472             : {
   17473             :     Form_pg_constraint con;
   17474             :     bool        isnull;
   17475             :     Datum       attr;
   17476             :     Datum       expr;
   17477             : 
   17478         388 :     con = (Form_pg_constraint) GETSTRUCT(contup);
   17479         388 :     attr = heap_getattr(contup, Anum_pg_constraint_conbin, tupdesc, &isnull);
   17480         388 :     if (isnull)
   17481           0 :         elog(ERROR, "null conbin for constraint %u", con->oid);
   17482             : 
   17483         388 :     expr = DirectFunctionCall2(pg_get_expr, attr,
   17484             :                                ObjectIdGetDatum(con->conrelid));
   17485         388 :     return TextDatumGetCString(expr);
   17486             : }
   17487             : 
   17488             : /*
   17489             :  * Determine whether two check constraints are functionally equivalent
   17490             :  *
   17491             :  * The test we apply is to see whether they reverse-compile to the same
   17492             :  * source string.  This insulates us from issues like whether attributes
   17493             :  * have the same physical column numbers in parent and child relations.
   17494             :  *
   17495             :  * Note that we ignore enforceability as there are cases where constraints
   17496             :  * with differing enforceability are allowed.
   17497             :  */
   17498             : static bool
   17499         194 : constraints_equivalent(HeapTuple a, HeapTuple b, TupleDesc tupleDesc)
   17500             : {
   17501         194 :     Form_pg_constraint acon = (Form_pg_constraint) GETSTRUCT(a);
   17502         194 :     Form_pg_constraint bcon = (Form_pg_constraint) GETSTRUCT(b);
   17503             : 
   17504         194 :     if (acon->condeferrable != bcon->condeferrable ||
   17505         194 :         acon->condeferred != bcon->condeferred ||
   17506         194 :         strcmp(decompile_conbin(a, tupleDesc),
   17507         194 :                decompile_conbin(b, tupleDesc)) != 0)
   17508           6 :         return false;
   17509             :     else
   17510         188 :         return true;
   17511             : }
   17512             : 
   17513             : /*
   17514             :  * Check columns in child table match up with columns in parent, and increment
   17515             :  * their attinhcount.
   17516             :  *
   17517             :  * Called by CreateInheritance
   17518             :  *
   17519             :  * Currently all parent columns must be found in child. Missing columns are an
   17520             :  * error.  One day we might consider creating new columns like CREATE TABLE
   17521             :  * does.  However, that is widely unpopular --- in the common use case of
   17522             :  * partitioned tables it's a foot-gun.
   17523             :  *
   17524             :  * The data type must match exactly. If the parent column is NOT NULL then
   17525             :  * the child must be as well. Defaults are not compared, however.
   17526             :  */
   17527             : static void
   17528        3426 : MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel, bool ispartition)
   17529             : {
   17530             :     Relation    attrrel;
   17531             :     TupleDesc   parent_desc;
   17532             : 
   17533        3426 :     attrrel = table_open(AttributeRelationId, RowExclusiveLock);
   17534        3426 :     parent_desc = RelationGetDescr(parent_rel);
   17535             : 
   17536       11254 :     for (AttrNumber parent_attno = 1; parent_attno <= parent_desc->natts; parent_attno++)
   17537             :     {
   17538        7960 :         Form_pg_attribute parent_att = TupleDescAttr(parent_desc, parent_attno - 1);
   17539        7960 :         char       *parent_attname = NameStr(parent_att->attname);
   17540             :         HeapTuple   tuple;
   17541             : 
   17542             :         /* Ignore dropped columns in the parent. */
   17543        7960 :         if (parent_att->attisdropped)
   17544         296 :             continue;
   17545             : 
   17546             :         /* Find same column in child (matching on column name). */
   17547        7664 :         tuple = SearchSysCacheCopyAttName(RelationGetRelid(child_rel), parent_attname);
   17548        7664 :         if (HeapTupleIsValid(tuple))
   17549             :         {
   17550        7652 :             Form_pg_attribute child_att = (Form_pg_attribute) GETSTRUCT(tuple);
   17551             : 
   17552        7652 :             if (parent_att->atttypid != child_att->atttypid ||
   17553        7646 :                 parent_att->atttypmod != child_att->atttypmod)
   17554          12 :                 ereport(ERROR,
   17555             :                         (errcode(ERRCODE_DATATYPE_MISMATCH),
   17556             :                          errmsg("child table \"%s\" has different type for column \"%s\"",
   17557             :                                 RelationGetRelationName(child_rel), parent_attname)));
   17558             : 
   17559        7640 :             if (parent_att->attcollation != child_att->attcollation)
   17560           6 :                 ereport(ERROR,
   17561             :                         (errcode(ERRCODE_COLLATION_MISMATCH),
   17562             :                          errmsg("child table \"%s\" has different collation for column \"%s\"",
   17563             :                                 RelationGetRelationName(child_rel), parent_attname)));
   17564             : 
   17565             :             /*
   17566             :              * If the parent has a not-null constraint that's not NO INHERIT,
   17567             :              * make sure the child has one too.
   17568             :              *
   17569             :              * Other constraints are checked elsewhere.
   17570             :              */
   17571        7634 :             if (parent_att->attnotnull && !child_att->attnotnull)
   17572             :             {
   17573             :                 HeapTuple   contup;
   17574             : 
   17575          48 :                 contup = findNotNullConstraintAttnum(RelationGetRelid(parent_rel),
   17576          48 :                                                      parent_att->attnum);
   17577          48 :                 if (HeapTupleIsValid(contup) &&
   17578          48 :                     !((Form_pg_constraint) GETSTRUCT(contup))->connoinherit)
   17579          30 :                     ereport(ERROR,
   17580             :                             errcode(ERRCODE_DATATYPE_MISMATCH),
   17581             :                             errmsg("column \"%s\" in child table \"%s\" must be marked NOT NULL",
   17582             :                                    parent_attname, RelationGetRelationName(child_rel)));
   17583             :             }
   17584             : 
   17585             :             /*
   17586             :              * Child column must be generated if and only if parent column is.
   17587             :              */
   17588        7604 :             if (parent_att->attgenerated && !child_att->attgenerated)
   17589          36 :                 ereport(ERROR,
   17590             :                         (errcode(ERRCODE_DATATYPE_MISMATCH),
   17591             :                          errmsg("column \"%s\" in child table must be a generated column", parent_attname)));
   17592        7568 :             if (child_att->attgenerated && !parent_att->attgenerated)
   17593          24 :                 ereport(ERROR,
   17594             :                         (errcode(ERRCODE_DATATYPE_MISMATCH),
   17595             :                          errmsg("column \"%s\" in child table must not be a generated column", parent_attname)));
   17596             : 
   17597        7544 :             if (parent_att->attgenerated && child_att->attgenerated && child_att->attgenerated != parent_att->attgenerated)
   17598          12 :                 ereport(ERROR,
   17599             :                         (errcode(ERRCODE_DATATYPE_MISMATCH),
   17600             :                          errmsg("column \"%s\" inherits from generated column of different kind", parent_attname),
   17601             :                          errdetail("Parent column is %s, child column is %s.",
   17602             :                                    parent_att->attgenerated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL",
   17603             :                                    child_att->attgenerated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL")));
   17604             : 
   17605             :             /*
   17606             :              * Regular inheritance children are independent enough not to
   17607             :              * inherit identity columns.  But partitions are integral part of
   17608             :              * a partitioned table and inherit identity column.
   17609             :              */
   17610        7532 :             if (ispartition)
   17611        6804 :                 child_att->attidentity = parent_att->attidentity;
   17612             : 
   17613             :             /*
   17614             :              * OK, bump the child column's inheritance count.  (If we fail
   17615             :              * later on, this change will just roll back.)
   17616             :              */
   17617        7532 :             if (pg_add_s16_overflow(child_att->attinhcount, 1,
   17618             :                                     &child_att->attinhcount))
   17619           0 :                 ereport(ERROR,
   17620             :                         errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
   17621             :                         errmsg("too many inheritance parents"));
   17622             : 
   17623             :             /*
   17624             :              * In case of partitions, we must enforce that value of attislocal
   17625             :              * is same in all partitions. (Note: there are only inherited
   17626             :              * attributes in partitions)
   17627             :              */
   17628        7532 :             if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
   17629             :             {
   17630             :                 Assert(child_att->attinhcount == 1);
   17631        6804 :                 child_att->attislocal = false;
   17632             :             }
   17633             : 
   17634        7532 :             CatalogTupleUpdate(attrrel, &tuple->t_self, tuple);
   17635        7532 :             heap_freetuple(tuple);
   17636             :         }
   17637             :         else
   17638             :         {
   17639          12 :             ereport(ERROR,
   17640             :                     (errcode(ERRCODE_DATATYPE_MISMATCH),
   17641             :                      errmsg("child table is missing column \"%s\"", parent_attname)));
   17642             :         }
   17643             :     }
   17644             : 
   17645        3294 :     table_close(attrrel, RowExclusiveLock);
   17646        3294 : }
   17647             : 
   17648             : /*
   17649             :  * Check constraints in child table match up with constraints in parent,
   17650             :  * and increment their coninhcount.
   17651             :  *
   17652             :  * Constraints that are marked ONLY in the parent are ignored.
   17653             :  *
   17654             :  * Called by CreateInheritance
   17655             :  *
   17656             :  * Currently all constraints in parent must be present in the child. One day we
   17657             :  * may consider adding new constraints like CREATE TABLE does.
   17658             :  *
   17659             :  * XXX This is O(N^2) which may be an issue with tables with hundreds of
   17660             :  * constraints. As long as tables have more like 10 constraints it shouldn't be
   17661             :  * a problem though. Even 100 constraints ought not be the end of the world.
   17662             :  *
   17663             :  * XXX See MergeWithExistingConstraint too if you change this code.
   17664             :  */
   17665             : static void
   17666        3294 : MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel)
   17667             : {
   17668             :     Relation    constraintrel;
   17669             :     SysScanDesc parent_scan;
   17670             :     ScanKeyData parent_key;
   17671             :     HeapTuple   parent_tuple;
   17672        3294 :     Oid         parent_relid = RelationGetRelid(parent_rel);
   17673             :     AttrMap    *attmap;
   17674             : 
   17675        3294 :     constraintrel = table_open(ConstraintRelationId, RowExclusiveLock);
   17676             : 
   17677             :     /* Outer loop scans through the parent's constraint definitions */
   17678        3294 :     ScanKeyInit(&parent_key,
   17679             :                 Anum_pg_constraint_conrelid,
   17680             :                 BTEqualStrategyNumber, F_OIDEQ,
   17681             :                 ObjectIdGetDatum(parent_relid));
   17682        3294 :     parent_scan = systable_beginscan(constraintrel, ConstraintRelidTypidNameIndexId,
   17683             :                                      true, NULL, 1, &parent_key);
   17684             : 
   17685        3294 :     attmap = build_attrmap_by_name(RelationGetDescr(parent_rel),
   17686             :                                    RelationGetDescr(child_rel),
   17687             :                                    true);
   17688             : 
   17689        5746 :     while (HeapTupleIsValid(parent_tuple = systable_getnext(parent_scan)))
   17690             :     {
   17691        2512 :         Form_pg_constraint parent_con = (Form_pg_constraint) GETSTRUCT(parent_tuple);
   17692             :         SysScanDesc child_scan;
   17693             :         ScanKeyData child_key;
   17694             :         HeapTuple   child_tuple;
   17695             :         AttrNumber  parent_attno;
   17696        2512 :         bool        found = false;
   17697             : 
   17698        2512 :         if (parent_con->contype != CONSTRAINT_CHECK &&
   17699        2274 :             parent_con->contype != CONSTRAINT_NOTNULL)
   17700        1162 :             continue;
   17701             : 
   17702             :         /* if the parent's constraint is marked NO INHERIT, it's not inherited */
   17703        1394 :         if (parent_con->connoinherit)
   17704          44 :             continue;
   17705             : 
   17706        1350 :         if (parent_con->contype == CONSTRAINT_NOTNULL)
   17707        1132 :             parent_attno = extractNotNullColumn(parent_tuple);
   17708             :         else
   17709         218 :             parent_attno = InvalidAttrNumber;
   17710             : 
   17711             :         /* Search for a child constraint matching this one */
   17712        1350 :         ScanKeyInit(&child_key,
   17713             :                     Anum_pg_constraint_conrelid,
   17714             :                     BTEqualStrategyNumber, F_OIDEQ,
   17715             :                     ObjectIdGetDatum(RelationGetRelid(child_rel)));
   17716        1350 :         child_scan = systable_beginscan(constraintrel, ConstraintRelidTypidNameIndexId,
   17717             :                                         true, NULL, 1, &child_key);
   17718             : 
   17719        2138 :         while (HeapTupleIsValid(child_tuple = systable_getnext(child_scan)))
   17720             :         {
   17721        2114 :             Form_pg_constraint child_con = (Form_pg_constraint) GETSTRUCT(child_tuple);
   17722             :             HeapTuple   child_copy;
   17723             : 
   17724        2114 :             if (child_con->contype != parent_con->contype)
   17725         412 :                 continue;
   17726             : 
   17727             :             /*
   17728             :              * CHECK constraint are matched by constraint name, NOT NULL ones
   17729             :              * by attribute number.
   17730             :              */
   17731        1702 :             if (child_con->contype == CONSTRAINT_CHECK)
   17732             :             {
   17733         374 :                 if (strcmp(NameStr(parent_con->conname),
   17734         284 :                            NameStr(child_con->conname)) != 0)
   17735          90 :                     continue;
   17736             :             }
   17737        1418 :             else if (child_con->contype == CONSTRAINT_NOTNULL)
   17738             :             {
   17739             :                 Form_pg_attribute parent_attr;
   17740             :                 Form_pg_attribute child_attr;
   17741             :                 AttrNumber  child_attno;
   17742             : 
   17743        1418 :                 parent_attr = TupleDescAttr(parent_rel->rd_att, parent_attno - 1);
   17744        1418 :                 child_attno = extractNotNullColumn(child_tuple);
   17745        1418 :                 if (parent_attno != attmap->attnums[child_attno - 1])
   17746         286 :                     continue;
   17747             : 
   17748        1132 :                 child_attr = TupleDescAttr(child_rel->rd_att, child_attno - 1);
   17749             :                 /* there shouldn't be constraints on dropped columns */
   17750        1132 :                 if (parent_attr->attisdropped || child_attr->attisdropped)
   17751           0 :                     elog(ERROR, "found not-null constraint on dropped columns");
   17752             :             }
   17753             : 
   17754        1326 :             if (child_con->contype == CONSTRAINT_CHECK &&
   17755         194 :                 !constraints_equivalent(parent_tuple, child_tuple, RelationGetDescr(constraintrel)))
   17756           6 :                 ereport(ERROR,
   17757             :                         (errcode(ERRCODE_DATATYPE_MISMATCH),
   17758             :                          errmsg("child table \"%s\" has different definition for check constraint \"%s\"",
   17759             :                                 RelationGetRelationName(child_rel), NameStr(parent_con->conname))));
   17760             : 
   17761             :             /*
   17762             :              * If the child constraint is "no inherit" then cannot merge
   17763             :              */
   17764        1320 :             if (child_con->connoinherit)
   17765          12 :                 ereport(ERROR,
   17766             :                         (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
   17767             :                          errmsg("constraint \"%s\" conflicts with non-inherited constraint on child table \"%s\"",
   17768             :                                 NameStr(child_con->conname), RelationGetRelationName(child_rel))));
   17769             : 
   17770             :             /*
   17771             :              * If the child constraint is "not valid" then cannot merge with a
   17772             :              * valid parent constraint
   17773             :              */
   17774        1308 :             if (parent_con->convalidated && child_con->conenforced &&
   17775        1200 :                 !child_con->convalidated)
   17776          12 :                 ereport(ERROR,
   17777             :                         (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
   17778             :                          errmsg("constraint \"%s\" conflicts with NOT VALID constraint on child table \"%s\"",
   17779             :                                 NameStr(child_con->conname), RelationGetRelationName(child_rel))));
   17780             : 
   17781             :             /*
   17782             :              * A NOT ENFORCED child constraint cannot be merged with an
   17783             :              * ENFORCED parent constraint. However, the reverse is allowed,
   17784             :              * where the child constraint is ENFORCED.
   17785             :              */
   17786        1296 :             if (parent_con->conenforced && !child_con->conenforced)
   17787           6 :                 ereport(ERROR,
   17788             :                         (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
   17789             :                          errmsg("constraint \"%s\" conflicts with NOT ENFORCED constraint on child table \"%s\"",
   17790             :                                 NameStr(child_con->conname), RelationGetRelationName(child_rel))));
   17791             : 
   17792             :             /*
   17793             :              * OK, bump the child constraint's inheritance count.  (If we fail
   17794             :              * later on, this change will just roll back.)
   17795             :              */
   17796        1290 :             child_copy = heap_copytuple(child_tuple);
   17797        1290 :             child_con = (Form_pg_constraint) GETSTRUCT(child_copy);
   17798             : 
   17799        1290 :             if (pg_add_s16_overflow(child_con->coninhcount, 1,
   17800             :                                     &child_con->coninhcount))
   17801           0 :                 ereport(ERROR,
   17802             :                         errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
   17803             :                         errmsg("too many inheritance parents"));
   17804             : 
   17805             :             /*
   17806             :              * In case of partitions, an inherited constraint must be
   17807             :              * inherited only once since it cannot have multiple parents and
   17808             :              * it is never considered local.
   17809             :              */
   17810        1290 :             if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
   17811             :             {
   17812             :                 Assert(child_con->coninhcount == 1);
   17813        1142 :                 child_con->conislocal = false;
   17814             :             }
   17815             : 
   17816        1290 :             CatalogTupleUpdate(constraintrel, &child_copy->t_self, child_copy);
   17817        1290 :             heap_freetuple(child_copy);
   17818             : 
   17819        1290 :             found = true;
   17820        1290 :             break;
   17821             :         }
   17822             : 
   17823        1314 :         systable_endscan(child_scan);
   17824             : 
   17825        1314 :         if (!found)
   17826             :         {
   17827          24 :             if (parent_con->contype == CONSTRAINT_NOTNULL)
   17828           0 :                 ereport(ERROR,
   17829             :                         errcode(ERRCODE_DATATYPE_MISMATCH),
   17830             :                         errmsg("column \"%s\" in child table \"%s\" must be marked NOT NULL",
   17831             :                                get_attname(parent_relid,
   17832             :                                            extractNotNullColumn(parent_tuple),
   17833             :                                            false),
   17834             :                                RelationGetRelationName(child_rel)));
   17835             : 
   17836          24 :             ereport(ERROR,
   17837             :                     (errcode(ERRCODE_DATATYPE_MISMATCH),
   17838             :                      errmsg("child table is missing constraint \"%s\"",
   17839             :                             NameStr(parent_con->conname))));
   17840             :         }
   17841             :     }
   17842             : 
   17843        3234 :     systable_endscan(parent_scan);
   17844        3234 :     table_close(constraintrel, RowExclusiveLock);
   17845        3234 : }
   17846             : 
   17847             : /*
   17848             :  * ALTER TABLE NO INHERIT
   17849             :  *
   17850             :  * Return value is the address of the relation that is no longer parent.
   17851             :  */
   17852             : static ObjectAddress
   17853          94 : ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode)
   17854             : {
   17855             :     ObjectAddress address;
   17856             :     Relation    parent_rel;
   17857             : 
   17858          94 :     if (rel->rd_rel->relispartition)
   17859           0 :         ereport(ERROR,
   17860             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   17861             :                  errmsg("cannot change inheritance of a partition")));
   17862             : 
   17863             :     /*
   17864             :      * AccessShareLock on the parent is probably enough, seeing that DROP
   17865             :      * TABLE doesn't lock parent tables at all.  We need some lock since we'll
   17866             :      * be inspecting the parent's schema.
   17867             :      */
   17868          94 :     parent_rel = table_openrv(parent, AccessShareLock);
   17869             : 
   17870             :     /*
   17871             :      * We don't bother to check ownership of the parent table --- ownership of
   17872             :      * the child is presumed enough rights.
   17873             :      */
   17874             : 
   17875             :     /* Off to RemoveInheritance() where most of the work happens */
   17876          94 :     RemoveInheritance(rel, parent_rel, false);
   17877             : 
   17878          88 :     ObjectAddressSet(address, RelationRelationId,
   17879             :                      RelationGetRelid(parent_rel));
   17880             : 
   17881             :     /* keep our lock on the parent relation until commit */
   17882          88 :     table_close(parent_rel, NoLock);
   17883             : 
   17884          88 :     return address;
   17885             : }
   17886             : 
   17887             : /*
   17888             :  * MarkInheritDetached
   17889             :  *
   17890             :  * Set inhdetachpending for a partition, for ATExecDetachPartition
   17891             :  * in concurrent mode.  While at it, verify that no other partition is
   17892             :  * already pending detach.
   17893             :  */
   17894             : static void
   17895         146 : MarkInheritDetached(Relation child_rel, Relation parent_rel)
   17896             : {
   17897             :     Relation    catalogRelation;
   17898             :     SysScanDesc scan;
   17899             :     ScanKeyData key;
   17900             :     HeapTuple   inheritsTuple;
   17901         146 :     bool        found = false;
   17902             : 
   17903             :     Assert(parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
   17904             : 
   17905             :     /*
   17906             :      * Find pg_inherits entries by inhparent.  (We need to scan them all in
   17907             :      * order to verify that no other partition is pending detach.)
   17908             :      */
   17909         146 :     catalogRelation = table_open(InheritsRelationId, RowExclusiveLock);
   17910         146 :     ScanKeyInit(&key,
   17911             :                 Anum_pg_inherits_inhparent,
   17912             :                 BTEqualStrategyNumber, F_OIDEQ,
   17913             :                 ObjectIdGetDatum(RelationGetRelid(parent_rel)));
   17914         146 :     scan = systable_beginscan(catalogRelation, InheritsParentIndexId,
   17915             :                               true, NULL, 1, &key);
   17916             : 
   17917         576 :     while (HeapTupleIsValid(inheritsTuple = systable_getnext(scan)))
   17918             :     {
   17919             :         Form_pg_inherits inhForm;
   17920             : 
   17921         286 :         inhForm = (Form_pg_inherits) GETSTRUCT(inheritsTuple);
   17922         286 :         if (inhForm->inhdetachpending)
   17923           2 :             ereport(ERROR,
   17924             :                     errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
   17925             :                     errmsg("partition \"%s\" already pending detach in partitioned table \"%s.%s\"",
   17926             :                            get_rel_name(inhForm->inhrelid),
   17927             :                            get_namespace_name(parent_rel->rd_rel->relnamespace),
   17928             :                            RelationGetRelationName(parent_rel)),
   17929             :                     errhint("Use ALTER TABLE ... DETACH PARTITION ... FINALIZE to complete the pending detach operation."));
   17930             : 
   17931         284 :         if (inhForm->inhrelid == RelationGetRelid(child_rel))
   17932             :         {
   17933             :             HeapTuple   newtup;
   17934             : 
   17935         144 :             newtup = heap_copytuple(inheritsTuple);
   17936         144 :             ((Form_pg_inherits) GETSTRUCT(newtup))->inhdetachpending = true;
   17937             : 
   17938         144 :             CatalogTupleUpdate(catalogRelation,
   17939         144 :                                &inheritsTuple->t_self,
   17940             :                                newtup);
   17941         144 :             found = true;
   17942         144 :             heap_freetuple(newtup);
   17943             :             /* keep looking, to ensure we catch others pending detach */
   17944             :         }
   17945             :     }
   17946             : 
   17947             :     /* Done */
   17948         144 :     systable_endscan(scan);
   17949         144 :     table_close(catalogRelation, RowExclusiveLock);
   17950             : 
   17951         144 :     if (!found)
   17952           0 :         ereport(ERROR,
   17953             :                 (errcode(ERRCODE_UNDEFINED_TABLE),
   17954             :                  errmsg("relation \"%s\" is not a partition of relation \"%s\"",
   17955             :                         RelationGetRelationName(child_rel),
   17956             :                         RelationGetRelationName(parent_rel))));
   17957         144 : }
   17958             : 
   17959             : /*
   17960             :  * RemoveInheritance
   17961             :  *
   17962             :  * Drop a parent from the child's parents. This just adjusts the attinhcount
   17963             :  * and attislocal of the columns and removes the pg_inherit and pg_depend
   17964             :  * entries.  expect_detached is passed down to DeleteInheritsTuple, q.v..
   17965             :  *
   17966             :  * If attinhcount goes to 0 then attislocal gets set to true. If it goes back
   17967             :  * up attislocal stays true, which means if a child is ever removed from a
   17968             :  * parent then its columns will never be automatically dropped which may
   17969             :  * surprise. But at least we'll never surprise by dropping columns someone
   17970             :  * isn't expecting to be dropped which would actually mean data loss.
   17971             :  *
   17972             :  * coninhcount and conislocal for inherited constraints are adjusted in
   17973             :  * exactly the same way.
   17974             :  *
   17975             :  * Common to ATExecDropInherit() and ATExecDetachPartition().
   17976             :  */
   17977             : static void
   17978        1182 : RemoveInheritance(Relation child_rel, Relation parent_rel, bool expect_detached)
   17979             : {
   17980             :     Relation    catalogRelation;
   17981             :     SysScanDesc scan;
   17982             :     ScanKeyData key[3];
   17983             :     HeapTuple   attributeTuple,
   17984             :                 constraintTuple;
   17985             :     AttrMap    *attmap;
   17986             :     List       *connames;
   17987             :     List       *nncolumns;
   17988             :     bool        found;
   17989             :     bool        is_partitioning;
   17990             : 
   17991        1182 :     is_partitioning = (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
   17992             : 
   17993        1182 :     found = DeleteInheritsTuple(RelationGetRelid(child_rel),
   17994             :                                 RelationGetRelid(parent_rel),
   17995             :                                 expect_detached,
   17996        1182 :                                 RelationGetRelationName(child_rel));
   17997        1182 :     if (!found)
   17998             :     {
   17999          24 :         if (is_partitioning)
   18000          18 :             ereport(ERROR,
   18001             :                     (errcode(ERRCODE_UNDEFINED_TABLE),
   18002             :                      errmsg("relation \"%s\" is not a partition of relation \"%s\"",
   18003             :                             RelationGetRelationName(child_rel),
   18004             :                             RelationGetRelationName(parent_rel))));
   18005             :         else
   18006           6 :             ereport(ERROR,
   18007             :                     (errcode(ERRCODE_UNDEFINED_TABLE),
   18008             :                      errmsg("relation \"%s\" is not a parent of relation \"%s\"",
   18009             :                             RelationGetRelationName(parent_rel),
   18010             :                             RelationGetRelationName(child_rel))));
   18011             :     }
   18012             : 
   18013             :     /*
   18014             :      * Search through child columns looking for ones matching parent rel
   18015             :      */
   18016        1158 :     catalogRelation = table_open(AttributeRelationId, RowExclusiveLock);
   18017        1158 :     ScanKeyInit(&key[0],
   18018             :                 Anum_pg_attribute_attrelid,
   18019             :                 BTEqualStrategyNumber, F_OIDEQ,
   18020             :                 ObjectIdGetDatum(RelationGetRelid(child_rel)));
   18021        1158 :     scan = systable_beginscan(catalogRelation, AttributeRelidNumIndexId,
   18022             :                               true, NULL, 1, key);
   18023       10664 :     while (HeapTupleIsValid(attributeTuple = systable_getnext(scan)))
   18024             :     {
   18025        9506 :         Form_pg_attribute att = (Form_pg_attribute) GETSTRUCT(attributeTuple);
   18026             : 
   18027             :         /* Ignore if dropped or not inherited */
   18028        9506 :         if (att->attisdropped)
   18029          42 :             continue;
   18030        9464 :         if (att->attinhcount <= 0)
   18031        6978 :             continue;
   18032             : 
   18033        2486 :         if (SearchSysCacheExistsAttName(RelationGetRelid(parent_rel),
   18034        2486 :                                         NameStr(att->attname)))
   18035             :         {
   18036             :             /* Decrement inhcount and possibly set islocal to true */
   18037        2432 :             HeapTuple   copyTuple = heap_copytuple(attributeTuple);
   18038        2432 :             Form_pg_attribute copy_att = (Form_pg_attribute) GETSTRUCT(copyTuple);
   18039             : 
   18040        2432 :             copy_att->attinhcount--;
   18041        2432 :             if (copy_att->attinhcount == 0)
   18042        2402 :                 copy_att->attislocal = true;
   18043             : 
   18044        2432 :             CatalogTupleUpdate(catalogRelation, &copyTuple->t_self, copyTuple);
   18045        2432 :             heap_freetuple(copyTuple);
   18046             :         }
   18047             :     }
   18048        1158 :     systable_endscan(scan);
   18049        1158 :     table_close(catalogRelation, RowExclusiveLock);
   18050             : 
   18051             :     /*
   18052             :      * Likewise, find inherited check and not-null constraints and disinherit
   18053             :      * them. To do this, we first need a list of the names of the parent's
   18054             :      * check constraints.  (We cheat a bit by only checking for name matches,
   18055             :      * assuming that the expressions will match.)
   18056             :      *
   18057             :      * For NOT NULL columns, we store column numbers to match, mapping them in
   18058             :      * to the child rel's attribute numbers.
   18059             :      */
   18060        1158 :     attmap = build_attrmap_by_name(RelationGetDescr(child_rel),
   18061             :                                    RelationGetDescr(parent_rel),
   18062             :                                    false);
   18063             : 
   18064        1158 :     catalogRelation = table_open(ConstraintRelationId, RowExclusiveLock);
   18065        1158 :     ScanKeyInit(&key[0],
   18066             :                 Anum_pg_constraint_conrelid,
   18067             :                 BTEqualStrategyNumber, F_OIDEQ,
   18068             :                 ObjectIdGetDatum(RelationGetRelid(parent_rel)));
   18069        1158 :     scan = systable_beginscan(catalogRelation, ConstraintRelidTypidNameIndexId,
   18070             :                               true, NULL, 1, key);
   18071             : 
   18072        1158 :     connames = NIL;
   18073        1158 :     nncolumns = NIL;
   18074             : 
   18075        2208 :     while (HeapTupleIsValid(constraintTuple = systable_getnext(scan)))
   18076             :     {
   18077        1050 :         Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(constraintTuple);
   18078             : 
   18079        1050 :         if (con->connoinherit)
   18080         224 :             continue;
   18081             : 
   18082         826 :         if (con->contype == CONSTRAINT_CHECK)
   18083         114 :             connames = lappend(connames, pstrdup(NameStr(con->conname)));
   18084         826 :         if (con->contype == CONSTRAINT_NOTNULL)
   18085             :         {
   18086         382 :             AttrNumber  parent_attno = extractNotNullColumn(constraintTuple);
   18087             : 
   18088         382 :             nncolumns = lappend_int(nncolumns, attmap->attnums[parent_attno - 1]);
   18089             :         }
   18090             :     }
   18091             : 
   18092        1158 :     systable_endscan(scan);
   18093             : 
   18094             :     /* Now scan the child's constraints to find matches */
   18095        1158 :     ScanKeyInit(&key[0],
   18096             :                 Anum_pg_constraint_conrelid,
   18097             :                 BTEqualStrategyNumber, F_OIDEQ,
   18098             :                 ObjectIdGetDatum(RelationGetRelid(child_rel)));
   18099        1158 :     scan = systable_beginscan(catalogRelation, ConstraintRelidTypidNameIndexId,
   18100             :                               true, NULL, 1, key);
   18101             : 
   18102        2204 :     while (HeapTupleIsValid(constraintTuple = systable_getnext(scan)))
   18103             :     {
   18104        1046 :         Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(constraintTuple);
   18105        1046 :         bool        match = false;
   18106             : 
   18107             :         /*
   18108             :          * Match CHECK constraints by name, not-null constraints by column
   18109             :          * number, and ignore all others.
   18110             :          */
   18111        1046 :         if (con->contype == CONSTRAINT_CHECK)
   18112             :         {
   18113         346 :             foreach_ptr(char, chkname, connames)
   18114             :             {
   18115         120 :                 if (con->contype == CONSTRAINT_CHECK &&
   18116         120 :                     strcmp(NameStr(con->conname), chkname) == 0)
   18117             :                 {
   18118         114 :                     match = true;
   18119         114 :                     connames = foreach_delete_current(connames, chkname);
   18120         114 :                     break;
   18121             :                 }
   18122             :             }
   18123             :         }
   18124         876 :         else if (con->contype == CONSTRAINT_NOTNULL)
   18125             :         {
   18126         442 :             AttrNumber  child_attno = extractNotNullColumn(constraintTuple);
   18127             : 
   18128         890 :             foreach_int(prevattno, nncolumns)
   18129             :             {
   18130         388 :                 if (prevattno == child_attno)
   18131             :                 {
   18132         382 :                     match = true;
   18133         382 :                     nncolumns = foreach_delete_current(nncolumns, prevattno);
   18134         382 :                     break;
   18135             :                 }
   18136             :             }
   18137             :         }
   18138             :         else
   18139         434 :             continue;
   18140             : 
   18141         612 :         if (match)
   18142             :         {
   18143             :             /* Decrement inhcount and possibly set islocal to true */
   18144         496 :             HeapTuple   copyTuple = heap_copytuple(constraintTuple);
   18145         496 :             Form_pg_constraint copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
   18146             : 
   18147         496 :             if (copy_con->coninhcount <= 0) /* shouldn't happen */
   18148           0 :                 elog(ERROR, "relation %u has non-inherited constraint \"%s\"",
   18149             :                      RelationGetRelid(child_rel), NameStr(copy_con->conname));
   18150             : 
   18151         496 :             copy_con->coninhcount--;
   18152         496 :             if (copy_con->coninhcount == 0)
   18153         478 :                 copy_con->conislocal = true;
   18154             : 
   18155         496 :             CatalogTupleUpdate(catalogRelation, &copyTuple->t_self, copyTuple);
   18156         496 :             heap_freetuple(copyTuple);
   18157             :         }
   18158             :     }
   18159             : 
   18160             :     /* We should have matched all constraints */
   18161        1158 :     if (connames != NIL || nncolumns != NIL)
   18162           0 :         elog(ERROR, "%d unmatched constraints while removing inheritance from \"%s\" to \"%s\"",
   18163             :              list_length(connames) + list_length(nncolumns),
   18164             :              RelationGetRelationName(child_rel), RelationGetRelationName(parent_rel));
   18165             : 
   18166        1158 :     systable_endscan(scan);
   18167        1158 :     table_close(catalogRelation, RowExclusiveLock);
   18168             : 
   18169        1158 :     drop_parent_dependency(RelationGetRelid(child_rel),
   18170             :                            RelationRelationId,
   18171             :                            RelationGetRelid(parent_rel),
   18172             :                            child_dependency_type(is_partitioning));
   18173             : 
   18174             :     /*
   18175             :      * Post alter hook of this inherits. Since object_access_hook doesn't take
   18176             :      * multiple object identifiers, we relay oid of parent relation using
   18177             :      * auxiliary_id argument.
   18178             :      */
   18179        1158 :     InvokeObjectPostAlterHookArg(InheritsRelationId,
   18180             :                                  RelationGetRelid(child_rel), 0,
   18181             :                                  RelationGetRelid(parent_rel), false);
   18182        1158 : }
   18183             : 
   18184             : /*
   18185             :  * Drop the dependency created by StoreCatalogInheritance1 (CREATE TABLE
   18186             :  * INHERITS/ALTER TABLE INHERIT -- refclassid will be RelationRelationId) or
   18187             :  * heap_create_with_catalog (CREATE TABLE OF/ALTER TABLE OF -- refclassid will
   18188             :  * be TypeRelationId).  There's no convenient way to do this, so go trawling
   18189             :  * through pg_depend.
   18190             :  */
   18191             : static void
   18192        1170 : drop_parent_dependency(Oid relid, Oid refclassid, Oid refobjid,
   18193             :                        DependencyType deptype)
   18194             : {
   18195             :     Relation    catalogRelation;
   18196             :     SysScanDesc scan;
   18197             :     ScanKeyData key[3];
   18198             :     HeapTuple   depTuple;
   18199             : 
   18200        1170 :     catalogRelation = table_open(DependRelationId, RowExclusiveLock);
   18201             : 
   18202        1170 :     ScanKeyInit(&key[0],
   18203             :                 Anum_pg_depend_classid,
   18204             :                 BTEqualStrategyNumber, F_OIDEQ,
   18205             :                 ObjectIdGetDatum(RelationRelationId));
   18206        1170 :     ScanKeyInit(&key[1],
   18207             :                 Anum_pg_depend_objid,
   18208             :                 BTEqualStrategyNumber, F_OIDEQ,
   18209             :                 ObjectIdGetDatum(relid));
   18210        1170 :     ScanKeyInit(&key[2],
   18211             :                 Anum_pg_depend_objsubid,
   18212             :                 BTEqualStrategyNumber, F_INT4EQ,
   18213             :                 Int32GetDatum(0));
   18214             : 
   18215        1170 :     scan = systable_beginscan(catalogRelation, DependDependerIndexId, true,
   18216             :                               NULL, 3, key);
   18217             : 
   18218        3592 :     while (HeapTupleIsValid(depTuple = systable_getnext(scan)))
   18219             :     {
   18220        2422 :         Form_pg_depend dep = (Form_pg_depend) GETSTRUCT(depTuple);
   18221             : 
   18222        2422 :         if (dep->refclassid == refclassid &&
   18223        1212 :             dep->refobjid == refobjid &&
   18224        1170 :             dep->refobjsubid == 0 &&
   18225        1170 :             dep->deptype == deptype)
   18226        1170 :             CatalogTupleDelete(catalogRelation, &depTuple->t_self);
   18227             :     }
   18228             : 
   18229        1170 :     systable_endscan(scan);
   18230        1170 :     table_close(catalogRelation, RowExclusiveLock);
   18231        1170 : }
   18232             : 
   18233             : /*
   18234             :  * ALTER TABLE OF
   18235             :  *
   18236             :  * Attach a table to a composite type, as though it had been created with CREATE
   18237             :  * TABLE OF.  All attname, atttypid, atttypmod and attcollation must match.  The
   18238             :  * subject table must not have inheritance parents.  These restrictions ensure
   18239             :  * that you cannot create a configuration impossible with CREATE TABLE OF alone.
   18240             :  *
   18241             :  * The address of the type is returned.
   18242             :  */
   18243             : static ObjectAddress
   18244          66 : ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode)
   18245             : {
   18246          66 :     Oid         relid = RelationGetRelid(rel);
   18247             :     Type        typetuple;
   18248             :     Form_pg_type typeform;
   18249             :     Oid         typeid;
   18250             :     Relation    inheritsRelation,
   18251             :                 relationRelation;
   18252             :     SysScanDesc scan;
   18253             :     ScanKeyData key;
   18254             :     AttrNumber  table_attno,
   18255             :                 type_attno;
   18256             :     TupleDesc   typeTupleDesc,
   18257             :                 tableTupleDesc;
   18258             :     ObjectAddress tableobj,
   18259             :                 typeobj;
   18260             :     HeapTuple   classtuple;
   18261             : 
   18262             :     /* Validate the type. */
   18263          66 :     typetuple = typenameType(NULL, ofTypename, NULL);
   18264          66 :     check_of_type(typetuple);
   18265          66 :     typeform = (Form_pg_type) GETSTRUCT(typetuple);
   18266          66 :     typeid = typeform->oid;
   18267             : 
   18268             :     /* Fail if the table has any inheritance parents. */
   18269          66 :     inheritsRelation = table_open(InheritsRelationId, AccessShareLock);
   18270          66 :     ScanKeyInit(&key,
   18271             :                 Anum_pg_inherits_inhrelid,
   18272             :                 BTEqualStrategyNumber, F_OIDEQ,
   18273             :                 ObjectIdGetDatum(relid));
   18274          66 :     scan = systable_beginscan(inheritsRelation, InheritsRelidSeqnoIndexId,
   18275             :                               true, NULL, 1, &key);
   18276          66 :     if (HeapTupleIsValid(systable_getnext(scan)))
   18277           6 :         ereport(ERROR,
   18278             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   18279             :                  errmsg("typed tables cannot inherit")));
   18280          60 :     systable_endscan(scan);
   18281          60 :     table_close(inheritsRelation, AccessShareLock);
   18282             : 
   18283             :     /*
   18284             :      * Check the tuple descriptors for compatibility.  Unlike inheritance, we
   18285             :      * require that the order also match.  However, attnotnull need not match.
   18286             :      */
   18287          60 :     typeTupleDesc = lookup_rowtype_tupdesc(typeid, -1);
   18288          60 :     tableTupleDesc = RelationGetDescr(rel);
   18289          60 :     table_attno = 1;
   18290         190 :     for (type_attno = 1; type_attno <= typeTupleDesc->natts; type_attno++)
   18291             :     {
   18292             :         Form_pg_attribute type_attr,
   18293             :                     table_attr;
   18294             :         const char *type_attname,
   18295             :                    *table_attname;
   18296             : 
   18297             :         /* Get the next non-dropped type attribute. */
   18298         154 :         type_attr = TupleDescAttr(typeTupleDesc, type_attno - 1);
   18299         154 :         if (type_attr->attisdropped)
   18300          44 :             continue;
   18301         110 :         type_attname = NameStr(type_attr->attname);
   18302             : 
   18303             :         /* Get the next non-dropped table attribute. */
   18304             :         do
   18305             :         {
   18306         122 :             if (table_attno > tableTupleDesc->natts)
   18307           6 :                 ereport(ERROR,
   18308             :                         (errcode(ERRCODE_DATATYPE_MISMATCH),
   18309             :                          errmsg("table is missing column \"%s\"",
   18310             :                                 type_attname)));
   18311         116 :             table_attr = TupleDescAttr(tableTupleDesc, table_attno - 1);
   18312         116 :             table_attno++;
   18313         116 :         } while (table_attr->attisdropped);
   18314         104 :         table_attname = NameStr(table_attr->attname);
   18315             : 
   18316             :         /* Compare name. */
   18317         104 :         if (strncmp(table_attname, type_attname, NAMEDATALEN) != 0)
   18318           6 :             ereport(ERROR,
   18319             :                     (errcode(ERRCODE_DATATYPE_MISMATCH),
   18320             :                      errmsg("table has column \"%s\" where type requires \"%s\"",
   18321             :                             table_attname, type_attname)));
   18322             : 
   18323             :         /* Compare type. */
   18324          98 :         if (table_attr->atttypid != type_attr->atttypid ||
   18325          92 :             table_attr->atttypmod != type_attr->atttypmod ||
   18326          86 :             table_attr->attcollation != type_attr->attcollation)
   18327          12 :             ereport(ERROR,
   18328             :                     (errcode(ERRCODE_DATATYPE_MISMATCH),
   18329             :                      errmsg("table \"%s\" has different type for column \"%s\"",
   18330             :                             RelationGetRelationName(rel), type_attname)));
   18331             :     }
   18332          36 :     ReleaseTupleDesc(typeTupleDesc);
   18333             : 
   18334             :     /* Any remaining columns at the end of the table had better be dropped. */
   18335          36 :     for (; table_attno <= tableTupleDesc->natts; table_attno++)
   18336             :     {
   18337           6 :         Form_pg_attribute table_attr = TupleDescAttr(tableTupleDesc,
   18338             :                                                      table_attno - 1);
   18339             : 
   18340           6 :         if (!table_attr->attisdropped)
   18341           6 :             ereport(ERROR,
   18342             :                     (errcode(ERRCODE_DATATYPE_MISMATCH),
   18343             :                      errmsg("table has extra column \"%s\"",
   18344             :                             NameStr(table_attr->attname))));
   18345             :     }
   18346             : 
   18347             :     /* If the table was already typed, drop the existing dependency. */
   18348          30 :     if (rel->rd_rel->reloftype)
   18349           6 :         drop_parent_dependency(relid, TypeRelationId, rel->rd_rel->reloftype,
   18350             :                                DEPENDENCY_NORMAL);
   18351             : 
   18352             :     /* Record a dependency on the new type. */
   18353          30 :     tableobj.classId = RelationRelationId;
   18354          30 :     tableobj.objectId = relid;
   18355          30 :     tableobj.objectSubId = 0;
   18356          30 :     typeobj.classId = TypeRelationId;
   18357          30 :     typeobj.objectId = typeid;
   18358          30 :     typeobj.objectSubId = 0;
   18359          30 :     recordDependencyOn(&tableobj, &typeobj, DEPENDENCY_NORMAL);
   18360             : 
   18361             :     /* Update pg_class.reloftype */
   18362          30 :     relationRelation = table_open(RelationRelationId, RowExclusiveLock);
   18363          30 :     classtuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
   18364          30 :     if (!HeapTupleIsValid(classtuple))
   18365           0 :         elog(ERROR, "cache lookup failed for relation %u", relid);
   18366          30 :     ((Form_pg_class) GETSTRUCT(classtuple))->reloftype = typeid;
   18367          30 :     CatalogTupleUpdate(relationRelation, &classtuple->t_self, classtuple);
   18368             : 
   18369          30 :     InvokeObjectPostAlterHook(RelationRelationId, relid, 0);
   18370             : 
   18371          30 :     heap_freetuple(classtuple);
   18372          30 :     table_close(relationRelation, RowExclusiveLock);
   18373             : 
   18374          30 :     ReleaseSysCache(typetuple);
   18375             : 
   18376          30 :     return typeobj;
   18377             : }
   18378             : 
   18379             : /*
   18380             :  * ALTER TABLE NOT OF
   18381             :  *
   18382             :  * Detach a typed table from its originating type.  Just clear reloftype and
   18383             :  * remove the dependency.
   18384             :  */
   18385             : static void
   18386           6 : ATExecDropOf(Relation rel, LOCKMODE lockmode)
   18387             : {
   18388           6 :     Oid         relid = RelationGetRelid(rel);
   18389             :     Relation    relationRelation;
   18390             :     HeapTuple   tuple;
   18391             : 
   18392           6 :     if (!OidIsValid(rel->rd_rel->reloftype))
   18393           0 :         ereport(ERROR,
   18394             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   18395             :                  errmsg("\"%s\" is not a typed table",
   18396             :                         RelationGetRelationName(rel))));
   18397             : 
   18398             :     /*
   18399             :      * We don't bother to check ownership of the type --- ownership of the
   18400             :      * table is presumed enough rights.  No lock required on the type, either.
   18401             :      */
   18402             : 
   18403           6 :     drop_parent_dependency(relid, TypeRelationId, rel->rd_rel->reloftype,
   18404             :                            DEPENDENCY_NORMAL);
   18405             : 
   18406             :     /* Clear pg_class.reloftype */
   18407           6 :     relationRelation = table_open(RelationRelationId, RowExclusiveLock);
   18408           6 :     tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
   18409           6 :     if (!HeapTupleIsValid(tuple))
   18410           0 :         elog(ERROR, "cache lookup failed for relation %u", relid);
   18411           6 :     ((Form_pg_class) GETSTRUCT(tuple))->reloftype = InvalidOid;
   18412           6 :     CatalogTupleUpdate(relationRelation, &tuple->t_self, tuple);
   18413             : 
   18414           6 :     InvokeObjectPostAlterHook(RelationRelationId, relid, 0);
   18415             : 
   18416           6 :     heap_freetuple(tuple);
   18417           6 :     table_close(relationRelation, RowExclusiveLock);
   18418           6 : }
   18419             : 
   18420             : /*
   18421             :  * relation_mark_replica_identity: Update a table's replica identity
   18422             :  *
   18423             :  * Iff ri_type = REPLICA_IDENTITY_INDEX, indexOid must be the Oid of a suitable
   18424             :  * index. Otherwise, it must be InvalidOid.
   18425             :  *
   18426             :  * Caller had better hold an exclusive lock on the relation, as the results
   18427             :  * of running two of these concurrently wouldn't be pretty.
   18428             :  */
   18429             : static void
   18430         464 : relation_mark_replica_identity(Relation rel, char ri_type, Oid indexOid,
   18431             :                                bool is_internal)
   18432             : {
   18433             :     Relation    pg_index;
   18434             :     Relation    pg_class;
   18435             :     HeapTuple   pg_class_tuple;
   18436             :     HeapTuple   pg_index_tuple;
   18437             :     Form_pg_class pg_class_form;
   18438             :     Form_pg_index pg_index_form;
   18439             :     ListCell   *index;
   18440             : 
   18441             :     /*
   18442             :      * Check whether relreplident has changed, and update it if so.
   18443             :      */
   18444         464 :     pg_class = table_open(RelationRelationId, RowExclusiveLock);
   18445         464 :     pg_class_tuple = SearchSysCacheCopy1(RELOID,
   18446             :                                          ObjectIdGetDatum(RelationGetRelid(rel)));
   18447         464 :     if (!HeapTupleIsValid(pg_class_tuple))
   18448           0 :         elog(ERROR, "cache lookup failed for relation \"%s\"",
   18449             :              RelationGetRelationName(rel));
   18450         464 :     pg_class_form = (Form_pg_class) GETSTRUCT(pg_class_tuple);
   18451         464 :     if (pg_class_form->relreplident != ri_type)
   18452             :     {
   18453         414 :         pg_class_form->relreplident = ri_type;
   18454         414 :         CatalogTupleUpdate(pg_class, &pg_class_tuple->t_self, pg_class_tuple);
   18455             :     }
   18456         464 :     table_close(pg_class, RowExclusiveLock);
   18457         464 :     heap_freetuple(pg_class_tuple);
   18458             : 
   18459             :     /*
   18460             :      * Update the per-index indisreplident flags correctly.
   18461             :      */
   18462         464 :     pg_index = table_open(IndexRelationId, RowExclusiveLock);
   18463        1188 :     foreach(index, RelationGetIndexList(rel))
   18464             :     {
   18465         724 :         Oid         thisIndexOid = lfirst_oid(index);
   18466         724 :         bool        dirty = false;
   18467             : 
   18468         724 :         pg_index_tuple = SearchSysCacheCopy1(INDEXRELID,
   18469             :                                              ObjectIdGetDatum(thisIndexOid));
   18470         724 :         if (!HeapTupleIsValid(pg_index_tuple))
   18471           0 :             elog(ERROR, "cache lookup failed for index %u", thisIndexOid);
   18472         724 :         pg_index_form = (Form_pg_index) GETSTRUCT(pg_index_tuple);
   18473             : 
   18474         724 :         if (thisIndexOid == indexOid)
   18475             :         {
   18476             :             /* Set the bit if not already set. */
   18477         240 :             if (!pg_index_form->indisreplident)
   18478             :             {
   18479         222 :                 dirty = true;
   18480         222 :                 pg_index_form->indisreplident = true;
   18481             :             }
   18482             :         }
   18483             :         else
   18484             :         {
   18485             :             /* Unset the bit if set. */
   18486         484 :             if (pg_index_form->indisreplident)
   18487             :             {
   18488          52 :                 dirty = true;
   18489          52 :                 pg_index_form->indisreplident = false;
   18490             :             }
   18491             :         }
   18492             : 
   18493         724 :         if (dirty)
   18494             :         {
   18495         274 :             CatalogTupleUpdate(pg_index, &pg_index_tuple->t_self, pg_index_tuple);
   18496         274 :             InvokeObjectPostAlterHookArg(IndexRelationId, thisIndexOid, 0,
   18497             :                                          InvalidOid, is_internal);
   18498             : 
   18499             :             /*
   18500             :              * Invalidate the relcache for the table, so that after we commit
   18501             :              * all sessions will refresh the table's replica identity index
   18502             :              * before attempting any UPDATE or DELETE on the table.  (If we
   18503             :              * changed the table's pg_class row above, then a relcache inval
   18504             :              * is already queued due to that; but we might not have.)
   18505             :              */
   18506         274 :             CacheInvalidateRelcache(rel);
   18507             :         }
   18508         724 :         heap_freetuple(pg_index_tuple);
   18509             :     }
   18510             : 
   18511         464 :     table_close(pg_index, RowExclusiveLock);
   18512         464 : }
   18513             : 
   18514             : /*
   18515             :  * ALTER TABLE <name> REPLICA IDENTITY ...
   18516             :  */
   18517             : static void
   18518         512 : ATExecReplicaIdentity(Relation rel, ReplicaIdentityStmt *stmt, LOCKMODE lockmode)
   18519             : {
   18520             :     Oid         indexOid;
   18521             :     Relation    indexRel;
   18522             :     int         key;
   18523             : 
   18524         512 :     if (stmt->identity_type == REPLICA_IDENTITY_DEFAULT)
   18525             :     {
   18526           6 :         relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
   18527           6 :         return;
   18528             :     }
   18529         506 :     else if (stmt->identity_type == REPLICA_IDENTITY_FULL)
   18530             :     {
   18531         170 :         relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
   18532         170 :         return;
   18533             :     }
   18534         336 :     else if (stmt->identity_type == REPLICA_IDENTITY_NOTHING)
   18535             :     {
   18536          48 :         relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
   18537          48 :         return;
   18538             :     }
   18539         288 :     else if (stmt->identity_type == REPLICA_IDENTITY_INDEX)
   18540             :     {
   18541             :          /* fallthrough */ ;
   18542             :     }
   18543             :     else
   18544           0 :         elog(ERROR, "unexpected identity type %u", stmt->identity_type);
   18545             : 
   18546             :     /* Check that the index exists */
   18547         288 :     indexOid = get_relname_relid(stmt->name, rel->rd_rel->relnamespace);
   18548         288 :     if (!OidIsValid(indexOid))
   18549           0 :         ereport(ERROR,
   18550             :                 (errcode(ERRCODE_UNDEFINED_OBJECT),
   18551             :                  errmsg("index \"%s\" for table \"%s\" does not exist",
   18552             :                         stmt->name, RelationGetRelationName(rel))));
   18553             : 
   18554         288 :     indexRel = index_open(indexOid, ShareLock);
   18555             : 
   18556             :     /* Check that the index is on the relation we're altering. */
   18557         288 :     if (indexRel->rd_index == NULL ||
   18558         288 :         indexRel->rd_index->indrelid != RelationGetRelid(rel))
   18559           6 :         ereport(ERROR,
   18560             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   18561             :                  errmsg("\"%s\" is not an index for table \"%s\"",
   18562             :                         RelationGetRelationName(indexRel),
   18563             :                         RelationGetRelationName(rel))));
   18564             : 
   18565             :     /*
   18566             :      * The AM must support uniqueness, and the index must in fact be unique.
   18567             :      * If we have a WITHOUT OVERLAPS constraint (identified by uniqueness +
   18568             :      * exclusion), we can use that too.
   18569             :      */
   18570         282 :     if ((!indexRel->rd_indam->amcanunique ||
   18571         262 :          !indexRel->rd_index->indisunique) &&
   18572          26 :         !(indexRel->rd_index->indisunique && indexRel->rd_index->indisexclusion))
   18573          12 :         ereport(ERROR,
   18574             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   18575             :                  errmsg("cannot use non-unique index \"%s\" as replica identity",
   18576             :                         RelationGetRelationName(indexRel))));
   18577             :     /* Deferred indexes are not guaranteed to be always unique. */
   18578         270 :     if (!indexRel->rd_index->indimmediate)
   18579          12 :         ereport(ERROR,
   18580             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   18581             :                  errmsg("cannot use non-immediate index \"%s\" as replica identity",
   18582             :                         RelationGetRelationName(indexRel))));
   18583             :     /* Expression indexes aren't supported. */
   18584         258 :     if (RelationGetIndexExpressions(indexRel) != NIL)
   18585           6 :         ereport(ERROR,
   18586             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   18587             :                  errmsg("cannot use expression index \"%s\" as replica identity",
   18588             :                         RelationGetRelationName(indexRel))));
   18589             :     /* Predicate indexes aren't supported. */
   18590         252 :     if (RelationGetIndexPredicate(indexRel) != NIL)
   18591           6 :         ereport(ERROR,
   18592             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   18593             :                  errmsg("cannot use partial index \"%s\" as replica identity",
   18594             :                         RelationGetRelationName(indexRel))));
   18595             : 
   18596             :     /* Check index for nullable columns. */
   18597         552 :     for (key = 0; key < IndexRelationGetNumberOfKeyAttributes(indexRel); key++)
   18598             :     {
   18599         312 :         int16       attno = indexRel->rd_index->indkey.values[key];
   18600             :         Form_pg_attribute attr;
   18601             : 
   18602             :         /*
   18603             :          * Reject any other system columns.  (Going forward, we'll disallow
   18604             :          * indexes containing such columns in the first place, but they might
   18605             :          * exist in older branches.)
   18606             :          */
   18607         312 :         if (attno <= 0)
   18608           0 :             ereport(ERROR,
   18609             :                     (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
   18610             :                      errmsg("index \"%s\" cannot be used as replica identity because column %d is a system column",
   18611             :                             RelationGetRelationName(indexRel), attno)));
   18612             : 
   18613         312 :         attr = TupleDescAttr(rel->rd_att, attno - 1);
   18614         312 :         if (!attr->attnotnull)
   18615           6 :             ereport(ERROR,
   18616             :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   18617             :                      errmsg("index \"%s\" cannot be used as replica identity because column \"%s\" is nullable",
   18618             :                             RelationGetRelationName(indexRel),
   18619             :                             NameStr(attr->attname))));
   18620             :     }
   18621             : 
   18622             :     /* This index is suitable for use as a replica identity. Mark it. */
   18623         240 :     relation_mark_replica_identity(rel, stmt->identity_type, indexOid, true);
   18624             : 
   18625         240 :     index_close(indexRel, NoLock);
   18626             : }
   18627             : 
   18628             : /*
   18629             :  * ALTER TABLE ENABLE/DISABLE ROW LEVEL SECURITY
   18630             :  */
   18631             : static void
   18632         348 : ATExecSetRowSecurity(Relation rel, bool rls)
   18633             : {
   18634             :     Relation    pg_class;
   18635             :     Oid         relid;
   18636             :     HeapTuple   tuple;
   18637             : 
   18638         348 :     relid = RelationGetRelid(rel);
   18639             : 
   18640             :     /* Pull the record for this relation and update it */
   18641         348 :     pg_class = table_open(RelationRelationId, RowExclusiveLock);
   18642             : 
   18643         348 :     tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
   18644             : 
   18645         348 :     if (!HeapTupleIsValid(tuple))
   18646           0 :         elog(ERROR, "cache lookup failed for relation %u", relid);
   18647             : 
   18648         348 :     ((Form_pg_class) GETSTRUCT(tuple))->relrowsecurity = rls;
   18649         348 :     CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
   18650             : 
   18651         348 :     InvokeObjectPostAlterHook(RelationRelationId,
   18652             :                               RelationGetRelid(rel), 0);
   18653             : 
   18654         348 :     table_close(pg_class, RowExclusiveLock);
   18655         348 :     heap_freetuple(tuple);
   18656         348 : }
   18657             : 
   18658             : /*
   18659             :  * ALTER TABLE FORCE/NO FORCE ROW LEVEL SECURITY
   18660             :  */
   18661             : static void
   18662         132 : ATExecForceNoForceRowSecurity(Relation rel, bool force_rls)
   18663             : {
   18664             :     Relation    pg_class;
   18665             :     Oid         relid;
   18666             :     HeapTuple   tuple;
   18667             : 
   18668         132 :     relid = RelationGetRelid(rel);
   18669             : 
   18670         132 :     pg_class = table_open(RelationRelationId, RowExclusiveLock);
   18671             : 
   18672         132 :     tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
   18673             : 
   18674         132 :     if (!HeapTupleIsValid(tuple))
   18675           0 :         elog(ERROR, "cache lookup failed for relation %u", relid);
   18676             : 
   18677         132 :     ((Form_pg_class) GETSTRUCT(tuple))->relforcerowsecurity = force_rls;
   18678         132 :     CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
   18679             : 
   18680         132 :     InvokeObjectPostAlterHook(RelationRelationId,
   18681             :                               RelationGetRelid(rel), 0);
   18682             : 
   18683         132 :     table_close(pg_class, RowExclusiveLock);
   18684         132 :     heap_freetuple(tuple);
   18685         132 : }
   18686             : 
   18687             : /*
   18688             :  * ALTER FOREIGN TABLE <name> OPTIONS (...)
   18689             :  */
   18690             : static void
   18691          58 : ATExecGenericOptions(Relation rel, List *options)
   18692             : {
   18693             :     Relation    ftrel;
   18694             :     ForeignServer *server;
   18695             :     ForeignDataWrapper *fdw;
   18696             :     HeapTuple   tuple;
   18697             :     bool        isnull;
   18698             :     Datum       repl_val[Natts_pg_foreign_table];
   18699             :     bool        repl_null[Natts_pg_foreign_table];
   18700             :     bool        repl_repl[Natts_pg_foreign_table];
   18701             :     Datum       datum;
   18702             :     Form_pg_foreign_table tableform;
   18703             : 
   18704          58 :     if (options == NIL)
   18705           0 :         return;
   18706             : 
   18707          58 :     ftrel = table_open(ForeignTableRelationId, RowExclusiveLock);
   18708             : 
   18709          58 :     tuple = SearchSysCacheCopy1(FOREIGNTABLEREL,
   18710             :                                 ObjectIdGetDatum(rel->rd_id));
   18711          58 :     if (!HeapTupleIsValid(tuple))
   18712           0 :         ereport(ERROR,
   18713             :                 (errcode(ERRCODE_UNDEFINED_OBJECT),
   18714             :                  errmsg("foreign table \"%s\" does not exist",
   18715             :                         RelationGetRelationName(rel))));
   18716          58 :     tableform = (Form_pg_foreign_table) GETSTRUCT(tuple);
   18717          58 :     server = GetForeignServer(tableform->ftserver);
   18718          58 :     fdw = GetForeignDataWrapper(server->fdwid);
   18719             : 
   18720          58 :     memset(repl_val, 0, sizeof(repl_val));
   18721          58 :     memset(repl_null, false, sizeof(repl_null));
   18722          58 :     memset(repl_repl, false, sizeof(repl_repl));
   18723             : 
   18724             :     /* Extract the current options */
   18725          58 :     datum = SysCacheGetAttr(FOREIGNTABLEREL,
   18726             :                             tuple,
   18727             :                             Anum_pg_foreign_table_ftoptions,
   18728             :                             &isnull);
   18729          58 :     if (isnull)
   18730           4 :         datum = PointerGetDatum(NULL);
   18731             : 
   18732             :     /* Transform the options */
   18733          58 :     datum = transformGenericOptions(ForeignTableRelationId,
   18734             :                                     datum,
   18735             :                                     options,
   18736             :                                     fdw->fdwvalidator);
   18737             : 
   18738          56 :     if (DatumGetPointer(datum) != NULL)
   18739          56 :         repl_val[Anum_pg_foreign_table_ftoptions - 1] = datum;
   18740             :     else
   18741           0 :         repl_null[Anum_pg_foreign_table_ftoptions - 1] = true;
   18742             : 
   18743          56 :     repl_repl[Anum_pg_foreign_table_ftoptions - 1] = true;
   18744             : 
   18745             :     /* Everything looks good - update the tuple */
   18746             : 
   18747          56 :     tuple = heap_modify_tuple(tuple, RelationGetDescr(ftrel),
   18748             :                               repl_val, repl_null, repl_repl);
   18749             : 
   18750          56 :     CatalogTupleUpdate(ftrel, &tuple->t_self, tuple);
   18751             : 
   18752             :     /*
   18753             :      * Invalidate relcache so that all sessions will refresh any cached plans
   18754             :      * that might depend on the old options.
   18755             :      */
   18756          56 :     CacheInvalidateRelcache(rel);
   18757             : 
   18758          56 :     InvokeObjectPostAlterHook(ForeignTableRelationId,
   18759             :                               RelationGetRelid(rel), 0);
   18760             : 
   18761          56 :     table_close(ftrel, RowExclusiveLock);
   18762             : 
   18763          56 :     heap_freetuple(tuple);
   18764             : }
   18765             : 
   18766             : /*
   18767             :  * ALTER TABLE ALTER COLUMN SET COMPRESSION
   18768             :  *
   18769             :  * Return value is the address of the modified column
   18770             :  */
   18771             : static ObjectAddress
   18772          78 : ATExecSetCompression(Relation rel,
   18773             :                      const char *column,
   18774             :                      Node *newValue,
   18775             :                      LOCKMODE lockmode)
   18776             : {
   18777             :     Relation    attrel;
   18778             :     HeapTuple   tuple;
   18779             :     Form_pg_attribute atttableform;
   18780             :     AttrNumber  attnum;
   18781             :     char       *compression;
   18782             :     char        cmethod;
   18783             :     ObjectAddress address;
   18784             : 
   18785          78 :     compression = strVal(newValue);
   18786             : 
   18787          78 :     attrel = table_open(AttributeRelationId, RowExclusiveLock);
   18788             : 
   18789             :     /* copy the cache entry so we can scribble on it below */
   18790          78 :     tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), column);
   18791          78 :     if (!HeapTupleIsValid(tuple))
   18792           0 :         ereport(ERROR,
   18793             :                 (errcode(ERRCODE_UNDEFINED_COLUMN),
   18794             :                  errmsg("column \"%s\" of relation \"%s\" does not exist",
   18795             :                         column, RelationGetRelationName(rel))));
   18796             : 
   18797             :     /* prevent them from altering a system attribute */
   18798          78 :     atttableform = (Form_pg_attribute) GETSTRUCT(tuple);
   18799          78 :     attnum = atttableform->attnum;
   18800          78 :     if (attnum <= 0)
   18801           0 :         ereport(ERROR,
   18802             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   18803             :                  errmsg("cannot alter system column \"%s\"", column)));
   18804             : 
   18805             :     /*
   18806             :      * Check that column type is compressible, then get the attribute
   18807             :      * compression method code
   18808             :      */
   18809          78 :     cmethod = GetAttributeCompression(atttableform->atttypid, compression);
   18810             : 
   18811             :     /* update pg_attribute entry */
   18812          72 :     atttableform->attcompression = cmethod;
   18813          72 :     CatalogTupleUpdate(attrel, &tuple->t_self, tuple);
   18814             : 
   18815          72 :     InvokeObjectPostAlterHook(RelationRelationId,
   18816             :                               RelationGetRelid(rel),
   18817             :                               attnum);
   18818             : 
   18819             :     /*
   18820             :      * Apply the change to indexes as well (only for simple index columns,
   18821             :      * matching behavior of index.c ConstructTupleDescriptor()).
   18822             :      */
   18823          72 :     SetIndexStorageProperties(rel, attrel, attnum,
   18824             :                               false, 0,
   18825             :                               true, cmethod,
   18826             :                               lockmode);
   18827             : 
   18828          72 :     heap_freetuple(tuple);
   18829             : 
   18830          72 :     table_close(attrel, RowExclusiveLock);
   18831             : 
   18832             :     /* make changes visible */
   18833          72 :     CommandCounterIncrement();
   18834             : 
   18835          72 :     ObjectAddressSubSet(address, RelationRelationId,
   18836             :                         RelationGetRelid(rel), attnum);
   18837          72 :     return address;
   18838             : }
   18839             : 
   18840             : 
   18841             : /*
   18842             :  * Preparation phase for SET LOGGED/UNLOGGED
   18843             :  *
   18844             :  * This verifies that we're not trying to change a temp table.  Also,
   18845             :  * existing foreign key constraints are checked to avoid ending up with
   18846             :  * permanent tables referencing unlogged tables.
   18847             :  */
   18848             : static void
   18849         100 : ATPrepChangePersistence(AlteredTableInfo *tab, Relation rel, bool toLogged)
   18850             : {
   18851             :     Relation    pg_constraint;
   18852             :     HeapTuple   tuple;
   18853             :     SysScanDesc scan;
   18854             :     ScanKeyData skey[1];
   18855             : 
   18856             :     /*
   18857             :      * Disallow changing status for a temp table.  Also verify whether we can
   18858             :      * get away with doing nothing; in such cases we don't need to run the
   18859             :      * checks below, either.
   18860             :      */
   18861         100 :     switch (rel->rd_rel->relpersistence)
   18862             :     {
   18863           0 :         case RELPERSISTENCE_TEMP:
   18864           0 :             ereport(ERROR,
   18865             :                     (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
   18866             :                      errmsg("cannot change logged status of table \"%s\" because it is temporary",
   18867             :                             RelationGetRelationName(rel)),
   18868             :                      errtable(rel)));
   18869             :             break;
   18870          56 :         case RELPERSISTENCE_PERMANENT:
   18871          56 :             if (toLogged)
   18872             :                 /* nothing to do */
   18873          12 :                 return;
   18874          50 :             break;
   18875          44 :         case RELPERSISTENCE_UNLOGGED:
   18876          44 :             if (!toLogged)
   18877             :                 /* nothing to do */
   18878           6 :                 return;
   18879          38 :             break;
   18880             :     }
   18881             : 
   18882             :     /*
   18883             :      * Check that the table is not part of any publication when changing to
   18884             :      * UNLOGGED, as UNLOGGED tables can't be published.
   18885             :      */
   18886         138 :     if (!toLogged &&
   18887          50 :         GetRelationPublications(RelationGetRelid(rel)) != NIL)
   18888           0 :         ereport(ERROR,
   18889             :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
   18890             :                  errmsg("cannot change table \"%s\" to unlogged because it is part of a publication",
   18891             :                         RelationGetRelationName(rel)),
   18892             :                  errdetail("Unlogged relations cannot be replicated.")));
   18893             : 
   18894             :     /*
   18895             :      * Check existing foreign key constraints to preserve the invariant that
   18896             :      * permanent tables cannot reference unlogged ones.  Self-referencing
   18897             :      * foreign keys can safely be ignored.
   18898             :      */
   18899          88 :     pg_constraint = table_open(ConstraintRelationId, AccessShareLock);
   18900             : 
   18901             :     /*
   18902             :      * Scan conrelid if changing to permanent, else confrelid.  This also
   18903             :      * determines whether a useful index exists.
   18904             :      */
   18905          88 :     ScanKeyInit(&skey[0],
   18906             :                 toLogged ? Anum_pg_constraint_conrelid :
   18907             :                 Anum_pg_constraint_confrelid,
   18908             :                 BTEqualStrategyNumber, F_OIDEQ,
   18909             :                 ObjectIdGetDatum(RelationGetRelid(rel)));
   18910          88 :     scan = systable_beginscan(pg_constraint,
   18911             :                               toLogged ? ConstraintRelidTypidNameIndexId : InvalidOid,
   18912             :                               true, NULL, 1, skey);
   18913             : 
   18914         142 :     while (HeapTupleIsValid(tuple = systable_getnext(scan)))
   18915             :     {
   18916          66 :         Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tuple);
   18917             : 
   18918          66 :         if (con->contype == CONSTRAINT_FOREIGN)
   18919             :         {
   18920             :             Oid         foreignrelid;
   18921             :             Relation    foreignrel;
   18922             : 
   18923             :             /* the opposite end of what we used as scankey */
   18924          30 :             foreignrelid = toLogged ? con->confrelid : con->conrelid;
   18925             : 
   18926             :             /* ignore if self-referencing */
   18927          30 :             if (RelationGetRelid(rel) == foreignrelid)
   18928          12 :                 continue;
   18929             : 
   18930          18 :             foreignrel = relation_open(foreignrelid, AccessShareLock);
   18931             : 
   18932          18 :             if (toLogged)
   18933             :             {
   18934           6 :                 if (!RelationIsPermanent(foreignrel))
   18935           6 :                     ereport(ERROR,
   18936             :                             (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
   18937             :                              errmsg("could not change table \"%s\" to logged because it references unlogged table \"%s\"",
   18938             :                                     RelationGetRelationName(rel),
   18939             :                                     RelationGetRelationName(foreignrel)),
   18940             :                              errtableconstraint(rel, NameStr(con->conname))));
   18941             :             }
   18942             :             else
   18943             :             {
   18944          12 :                 if (RelationIsPermanent(foreignrel))
   18945           6 :                     ereport(ERROR,
   18946             :                             (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
   18947             :                              errmsg("could not change table \"%s\" to unlogged because it references logged table \"%s\"",
   18948             :                                     RelationGetRelationName(rel),
   18949             :                                     RelationGetRelationName(foreignrel)),
   18950             :                              errtableconstraint(rel, NameStr(con->conname))));
   18951             :             }
   18952             : 
   18953           6 :             relation_close(foreignrel, AccessShareLock);
   18954             :         }
   18955             :     }
   18956             : 
   18957          76 :     systable_endscan(scan);
   18958             : 
   18959          76 :     table_close(pg_constraint, AccessShareLock);
   18960             : 
   18961             :     /* force rewrite if necessary; see comment in ATRewriteTables */
   18962          76 :     tab->rewrite |= AT_REWRITE_ALTER_PERSISTENCE;
   18963          76 :     if (toLogged)
   18964          32 :         tab->newrelpersistence = RELPERSISTENCE_PERMANENT;
   18965             :     else
   18966          44 :         tab->newrelpersistence = RELPERSISTENCE_UNLOGGED;
   18967          76 :     tab->chgPersistence = true;
   18968             : }
   18969             : 
   18970             : /*
   18971             :  * Execute ALTER TABLE SET SCHEMA
   18972             :  */
   18973             : ObjectAddress
   18974         104 : AlterTableNamespace(AlterObjectSchemaStmt *stmt, Oid *oldschema)
   18975             : {
   18976             :     Relation    rel;
   18977             :     Oid         relid;
   18978             :     Oid         oldNspOid;
   18979             :     Oid         nspOid;
   18980             :     RangeVar   *newrv;
   18981             :     ObjectAddresses *objsMoved;
   18982             :     ObjectAddress myself;
   18983             : 
   18984         104 :     relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
   18985         104 :                                      stmt->missing_ok ? RVR_MISSING_OK : 0,
   18986             :                                      RangeVarCallbackForAlterRelation,
   18987             :                                      stmt);
   18988             : 
   18989         102 :     if (!OidIsValid(relid))
   18990             :     {
   18991          12 :         ereport(NOTICE,
   18992             :                 (errmsg("relation \"%s\" does not exist, skipping",
   18993             :                         stmt->relation->relname)));
   18994          12 :         return InvalidObjectAddress;
   18995             :     }
   18996             : 
   18997          90 :     rel = relation_open(relid, NoLock);
   18998             : 
   18999          90 :     oldNspOid = RelationGetNamespace(rel);
   19000             : 
   19001             :     /* If it's an owned sequence, disallow moving it by itself. */
   19002          90 :     if (rel->rd_rel->relkind == RELKIND_SEQUENCE)
   19003             :     {
   19004             :         Oid         tableId;
   19005             :         int32       colId;
   19006             : 
   19007          10 :         if (sequenceIsOwned(relid, DEPENDENCY_AUTO, &tableId, &colId) ||
   19008           2 :             sequenceIsOwned(relid, DEPENDENCY_INTERNAL, &tableId, &colId))
   19009           6 :             ereport(ERROR,
   19010             :                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   19011             :                      errmsg("cannot move an owned sequence into another schema"),
   19012             :                      errdetail("Sequence \"%s\" is linked to table \"%s\".",
   19013             :                                RelationGetRelationName(rel),
   19014             :                                get_rel_name(tableId))));
   19015             :     }
   19016             : 
   19017             :     /* Get and lock schema OID and check its permissions. */
   19018          84 :     newrv = makeRangeVar(stmt->newschema, RelationGetRelationName(rel), -1);
   19019          84 :     nspOid = RangeVarGetAndCheckCreationNamespace(newrv, NoLock, NULL);
   19020             : 
   19021             :     /* common checks on switching namespaces */
   19022          84 :     CheckSetNamespace(oldNspOid, nspOid);
   19023             : 
   19024          84 :     objsMoved = new_object_addresses();
   19025          84 :     AlterTableNamespaceInternal(rel, oldNspOid, nspOid, objsMoved);
   19026          84 :     free_object_addresses(objsMoved);
   19027             : 
   19028          84 :     ObjectAddressSet(myself, RelationRelationId, relid);
   19029             : 
   19030          84 :     if (oldschema)
   19031          84 :         *oldschema = oldNspOid;
   19032             : 
   19033             :     /* close rel, but keep lock until commit */
   19034          84 :     relation_close(rel, NoLock);
   19035             : 
   19036          84 :     return myself;
   19037             : }
   19038             : 
   19039             : /*
   19040             :  * The guts of relocating a table or materialized view to another namespace:
   19041             :  * besides moving the relation itself, its dependent objects are relocated to
   19042             :  * the new schema.
   19043             :  */
   19044             : void
   19045          86 : AlterTableNamespaceInternal(Relation rel, Oid oldNspOid, Oid nspOid,
   19046             :                             ObjectAddresses *objsMoved)
   19047             : {
   19048             :     Relation    classRel;
   19049             : 
   19050             :     Assert(objsMoved != NULL);
   19051             : 
   19052             :     /* OK, modify the pg_class row and pg_depend entry */
   19053          86 :     classRel = table_open(RelationRelationId, RowExclusiveLock);
   19054             : 
   19055          86 :     AlterRelationNamespaceInternal(classRel, RelationGetRelid(rel), oldNspOid,
   19056             :                                    nspOid, true, objsMoved);
   19057             : 
   19058             :     /* Fix the table's row type too, if it has one */
   19059          86 :     if (OidIsValid(rel->rd_rel->reltype))
   19060          84 :         AlterTypeNamespaceInternal(rel->rd_rel->reltype, nspOid,
   19061             :                                    false,   /* isImplicitArray */
   19062             :                                    false,   /* ignoreDependent */
   19063             :                                    false,   /* errorOnTableType */
   19064             :                                    objsMoved);
   19065             : 
   19066             :     /* Fix other dependent stuff */
   19067          86 :     AlterIndexNamespaces(classRel, rel, oldNspOid, nspOid, objsMoved);
   19068          86 :     AlterSeqNamespaces(classRel, rel, oldNspOid, nspOid,
   19069             :                        objsMoved, AccessExclusiveLock);
   19070          86 :     AlterConstraintNamespaces(RelationGetRelid(rel), oldNspOid, nspOid,
   19071             :                               false, objsMoved);
   19072             : 
   19073          86 :     table_close(classRel, RowExclusiveLock);
   19074          86 : }
   19075             : 
   19076             : /*
   19077             :  * The guts of relocating a relation to another namespace: fix the pg_class
   19078             :  * entry, and the pg_depend entry if any.  Caller must already have
   19079             :  * opened and write-locked pg_class.
   19080             :  */
   19081             : void
   19082         188 : AlterRelationNamespaceInternal(Relation classRel, Oid relOid,
   19083             :                                Oid oldNspOid, Oid newNspOid,
   19084             :                                bool hasDependEntry,
   19085             :                                ObjectAddresses *objsMoved)
   19086             : {
   19087             :     HeapTuple   classTup;
   19088             :     Form_pg_class classForm;
   19089             :     ObjectAddress thisobj;
   19090         188 :     bool        already_done = false;
   19091             : 
   19092             :     /* no rel lock for relkind=c so use LOCKTAG_TUPLE */
   19093         188 :     classTup = SearchSysCacheLockedCopy1(RELOID, ObjectIdGetDatum(relOid));
   19094         188 :     if (!HeapTupleIsValid(classTup))
   19095           0 :         elog(ERROR, "cache lookup failed for relation %u", relOid);
   19096         188 :     classForm = (Form_pg_class) GETSTRUCT(classTup);
   19097             : 
   19098             :     Assert(classForm->relnamespace == oldNspOid);
   19099             : 
   19100         188 :     thisobj.classId = RelationRelationId;
   19101         188 :     thisobj.objectId = relOid;
   19102         188 :     thisobj.objectSubId = 0;
   19103             : 
   19104             :     /*
   19105             :      * If the object has already been moved, don't move it again.  If it's
   19106             :      * already in the right place, don't move it, but still fire the object
   19107             :      * access hook.
   19108             :      */
   19109         188 :     already_done = object_address_present(&thisobj, objsMoved);
   19110         188 :     if (!already_done && oldNspOid != newNspOid)
   19111         146 :     {
   19112         146 :         ItemPointerData otid = classTup->t_self;
   19113             : 
   19114             :         /* check for duplicate name (more friendly than unique-index failure) */
   19115         146 :         if (get_relname_relid(NameStr(classForm->relname),
   19116             :                               newNspOid) != InvalidOid)
   19117           0 :             ereport(ERROR,
   19118             :                     (errcode(ERRCODE_DUPLICATE_TABLE),
   19119             :                      errmsg("relation \"%s\" already exists in schema \"%s\"",
   19120             :                             NameStr(classForm->relname),
   19121             :                             get_namespace_name(newNspOid))));
   19122             : 
   19123             :         /* classTup is a copy, so OK to scribble on */
   19124         146 :         classForm->relnamespace = newNspOid;
   19125             : 
   19126         146 :         CatalogTupleUpdate(classRel, &otid, classTup);
   19127         146 :         UnlockTuple(classRel, &otid, InplaceUpdateTupleLock);
   19128             : 
   19129             : 
   19130             :         /* Update dependency on schema if caller said so */
   19131         250 :         if (hasDependEntry &&
   19132         104 :             changeDependencyFor(RelationRelationId,
   19133             :                                 relOid,
   19134             :                                 NamespaceRelationId,
   19135             :                                 oldNspOid,
   19136             :                                 newNspOid) != 1)
   19137           0 :             elog(ERROR, "could not change schema dependency for relation \"%s\"",
   19138             :                  NameStr(classForm->relname));
   19139             :     }
   19140             :     else
   19141          42 :         UnlockTuple(classRel, &classTup->t_self, InplaceUpdateTupleLock);
   19142         188 :     if (!already_done)
   19143             :     {
   19144         188 :         add_exact_object_address(&thisobj, objsMoved);
   19145             : 
   19146         188 :         InvokeObjectPostAlterHook(RelationRelationId, relOid, 0);
   19147             :     }
   19148             : 
   19149         188 :     heap_freetuple(classTup);
   19150         188 : }
   19151             : 
   19152             : /*
   19153             :  * Move all indexes for the specified relation to another namespace.
   19154             :  *
   19155             :  * Note: we assume adequate permission checking was done by the caller,
   19156             :  * and that the caller has a suitable lock on the owning relation.
   19157             :  */
   19158             : static void
   19159          86 : AlterIndexNamespaces(Relation classRel, Relation rel,
   19160             :                      Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved)
   19161             : {
   19162             :     List       *indexList;
   19163             :     ListCell   *l;
   19164             : 
   19165          86 :     indexList = RelationGetIndexList(rel);
   19166             : 
   19167         132 :     foreach(l, indexList)
   19168             :     {
   19169          46 :         Oid         indexOid = lfirst_oid(l);
   19170             :         ObjectAddress thisobj;
   19171             : 
   19172          46 :         thisobj.classId = RelationRelationId;
   19173          46 :         thisobj.objectId = indexOid;
   19174          46 :         thisobj.objectSubId = 0;
   19175             : 
   19176             :         /*
   19177             :          * Note: currently, the index will not have its own dependency on the
   19178             :          * namespace, so we don't need to do changeDependencyFor(). There's no
   19179             :          * row type in pg_type, either.
   19180             :          *
   19181             :          * XXX this objsMoved test may be pointless -- surely we have a single
   19182             :          * dependency link from a relation to each index?
   19183             :          */
   19184          46 :         if (!object_address_present(&thisobj, objsMoved))
   19185             :         {
   19186          46 :             AlterRelationNamespaceInternal(classRel, indexOid,
   19187             :                                            oldNspOid, newNspOid,
   19188             :                                            false, objsMoved);
   19189          46 :             add_exact_object_address(&thisobj, objsMoved);
   19190             :         }
   19191             :     }
   19192             : 
   19193          86 :     list_free(indexList);
   19194          86 : }
   19195             : 
   19196             : /*
   19197             :  * Move all identity and SERIAL-column sequences of the specified relation to another
   19198             :  * namespace.
   19199             :  *
   19200             :  * Note: we assume adequate permission checking was done by the caller,
   19201             :  * and that the caller has a suitable lock on the owning relation.
   19202             :  */
   19203             : static void
   19204          86 : AlterSeqNamespaces(Relation classRel, Relation rel,
   19205             :                    Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved,
   19206             :                    LOCKMODE lockmode)
   19207             : {
   19208             :     Relation    depRel;
   19209             :     SysScanDesc scan;
   19210             :     ScanKeyData key[2];
   19211             :     HeapTuple   tup;
   19212             : 
   19213             :     /*
   19214             :      * SERIAL sequences are those having an auto dependency on one of the
   19215             :      * table's columns (we don't care *which* column, exactly).
   19216             :      */
   19217          86 :     depRel = table_open(DependRelationId, AccessShareLock);
   19218             : 
   19219          86 :     ScanKeyInit(&key[0],
   19220             :                 Anum_pg_depend_refclassid,
   19221             :                 BTEqualStrategyNumber, F_OIDEQ,
   19222             :                 ObjectIdGetDatum(RelationRelationId));
   19223          86 :     ScanKeyInit(&key[1],
   19224             :                 Anum_pg_depend_refobjid,
   19225             :                 BTEqualStrategyNumber, F_OIDEQ,
   19226             :                 ObjectIdGetDatum(RelationGetRelid(rel)));
   19227             :     /* we leave refobjsubid unspecified */
   19228             : 
   19229          86 :     scan = systable_beginscan(depRel, DependReferenceIndexId, true,
   19230             :                               NULL, 2, key);
   19231             : 
   19232         616 :     while (HeapTupleIsValid(tup = systable_getnext(scan)))
   19233             :     {
   19234         530 :         Form_pg_depend depForm = (Form_pg_depend) GETSTRUCT(tup);
   19235             :         Relation    seqRel;
   19236             : 
   19237             :         /* skip dependencies other than auto dependencies on columns */
   19238         530 :         if (depForm->refobjsubid == 0 ||
   19239         382 :             depForm->classid != RelationRelationId ||
   19240          42 :             depForm->objsubid != 0 ||
   19241          42 :             !(depForm->deptype == DEPENDENCY_AUTO || depForm->deptype == DEPENDENCY_INTERNAL))
   19242         488 :             continue;
   19243             : 
   19244             :         /* Use relation_open just in case it's an index */
   19245          42 :         seqRel = relation_open(depForm->objid, lockmode);
   19246             : 
   19247             :         /* skip non-sequence relations */
   19248          42 :         if (RelationGetForm(seqRel)->relkind != RELKIND_SEQUENCE)
   19249             :         {
   19250             :             /* No need to keep the lock */
   19251           0 :             relation_close(seqRel, lockmode);
   19252           0 :             continue;
   19253             :         }
   19254             : 
   19255             :         /* Fix the pg_class and pg_depend entries */
   19256          42 :         AlterRelationNamespaceInternal(classRel, depForm->objid,
   19257             :                                        oldNspOid, newNspOid,
   19258             :                                        true, objsMoved);
   19259             : 
   19260             :         /*
   19261             :          * Sequences used to have entries in pg_type, but no longer do.  If we
   19262             :          * ever re-instate that, we'll need to move the pg_type entry to the
   19263             :          * new namespace, too (using AlterTypeNamespaceInternal).
   19264             :          */
   19265             :         Assert(RelationGetForm(seqRel)->reltype == InvalidOid);
   19266             : 
   19267             :         /* Now we can close it.  Keep the lock till end of transaction. */
   19268          42 :         relation_close(seqRel, NoLock);
   19269             :     }
   19270             : 
   19271          86 :     systable_endscan(scan);
   19272             : 
   19273          86 :     relation_close(depRel, AccessShareLock);
   19274          86 : }
   19275             : 
   19276             : 
   19277             : /*
   19278             :  * This code supports
   19279             :  *  CREATE TEMP TABLE ... ON COMMIT { DROP | PRESERVE ROWS | DELETE ROWS }
   19280             :  *
   19281             :  * Because we only support this for TEMP tables, it's sufficient to remember
   19282             :  * the state in a backend-local data structure.
   19283             :  */
   19284             : 
   19285             : /*
   19286             :  * Register a newly-created relation's ON COMMIT action.
   19287             :  */
   19288             : void
   19289         182 : register_on_commit_action(Oid relid, OnCommitAction action)
   19290             : {
   19291             :     OnCommitItem *oc;
   19292             :     MemoryContext oldcxt;
   19293             : 
   19294             :     /*
   19295             :      * We needn't bother registering the relation unless there is an ON COMMIT
   19296             :      * action we need to take.
   19297             :      */
   19298         182 :     if (action == ONCOMMIT_NOOP || action == ONCOMMIT_PRESERVE_ROWS)
   19299          24 :         return;
   19300             : 
   19301         158 :     oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
   19302             : 
   19303         158 :     oc = palloc_object(OnCommitItem);
   19304         158 :     oc->relid = relid;
   19305         158 :     oc->oncommit = action;
   19306         158 :     oc->creating_subid = GetCurrentSubTransactionId();
   19307         158 :     oc->deleting_subid = InvalidSubTransactionId;
   19308             : 
   19309             :     /*
   19310             :      * We use lcons() here so that ON COMMIT actions are processed in reverse
   19311             :      * order of registration.  That might not be essential but it seems
   19312             :      * reasonable.
   19313             :      */
   19314         158 :     on_commits = lcons(oc, on_commits);
   19315             : 
   19316         158 :     MemoryContextSwitchTo(oldcxt);
   19317             : }
   19318             : 
   19319             : /*
   19320             :  * Unregister any ON COMMIT action when a relation is deleted.
   19321             :  *
   19322             :  * Actually, we only mark the OnCommitItem entry as to be deleted after commit.
   19323             :  */
   19324             : void
   19325       51692 : remove_on_commit_action(Oid relid)
   19326             : {
   19327             :     ListCell   *l;
   19328             : 
   19329       51862 :     foreach(l, on_commits)
   19330             :     {
   19331         310 :         OnCommitItem *oc = (OnCommitItem *) lfirst(l);
   19332             : 
   19333         310 :         if (oc->relid == relid)
   19334             :         {
   19335         140 :             oc->deleting_subid = GetCurrentSubTransactionId();
   19336         140 :             break;
   19337             :         }
   19338             :     }
   19339       51692 : }
   19340             : 
   19341             : /*
   19342             :  * Perform ON COMMIT actions.
   19343             :  *
   19344             :  * This is invoked just before actually committing, since it's possible
   19345             :  * to encounter errors.
   19346             :  */
   19347             : void
   19348      954570 : PreCommit_on_commit_actions(void)
   19349             : {
   19350             :     ListCell   *l;
   19351      954570 :     List       *oids_to_truncate = NIL;
   19352      954570 :     List       *oids_to_drop = NIL;
   19353             : 
   19354      955392 :     foreach(l, on_commits)
   19355             :     {
   19356         822 :         OnCommitItem *oc = (OnCommitItem *) lfirst(l);
   19357             : 
   19358             :         /* Ignore entry if already dropped in this xact */
   19359         822 :         if (oc->deleting_subid != InvalidSubTransactionId)
   19360          74 :             continue;
   19361             : 
   19362         748 :         switch (oc->oncommit)
   19363             :         {
   19364           0 :             case ONCOMMIT_NOOP:
   19365             :             case ONCOMMIT_PRESERVE_ROWS:
   19366             :                 /* Do nothing (there shouldn't be such entries, actually) */
   19367           0 :                 break;
   19368         694 :             case ONCOMMIT_DELETE_ROWS:
   19369             : 
   19370             :                 /*
   19371             :                  * If this transaction hasn't accessed any temporary
   19372             :                  * relations, we can skip truncating ON COMMIT DELETE ROWS
   19373             :                  * tables, as they must still be empty.
   19374             :                  */
   19375         694 :                 if ((MyXactFlags & XACT_FLAGS_ACCESSEDTEMPNAMESPACE))
   19376         448 :                     oids_to_truncate = lappend_oid(oids_to_truncate, oc->relid);
   19377         694 :                 break;
   19378          54 :             case ONCOMMIT_DROP:
   19379          54 :                 oids_to_drop = lappend_oid(oids_to_drop, oc->relid);
   19380          54 :                 break;
   19381             :         }
   19382             :     }
   19383             : 
   19384             :     /*
   19385             :      * Truncate relations before dropping so that all dependencies between
   19386             :      * relations are removed after they are worked on.  Doing it like this
   19387             :      * might be a waste as it is possible that a relation being truncated will
   19388             :      * be dropped anyway due to its parent being dropped, but this makes the
   19389             :      * code more robust because of not having to re-check that the relation
   19390             :      * exists at truncation time.
   19391             :      */
   19392      954570 :     if (oids_to_truncate != NIL)
   19393         382 :         heap_truncate(oids_to_truncate);
   19394             : 
   19395      954564 :     if (oids_to_drop != NIL)
   19396             :     {
   19397          48 :         ObjectAddresses *targetObjects = new_object_addresses();
   19398             : 
   19399         102 :         foreach(l, oids_to_drop)
   19400             :         {
   19401             :             ObjectAddress object;
   19402             : 
   19403          54 :             object.classId = RelationRelationId;
   19404          54 :             object.objectId = lfirst_oid(l);
   19405          54 :             object.objectSubId = 0;
   19406             : 
   19407             :             Assert(!object_address_present(&object, targetObjects));
   19408             : 
   19409          54 :             add_exact_object_address(&object, targetObjects);
   19410             :         }
   19411             : 
   19412             :         /*
   19413             :          * Object deletion might involve toast table access (to clean up
   19414             :          * toasted catalog entries), so ensure we have a valid snapshot.
   19415             :          */
   19416          48 :         PushActiveSnapshot(GetTransactionSnapshot());
   19417             : 
   19418             :         /*
   19419             :          * Since this is an automatic drop, rather than one directly initiated
   19420             :          * by the user, we pass the PERFORM_DELETION_INTERNAL flag.
   19421             :          */
   19422          48 :         performMultipleDeletions(targetObjects, DROP_CASCADE,
   19423             :                                  PERFORM_DELETION_INTERNAL | PERFORM_DELETION_QUIETLY);
   19424             : 
   19425          48 :         PopActiveSnapshot();
   19426             : 
   19427             : #ifdef USE_ASSERT_CHECKING
   19428             : 
   19429             :         /*
   19430             :          * Note that table deletion will call remove_on_commit_action, so the
   19431             :          * entry should get marked as deleted.
   19432             :          */
   19433             :         foreach(l, on_commits)
   19434             :         {
   19435             :             OnCommitItem *oc = (OnCommitItem *) lfirst(l);
   19436             : 
   19437             :             if (oc->oncommit != ONCOMMIT_DROP)
   19438             :                 continue;
   19439             : 
   19440             :             Assert(oc->deleting_subid != InvalidSubTransactionId);
   19441             :         }
   19442             : #endif
   19443             :     }
   19444      954564 : }
   19445             : 
   19446             : /*
   19447             :  * Post-commit or post-abort cleanup for ON COMMIT management.
   19448             :  *
   19449             :  * All we do here is remove no-longer-needed OnCommitItem entries.
   19450             :  *
   19451             :  * During commit, remove entries that were deleted during this transaction;
   19452             :  * during abort, remove those created during this transaction.
   19453             :  */
   19454             : void
   19455     1006222 : AtEOXact_on_commit_actions(bool isCommit)
   19456             : {
   19457             :     ListCell   *cur_item;
   19458             : 
   19459     1007080 :     foreach(cur_item, on_commits)
   19460             :     {
   19461         858 :         OnCommitItem *oc = (OnCommitItem *) lfirst(cur_item);
   19462             : 
   19463         966 :         if (isCommit ? oc->deleting_subid != InvalidSubTransactionId :
   19464         108 :             oc->creating_subid != InvalidSubTransactionId)
   19465             :         {
   19466             :             /* cur_item must be removed */
   19467         158 :             on_commits = foreach_delete_current(on_commits, cur_item);
   19468         158 :             pfree(oc);
   19469             :         }
   19470             :         else
   19471             :         {
   19472             :             /* cur_item must be preserved */
   19473         700 :             oc->creating_subid = InvalidSubTransactionId;
   19474         700 :             oc->deleting_subid = InvalidSubTransactionId;
   19475             :         }
   19476             :     }
   19477     1006222 : }
   19478             : 
   19479             : /*
   19480             :  * Post-subcommit or post-subabort cleanup for ON COMMIT management.
   19481             :  *
   19482             :  * During subabort, we can immediately remove entries created during this
   19483             :  * subtransaction.  During subcommit, just relabel entries marked during
   19484             :  * this subtransaction as being the parent's responsibility.
   19485             :  */
   19486             : void
   19487       20156 : AtEOSubXact_on_commit_actions(bool isCommit, SubTransactionId mySubid,
   19488             :                               SubTransactionId parentSubid)
   19489             : {
   19490             :     ListCell   *cur_item;
   19491             : 
   19492       20156 :     foreach(cur_item, on_commits)
   19493             :     {
   19494           0 :         OnCommitItem *oc = (OnCommitItem *) lfirst(cur_item);
   19495             : 
   19496           0 :         if (!isCommit && oc->creating_subid == mySubid)
   19497             :         {
   19498             :             /* cur_item must be removed */
   19499           0 :             on_commits = foreach_delete_current(on_commits, cur_item);
   19500           0 :             pfree(oc);
   19501             :         }
   19502             :         else
   19503             :         {
   19504             :             /* cur_item must be preserved */
   19505           0 :             if (oc->creating_subid == mySubid)
   19506           0 :                 oc->creating_subid = parentSubid;
   19507           0 :             if (oc->deleting_subid == mySubid)
   19508           0 :                 oc->deleting_subid = isCommit ? parentSubid : InvalidSubTransactionId;
   19509             :         }
   19510             :     }
   19511       20156 : }
   19512             : 
   19513             : /*
   19514             :  * This is intended as a callback for RangeVarGetRelidExtended().  It allows
   19515             :  * the relation to be locked only if (1) it's a plain or partitioned table,
   19516             :  * materialized view, or TOAST table and (2) the current user is the owner (or
   19517             :  * the superuser) or has been granted MAINTAIN.  This meets the
   19518             :  * permission-checking needs of CLUSTER, REINDEX TABLE, and REFRESH
   19519             :  * MATERIALIZED VIEW; we expose it here so that it can be used by all.
   19520             :  */
   19521             : void
   19522        1030 : RangeVarCallbackMaintainsTable(const RangeVar *relation,
   19523             :                                Oid relId, Oid oldRelId, void *arg)
   19524             : {
   19525             :     char        relkind;
   19526             :     AclResult   aclresult;
   19527             : 
   19528             :     /* Nothing to do if the relation was not found. */
   19529        1030 :     if (!OidIsValid(relId))
   19530           6 :         return;
   19531             : 
   19532             :     /*
   19533             :      * If the relation does exist, check whether it's an index.  But note that
   19534             :      * the relation might have been dropped between the time we did the name
   19535             :      * lookup and now.  In that case, there's nothing to do.
   19536             :      */
   19537        1024 :     relkind = get_rel_relkind(relId);
   19538        1024 :     if (!relkind)
   19539           0 :         return;
   19540        1024 :     if (relkind != RELKIND_RELATION && relkind != RELKIND_TOASTVALUE &&
   19541         140 :         relkind != RELKIND_MATVIEW && relkind != RELKIND_PARTITIONED_TABLE)
   19542          28 :         ereport(ERROR,
   19543             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   19544             :                  errmsg("\"%s\" is not a table or materialized view", relation->relname)));
   19545             : 
   19546             :     /* Check permissions */
   19547         996 :     aclresult = pg_class_aclcheck(relId, GetUserId(), ACL_MAINTAIN);
   19548         996 :     if (aclresult != ACLCHECK_OK)
   19549          30 :         aclcheck_error(aclresult,
   19550          30 :                        get_relkind_objtype(get_rel_relkind(relId)),
   19551          30 :                        relation->relname);
   19552             : }
   19553             : 
   19554             : /*
   19555             :  * Callback to RangeVarGetRelidExtended() for TRUNCATE processing.
   19556             :  */
   19557             : static void
   19558        2202 : RangeVarCallbackForTruncate(const RangeVar *relation,
   19559             :                             Oid relId, Oid oldRelId, void *arg)
   19560             : {
   19561             :     HeapTuple   tuple;
   19562             : 
   19563             :     /* Nothing to do if the relation was not found. */
   19564        2202 :     if (!OidIsValid(relId))
   19565           0 :         return;
   19566             : 
   19567        2202 :     tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relId));
   19568        2202 :     if (!HeapTupleIsValid(tuple))   /* should not happen */
   19569           0 :         elog(ERROR, "cache lookup failed for relation %u", relId);
   19570             : 
   19571        2202 :     truncate_check_rel(relId, (Form_pg_class) GETSTRUCT(tuple));
   19572        2196 :     truncate_check_perms(relId, (Form_pg_class) GETSTRUCT(tuple));
   19573             : 
   19574        2164 :     ReleaseSysCache(tuple);
   19575             : }
   19576             : 
   19577             : /*
   19578             :  * Callback for RangeVarGetRelidExtended().  Checks that the current user is
   19579             :  * the owner of the relation, or superuser.
   19580             :  */
   19581             : void
   19582       17390 : RangeVarCallbackOwnsRelation(const RangeVar *relation,
   19583             :                              Oid relId, Oid oldRelId, void *arg)
   19584             : {
   19585             :     HeapTuple   tuple;
   19586             : 
   19587             :     /* Nothing to do if the relation was not found. */
   19588       17390 :     if (!OidIsValid(relId))
   19589          14 :         return;
   19590             : 
   19591       17376 :     tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relId));
   19592       17376 :     if (!HeapTupleIsValid(tuple))   /* should not happen */
   19593           0 :         elog(ERROR, "cache lookup failed for relation %u", relId);
   19594             : 
   19595       17376 :     if (!object_ownercheck(RelationRelationId, relId, GetUserId()))
   19596          24 :         aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relId)),
   19597          24 :                        relation->relname);
   19598             : 
   19599       34584 :     if (!allowSystemTableMods &&
   19600       17232 :         IsSystemClass(relId, (Form_pg_class) GETSTRUCT(tuple)))
   19601           2 :         ereport(ERROR,
   19602             :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
   19603             :                  errmsg("permission denied: \"%s\" is a system catalog",
   19604             :                         relation->relname)));
   19605             : 
   19606       17350 :     ReleaseSysCache(tuple);
   19607             : }
   19608             : 
   19609             : /*
   19610             :  * Common RangeVarGetRelid callback for rename, set schema, and alter table
   19611             :  * processing.
   19612             :  */
   19613             : static void
   19614       35320 : RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid, Oid oldrelid,
   19615             :                                  void *arg)
   19616             : {
   19617       35320 :     Node       *stmt = (Node *) arg;
   19618             :     ObjectType  reltype;
   19619             :     HeapTuple   tuple;
   19620             :     Form_pg_class classform;
   19621             :     AclResult   aclresult;
   19622             :     char        relkind;
   19623             : 
   19624       35320 :     tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
   19625       35320 :     if (!HeapTupleIsValid(tuple))
   19626         212 :         return;                 /* concurrently dropped */
   19627       35108 :     classform = (Form_pg_class) GETSTRUCT(tuple);
   19628       35108 :     relkind = classform->relkind;
   19629             : 
   19630             :     /* Must own relation. */
   19631       35108 :     if (!object_ownercheck(RelationRelationId, relid, GetUserId()))
   19632          72 :         aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relid)), rv->relname);
   19633             : 
   19634             :     /* No system table modifications unless explicitly allowed. */
   19635       35036 :     if (!allowSystemTableMods && IsSystemClass(relid, classform))
   19636          30 :         ereport(ERROR,
   19637             :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
   19638             :                  errmsg("permission denied: \"%s\" is a system catalog",
   19639             :                         rv->relname)));
   19640             : 
   19641             :     /*
   19642             :      * Extract the specified relation type from the statement parse tree.
   19643             :      *
   19644             :      * Also, for ALTER .. RENAME, check permissions: the user must (still)
   19645             :      * have CREATE rights on the containing namespace.
   19646             :      */
   19647       35006 :     if (IsA(stmt, RenameStmt))
   19648             :     {
   19649         502 :         aclresult = object_aclcheck(NamespaceRelationId, classform->relnamespace,
   19650             :                                     GetUserId(), ACL_CREATE);
   19651         502 :         if (aclresult != ACLCHECK_OK)
   19652           0 :             aclcheck_error(aclresult, OBJECT_SCHEMA,
   19653           0 :                            get_namespace_name(classform->relnamespace));
   19654         502 :         reltype = ((RenameStmt *) stmt)->renameType;
   19655             :     }
   19656       34504 :     else if (IsA(stmt, AlterObjectSchemaStmt))
   19657          94 :         reltype = ((AlterObjectSchemaStmt *) stmt)->objectType;
   19658             : 
   19659       34410 :     else if (IsA(stmt, AlterTableStmt))
   19660       34410 :         reltype = ((AlterTableStmt *) stmt)->objtype;
   19661             :     else
   19662             :     {
   19663           0 :         elog(ERROR, "unrecognized node type: %d", (int) nodeTag(stmt));
   19664             :         reltype = OBJECT_TABLE; /* placate compiler */
   19665             :     }
   19666             : 
   19667             :     /*
   19668             :      * For compatibility with prior releases, we allow ALTER TABLE to be used
   19669             :      * with most other types of relations (but not composite types). We allow
   19670             :      * similar flexibility for ALTER INDEX in the case of RENAME, but not
   19671             :      * otherwise.  Otherwise, the user must select the correct form of the
   19672             :      * command for the relation at issue.
   19673             :      */
   19674       35006 :     if (reltype == OBJECT_SEQUENCE && relkind != RELKIND_SEQUENCE)
   19675           0 :         ereport(ERROR,
   19676             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   19677             :                  errmsg("\"%s\" is not a sequence", rv->relname)));
   19678             : 
   19679       35006 :     if (reltype == OBJECT_VIEW && relkind != RELKIND_VIEW)
   19680           0 :         ereport(ERROR,
   19681             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   19682             :                  errmsg("\"%s\" is not a view", rv->relname)));
   19683             : 
   19684       35006 :     if (reltype == OBJECT_MATVIEW && relkind != RELKIND_MATVIEW)
   19685           0 :         ereport(ERROR,
   19686             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   19687             :                  errmsg("\"%s\" is not a materialized view", rv->relname)));
   19688             : 
   19689       35006 :     if (reltype == OBJECT_FOREIGN_TABLE && relkind != RELKIND_FOREIGN_TABLE)
   19690           0 :         ereport(ERROR,
   19691             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   19692             :                  errmsg("\"%s\" is not a foreign table", rv->relname)));
   19693             : 
   19694       35006 :     if (reltype == OBJECT_TYPE && relkind != RELKIND_COMPOSITE_TYPE)
   19695           0 :         ereport(ERROR,
   19696             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   19697             :                  errmsg("\"%s\" is not a composite type", rv->relname)));
   19698             : 
   19699       35006 :     if (reltype == OBJECT_INDEX && relkind != RELKIND_INDEX &&
   19700             :         relkind != RELKIND_PARTITIONED_INDEX
   19701          42 :         && !IsA(stmt, RenameStmt))
   19702           6 :         ereport(ERROR,
   19703             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   19704             :                  errmsg("\"%s\" is not an index", rv->relname)));
   19705             : 
   19706             :     /*
   19707             :      * Don't allow ALTER TABLE on composite types. We want people to use ALTER
   19708             :      * TYPE for that.
   19709             :      */
   19710       35000 :     if (reltype != OBJECT_TYPE && relkind == RELKIND_COMPOSITE_TYPE)
   19711           0 :         ereport(ERROR,
   19712             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   19713             :                  errmsg("\"%s\" is a composite type", rv->relname),
   19714             :         /* translator: %s is an SQL ALTER command */
   19715             :                  errhint("Use %s instead.",
   19716             :                          "ALTER TYPE")));
   19717             : 
   19718             :     /*
   19719             :      * Don't allow ALTER TABLE .. SET SCHEMA on relations that can't be moved
   19720             :      * to a different schema, such as indexes and TOAST tables.
   19721             :      */
   19722       35000 :     if (IsA(stmt, AlterObjectSchemaStmt))
   19723             :     {
   19724          94 :         if (relkind == RELKIND_INDEX || relkind == RELKIND_PARTITIONED_INDEX)
   19725           0 :             ereport(ERROR,
   19726             :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   19727             :                      errmsg("cannot change schema of index \"%s\"",
   19728             :                             rv->relname),
   19729             :                      errhint("Change the schema of the table instead.")));
   19730          94 :         else if (relkind == RELKIND_COMPOSITE_TYPE)
   19731           0 :             ereport(ERROR,
   19732             :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   19733             :                      errmsg("cannot change schema of composite type \"%s\"",
   19734             :                             rv->relname),
   19735             :             /* translator: %s is an SQL ALTER command */
   19736             :                      errhint("Use %s instead.",
   19737             :                              "ALTER TYPE")));
   19738          94 :         else if (relkind == RELKIND_TOASTVALUE)
   19739           0 :             ereport(ERROR,
   19740             :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   19741             :                      errmsg("cannot change schema of TOAST table \"%s\"",
   19742             :                             rv->relname),
   19743             :                      errhint("Change the schema of the table instead.")));
   19744             :     }
   19745             : 
   19746       35000 :     ReleaseSysCache(tuple);
   19747             : }
   19748             : 
   19749             : /*
   19750             :  * Transform any expressions present in the partition key
   19751             :  *
   19752             :  * Returns a transformed PartitionSpec.
   19753             :  */
   19754             : static PartitionSpec *
   19755        5544 : transformPartitionSpec(Relation rel, PartitionSpec *partspec)
   19756             : {
   19757             :     PartitionSpec *newspec;
   19758             :     ParseState *pstate;
   19759             :     ParseNamespaceItem *nsitem;
   19760             :     ListCell   *l;
   19761             : 
   19762        5544 :     newspec = makeNode(PartitionSpec);
   19763             : 
   19764        5544 :     newspec->strategy = partspec->strategy;
   19765        5544 :     newspec->partParams = NIL;
   19766        5544 :     newspec->location = partspec->location;
   19767             : 
   19768             :     /* Check valid number of columns for strategy */
   19769        8126 :     if (partspec->strategy == PARTITION_STRATEGY_LIST &&
   19770        2582 :         list_length(partspec->partParams) != 1)
   19771           6 :         ereport(ERROR,
   19772             :                 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
   19773             :                  errmsg("cannot use \"list\" partition strategy with more than one column")));
   19774             : 
   19775             :     /*
   19776             :      * Create a dummy ParseState and insert the target relation as its sole
   19777             :      * rangetable entry.  We need a ParseState for transformExpr.
   19778             :      */
   19779        5538 :     pstate = make_parsestate(NULL);
   19780        5538 :     nsitem = addRangeTableEntryForRelation(pstate, rel, AccessShareLock,
   19781             :                                            NULL, false, true);
   19782        5538 :     addNSItemToQuery(pstate, nsitem, true, true, true);
   19783             : 
   19784             :     /* take care of any partition expressions */
   19785       11532 :     foreach(l, partspec->partParams)
   19786             :     {
   19787        6018 :         PartitionElem *pelem = lfirst_node(PartitionElem, l);
   19788             : 
   19789        6018 :         if (pelem->expr)
   19790             :         {
   19791             :             /* Copy, to avoid scribbling on the input */
   19792         352 :             pelem = copyObject(pelem);
   19793             : 
   19794             :             /* Now do parse transformation of the expression */
   19795         352 :             pelem->expr = transformExpr(pstate, pelem->expr,
   19796             :                                         EXPR_KIND_PARTITION_EXPRESSION);
   19797             : 
   19798             :             /* we have to fix its collations too */
   19799         328 :             assign_expr_collations(pstate, pelem->expr);
   19800             :         }
   19801             : 
   19802        5994 :         newspec->partParams = lappend(newspec->partParams, pelem);
   19803             :     }
   19804             : 
   19805        5514 :     return newspec;
   19806             : }
   19807             : 
   19808             : /*
   19809             :  * Compute per-partition-column information from a list of PartitionElems.
   19810             :  * Expressions in the PartitionElems must be parse-analyzed already.
   19811             :  */
   19812             : static void
   19813        5514 : ComputePartitionAttrs(ParseState *pstate, Relation rel, List *partParams, AttrNumber *partattrs,
   19814             :                       List **partexprs, Oid *partopclass, Oid *partcollation,
   19815             :                       PartitionStrategy strategy)
   19816             : {
   19817             :     int         attn;
   19818             :     ListCell   *lc;
   19819             :     Oid         am_oid;
   19820             : 
   19821        5514 :     attn = 0;
   19822       11376 :     foreach(lc, partParams)
   19823             :     {
   19824        5994 :         PartitionElem *pelem = lfirst_node(PartitionElem, lc);
   19825             :         Oid         atttype;
   19826             :         Oid         attcollation;
   19827             : 
   19828        5994 :         if (pelem->name != NULL)
   19829             :         {
   19830             :             /* Simple attribute reference */
   19831             :             HeapTuple   atttuple;
   19832             :             Form_pg_attribute attform;
   19833             : 
   19834        5666 :             atttuple = SearchSysCacheAttName(RelationGetRelid(rel),
   19835        5666 :                                              pelem->name);
   19836        5666 :             if (!HeapTupleIsValid(atttuple))
   19837          12 :                 ereport(ERROR,
   19838             :                         (errcode(ERRCODE_UNDEFINED_COLUMN),
   19839             :                          errmsg("column \"%s\" named in partition key does not exist",
   19840             :                                 pelem->name),
   19841             :                          parser_errposition(pstate, pelem->location)));
   19842        5654 :             attform = (Form_pg_attribute) GETSTRUCT(atttuple);
   19843             : 
   19844        5654 :             if (attform->attnum <= 0)
   19845           6 :                 ereport(ERROR,
   19846             :                         (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
   19847             :                          errmsg("cannot use system column \"%s\" in partition key",
   19848             :                                 pelem->name),
   19849             :                          parser_errposition(pstate, pelem->location)));
   19850             : 
   19851             :             /*
   19852             :              * Stored generated columns cannot work: They are computed after
   19853             :              * BEFORE triggers, but partition routing is done before all
   19854             :              * triggers.  Maybe virtual generated columns could be made to
   19855             :              * work, but then they would need to be handled as an expression
   19856             :              * below.
   19857             :              */
   19858        5648 :             if (attform->attgenerated)
   19859          12 :                 ereport(ERROR,
   19860             :                         (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
   19861             :                          errmsg("cannot use generated column in partition key"),
   19862             :                          errdetail("Column \"%s\" is a generated column.",
   19863             :                                    pelem->name),
   19864             :                          parser_errposition(pstate, pelem->location)));
   19865             : 
   19866        5636 :             partattrs[attn] = attform->attnum;
   19867        5636 :             atttype = attform->atttypid;
   19868        5636 :             attcollation = attform->attcollation;
   19869        5636 :             ReleaseSysCache(atttuple);
   19870             :         }
   19871             :         else
   19872             :         {
   19873             :             /* Expression */
   19874         328 :             Node       *expr = pelem->expr;
   19875             :             char        partattname[16];
   19876         328 :             Bitmapset  *expr_attrs = NULL;
   19877             :             int         i;
   19878             : 
   19879             :             Assert(expr != NULL);
   19880         328 :             atttype = exprType(expr);
   19881         328 :             attcollation = exprCollation(expr);
   19882             : 
   19883             :             /*
   19884             :              * The expression must be of a storable type (e.g., not RECORD).
   19885             :              * The test is the same as for whether a table column is of a safe
   19886             :              * type (which is why we needn't check for the non-expression
   19887             :              * case).
   19888             :              */
   19889         328 :             snprintf(partattname, sizeof(partattname), "%d", attn + 1);
   19890         328 :             CheckAttributeType(partattname,
   19891             :                                atttype, attcollation,
   19892             :                                NIL, CHKATYPE_IS_PARTKEY);
   19893             : 
   19894             :             /*
   19895             :              * Strip any top-level COLLATE clause.  This ensures that we treat
   19896             :              * "x COLLATE y" and "(x COLLATE y)" alike.
   19897             :              */
   19898         316 :             while (IsA(expr, CollateExpr))
   19899           0 :                 expr = (Node *) ((CollateExpr *) expr)->arg;
   19900             : 
   19901             :             /*
   19902             :              * Examine all the columns in the partition key expression. When
   19903             :              * the whole-row reference is present, examine all the columns of
   19904             :              * the partitioned table.
   19905             :              */
   19906         316 :             pull_varattnos(expr, 1, &expr_attrs);
   19907         316 :             if (bms_is_member(0 - FirstLowInvalidHeapAttributeNumber, expr_attrs))
   19908             :             {
   19909          60 :                 expr_attrs = bms_add_range(expr_attrs,
   19910             :                                            1 - FirstLowInvalidHeapAttributeNumber,
   19911          30 :                                            RelationGetNumberOfAttributes(rel) - FirstLowInvalidHeapAttributeNumber);
   19912          30 :                 expr_attrs = bms_del_member(expr_attrs, 0 - FirstLowInvalidHeapAttributeNumber);
   19913             :             }
   19914             : 
   19915         316 :             i = -1;
   19916         696 :             while ((i = bms_next_member(expr_attrs, i)) >= 0)
   19917             :             {
   19918         428 :                 AttrNumber  attno = i + FirstLowInvalidHeapAttributeNumber;
   19919             : 
   19920             :                 Assert(attno != 0);
   19921             : 
   19922             :                 /*
   19923             :                  * Cannot allow system column references, since that would
   19924             :                  * make partition routing impossible: their values won't be
   19925             :                  * known yet when we need to do that.
   19926             :                  */
   19927         428 :                 if (attno < 0)
   19928           0 :                     ereport(ERROR,
   19929             :                             (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
   19930             :                              errmsg("partition key expressions cannot contain system column references")));
   19931             : 
   19932             :                 /*
   19933             :                  * Stored generated columns cannot work: They are computed
   19934             :                  * after BEFORE triggers, but partition routing is done before
   19935             :                  * all triggers.  Virtual generated columns could probably
   19936             :                  * work, but it would require more work elsewhere (for example
   19937             :                  * SET EXPRESSION would need to check whether the column is
   19938             :                  * used in partition keys).  Seems safer to prohibit for now.
   19939             :                  */
   19940         428 :                 if (TupleDescAttr(RelationGetDescr(rel), attno - 1)->attgenerated)
   19941          48 :                     ereport(ERROR,
   19942             :                             (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
   19943             :                              errmsg("cannot use generated column in partition key"),
   19944             :                              errdetail("Column \"%s\" is a generated column.",
   19945             :                                        get_attname(RelationGetRelid(rel), attno, false)),
   19946             :                              parser_errposition(pstate, pelem->location)));
   19947             :             }
   19948             : 
   19949         268 :             if (IsA(expr, Var) &&
   19950          12 :                 ((Var *) expr)->varattno > 0)
   19951             :             {
   19952             : 
   19953             :                 /*
   19954             :                  * User wrote "(column)" or "(column COLLATE something)".
   19955             :                  * Treat it like simple attribute anyway.
   19956             :                  */
   19957           6 :                 partattrs[attn] = ((Var *) expr)->varattno;
   19958             :             }
   19959             :             else
   19960             :             {
   19961         262 :                 partattrs[attn] = 0;    /* marks the column as expression */
   19962         262 :                 *partexprs = lappend(*partexprs, expr);
   19963             : 
   19964             :                 /*
   19965             :                  * transformPartitionSpec() should have already rejected
   19966             :                  * subqueries, aggregates, window functions, and SRFs, based
   19967             :                  * on the EXPR_KIND_ for partition expressions.
   19968             :                  */
   19969             : 
   19970             :                 /*
   19971             :                  * Preprocess the expression before checking for mutability.
   19972             :                  * This is essential for the reasons described in
   19973             :                  * contain_mutable_functions_after_planning.  However, we call
   19974             :                  * expression_planner for ourselves rather than using that
   19975             :                  * function, because if constant-folding reduces the
   19976             :                  * expression to a constant, we'd like to know that so we can
   19977             :                  * complain below.
   19978             :                  *
   19979             :                  * Like contain_mutable_functions_after_planning, assume that
   19980             :                  * expression_planner won't scribble on its input, so this
   19981             :                  * won't affect the partexprs entry we saved above.
   19982             :                  */
   19983         262 :                 expr = (Node *) expression_planner((Expr *) expr);
   19984             : 
   19985             :                 /*
   19986             :                  * Partition expressions cannot contain mutable functions,
   19987             :                  * because a given row must always map to the same partition
   19988             :                  * as long as there is no change in the partition boundary
   19989             :                  * structure.
   19990             :                  */
   19991         262 :                 if (contain_mutable_functions(expr))
   19992           6 :                     ereport(ERROR,
   19993             :                             (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
   19994             :                              errmsg("functions in partition key expression must be marked IMMUTABLE")));
   19995             : 
   19996             :                 /*
   19997             :                  * While it is not exactly *wrong* for a partition expression
   19998             :                  * to be a constant, it seems better to reject such keys.
   19999             :                  */
   20000         256 :                 if (IsA(expr, Const))
   20001          12 :                     ereport(ERROR,
   20002             :                             (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
   20003             :                              errmsg("cannot use constant expression as partition key")));
   20004             :             }
   20005             :         }
   20006             : 
   20007             :         /*
   20008             :          * Apply collation override if any
   20009             :          */
   20010        5886 :         if (pelem->collation)
   20011          54 :             attcollation = get_collation_oid(pelem->collation, false);
   20012             : 
   20013             :         /*
   20014             :          * Check we have a collation iff it's a collatable type.  The only
   20015             :          * expected failures here are (1) COLLATE applied to a noncollatable
   20016             :          * type, or (2) partition expression had an unresolved collation. But
   20017             :          * we might as well code this to be a complete consistency check.
   20018             :          */
   20019        5886 :         if (type_is_collatable(atttype))
   20020             :         {
   20021         660 :             if (!OidIsValid(attcollation))
   20022           0 :                 ereport(ERROR,
   20023             :                         (errcode(ERRCODE_INDETERMINATE_COLLATION),
   20024             :                          errmsg("could not determine which collation to use for partition expression"),
   20025             :                          errhint("Use the COLLATE clause to set the collation explicitly.")));
   20026             :         }
   20027             :         else
   20028             :         {
   20029        5226 :             if (OidIsValid(attcollation))
   20030           0 :                 ereport(ERROR,
   20031             :                         (errcode(ERRCODE_DATATYPE_MISMATCH),
   20032             :                          errmsg("collations are not supported by type %s",
   20033             :                                 format_type_be(atttype))));
   20034             :         }
   20035             : 
   20036        5886 :         partcollation[attn] = attcollation;
   20037             : 
   20038             :         /*
   20039             :          * Identify the appropriate operator class.  For list and range
   20040             :          * partitioning, we use a btree operator class; hash partitioning uses
   20041             :          * a hash operator class.
   20042             :          */
   20043        5886 :         if (strategy == PARTITION_STRATEGY_HASH)
   20044         332 :             am_oid = HASH_AM_OID;
   20045             :         else
   20046        5554 :             am_oid = BTREE_AM_OID;
   20047             : 
   20048        5886 :         if (!pelem->opclass)
   20049             :         {
   20050        5748 :             partopclass[attn] = GetDefaultOpClass(atttype, am_oid);
   20051             : 
   20052        5748 :             if (!OidIsValid(partopclass[attn]))
   20053             :             {
   20054          12 :                 if (strategy == PARTITION_STRATEGY_HASH)
   20055           0 :                     ereport(ERROR,
   20056             :                             (errcode(ERRCODE_UNDEFINED_OBJECT),
   20057             :                              errmsg("data type %s has no default operator class for access method \"%s\"",
   20058             :                                     format_type_be(atttype), "hash"),
   20059             :                              errhint("You must specify a hash operator class or define a default hash operator class for the data type.")));
   20060             :                 else
   20061          12 :                     ereport(ERROR,
   20062             :                             (errcode(ERRCODE_UNDEFINED_OBJECT),
   20063             :                              errmsg("data type %s has no default operator class for access method \"%s\"",
   20064             :                                     format_type_be(atttype), "btree"),
   20065             :                              errhint("You must specify a btree operator class or define a default btree operator class for the data type.")));
   20066             :             }
   20067             :         }
   20068             :         else
   20069         138 :             partopclass[attn] = ResolveOpClass(pelem->opclass,
   20070             :                                                atttype,
   20071             :                                                am_oid == HASH_AM_OID ? "hash" : "btree",
   20072             :                                                am_oid);
   20073             : 
   20074        5862 :         attn++;
   20075             :     }
   20076        5382 : }
   20077             : 
   20078             : /*
   20079             :  * PartConstraintImpliedByRelConstraint
   20080             :  *      Do scanrel's existing constraints imply the partition constraint?
   20081             :  *
   20082             :  * "Existing constraints" include its check constraints and column-level
   20083             :  * not-null constraints.  partConstraint describes the partition constraint,
   20084             :  * in implicit-AND form.
   20085             :  */
   20086             : bool
   20087        3220 : PartConstraintImpliedByRelConstraint(Relation scanrel,
   20088             :                                      List *partConstraint)
   20089             : {
   20090        3220 :     List       *existConstraint = NIL;
   20091        3220 :     TupleConstr *constr = RelationGetDescr(scanrel)->constr;
   20092             :     int         i;
   20093             : 
   20094        3220 :     if (constr && constr->has_not_null)
   20095             :     {
   20096         842 :         int         natts = scanrel->rd_att->natts;
   20097             : 
   20098        2838 :         for (i = 1; i <= natts; i++)
   20099             :         {
   20100        1996 :             CompactAttribute *att = TupleDescCompactAttr(scanrel->rd_att, i - 1);
   20101             : 
   20102             :             /* invalid not-null constraint must be ignored here */
   20103        1996 :             if (att->attnullability == ATTNULLABLE_VALID && !att->attisdropped)
   20104             :             {
   20105        1138 :                 Form_pg_attribute wholeatt = TupleDescAttr(scanrel->rd_att, i - 1);
   20106        1138 :                 NullTest   *ntest = makeNode(NullTest);
   20107             : 
   20108        1138 :                 ntest->arg = (Expr *) makeVar(1,
   20109             :                                               i,
   20110             :                                               wholeatt->atttypid,
   20111             :                                               wholeatt->atttypmod,
   20112             :                                               wholeatt->attcollation,
   20113             :                                               0);
   20114        1138 :                 ntest->nulltesttype = IS_NOT_NULL;
   20115             : 
   20116             :                 /*
   20117             :                  * argisrow=false is correct even for a composite column,
   20118             :                  * because attnotnull does not represent a SQL-spec IS NOT
   20119             :                  * NULL test in such a case, just IS DISTINCT FROM NULL.
   20120             :                  */
   20121        1138 :                 ntest->argisrow = false;
   20122        1138 :                 ntest->location = -1;
   20123        1138 :                 existConstraint = lappend(existConstraint, ntest);
   20124             :             }
   20125             :         }
   20126             :     }
   20127             : 
   20128        3220 :     return ConstraintImpliedByRelConstraint(scanrel, partConstraint, existConstraint);
   20129             : }
   20130             : 
   20131             : /*
   20132             :  * ConstraintImpliedByRelConstraint
   20133             :  *      Do scanrel's existing constraints imply the given constraint?
   20134             :  *
   20135             :  * testConstraint is the constraint to validate. provenConstraint is a
   20136             :  * caller-provided list of conditions which this function may assume
   20137             :  * to be true. Both provenConstraint and testConstraint must be in
   20138             :  * implicit-AND form, must only contain immutable clauses, and must
   20139             :  * contain only Vars with varno = 1.
   20140             :  */
   20141             : bool
   20142        4466 : ConstraintImpliedByRelConstraint(Relation scanrel, List *testConstraint, List *provenConstraint)
   20143             : {
   20144        4466 :     List       *existConstraint = list_copy(provenConstraint);
   20145        4466 :     TupleConstr *constr = RelationGetDescr(scanrel)->constr;
   20146             :     int         num_check,
   20147             :                 i;
   20148             : 
   20149        4466 :     num_check = (constr != NULL) ? constr->num_check : 0;
   20150        4974 :     for (i = 0; i < num_check; i++)
   20151             :     {
   20152             :         Node       *cexpr;
   20153             : 
   20154             :         /*
   20155             :          * If this constraint hasn't been fully validated yet, we must ignore
   20156             :          * it here.
   20157             :          */
   20158         508 :         if (!constr->check[i].ccvalid)
   20159           6 :             continue;
   20160             : 
   20161             :         /*
   20162             :          * NOT ENFORCED constraints are always marked as invalid, which should
   20163             :          * have been ignored.
   20164             :          */
   20165             :         Assert(constr->check[i].ccenforced);
   20166             : 
   20167         502 :         cexpr = stringToNode(constr->check[i].ccbin);
   20168             : 
   20169             :         /*
   20170             :          * Run each expression through const-simplification and
   20171             :          * canonicalization.  It is necessary, because we will be comparing it
   20172             :          * to similarly-processed partition constraint expressions, and may
   20173             :          * fail to detect valid matches without this.
   20174             :          */
   20175         502 :         cexpr = eval_const_expressions(NULL, cexpr);
   20176         502 :         cexpr = (Node *) canonicalize_qual((Expr *) cexpr, true);
   20177             : 
   20178         502 :         existConstraint = list_concat(existConstraint,
   20179         502 :                                       make_ands_implicit((Expr *) cexpr));
   20180             :     }
   20181             : 
   20182             :     /*
   20183             :      * Try to make the proof.  Since we are comparing CHECK constraints, we
   20184             :      * need to use weak implication, i.e., we assume existConstraint is
   20185             :      * not-false and try to prove the same for testConstraint.
   20186             :      *
   20187             :      * Note that predicate_implied_by assumes its first argument is known
   20188             :      * immutable.  That should always be true for both NOT NULL and partition
   20189             :      * constraints, so we don't test it here.
   20190             :      */
   20191        4466 :     return predicate_implied_by(testConstraint, existConstraint, true);
   20192             : }
   20193             : 
   20194             : /*
   20195             :  * QueuePartitionConstraintValidation
   20196             :  *
   20197             :  * Add an entry to wqueue to have the given partition constraint validated by
   20198             :  * Phase 3, for the given relation, and all its children.
   20199             :  *
   20200             :  * We first verify whether the given constraint is implied by pre-existing
   20201             :  * relation constraints; if it is, there's no need to scan the table to
   20202             :  * validate, so don't queue in that case.
   20203             :  */
   20204             : static void
   20205        2730 : QueuePartitionConstraintValidation(List **wqueue, Relation scanrel,
   20206             :                                    List *partConstraint,
   20207             :                                    bool validate_default)
   20208             : {
   20209             :     /*
   20210             :      * Based on the table's existing constraints, determine whether or not we
   20211             :      * may skip scanning the table.
   20212             :      */
   20213        2730 :     if (PartConstraintImpliedByRelConstraint(scanrel, partConstraint))
   20214             :     {
   20215          88 :         if (!validate_default)
   20216          66 :             ereport(DEBUG1,
   20217             :                     (errmsg_internal("partition constraint for table \"%s\" is implied by existing constraints",
   20218             :                                      RelationGetRelationName(scanrel))));
   20219             :         else
   20220          22 :             ereport(DEBUG1,
   20221             :                     (errmsg_internal("updated partition constraint for default partition \"%s\" is implied by existing constraints",
   20222             :                                      RelationGetRelationName(scanrel))));
   20223          88 :         return;
   20224             :     }
   20225             : 
   20226             :     /*
   20227             :      * Constraints proved insufficient. For plain relations, queue a
   20228             :      * validation item now; for partitioned tables, recurse to process each
   20229             :      * partition.
   20230             :      */
   20231        2642 :     if (scanrel->rd_rel->relkind == RELKIND_RELATION)
   20232             :     {
   20233             :         AlteredTableInfo *tab;
   20234             : 
   20235             :         /* Grab a work queue entry. */
   20236        2204 :         tab = ATGetQueueEntry(wqueue, scanrel);
   20237             :         Assert(tab->partition_constraint == NULL);
   20238        2204 :         tab->partition_constraint = (Expr *) linitial(partConstraint);
   20239        2204 :         tab->validate_default = validate_default;
   20240             :     }
   20241         438 :     else if (scanrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
   20242             :     {
   20243         386 :         PartitionDesc partdesc = RelationGetPartitionDesc(scanrel, true);
   20244             :         int         i;
   20245             : 
   20246         812 :         for (i = 0; i < partdesc->nparts; i++)
   20247             :         {
   20248             :             Relation    part_rel;
   20249             :             List       *thisPartConstraint;
   20250             : 
   20251             :             /*
   20252             :              * This is the minimum lock we need to prevent deadlocks.
   20253             :              */
   20254         426 :             part_rel = table_open(partdesc->oids[i], AccessExclusiveLock);
   20255             : 
   20256             :             /*
   20257             :              * Adjust the constraint for scanrel so that it matches this
   20258             :              * partition's attribute numbers.
   20259             :              */
   20260             :             thisPartConstraint =
   20261         426 :                 map_partition_varattnos(partConstraint, 1,
   20262             :                                         part_rel, scanrel);
   20263             : 
   20264         426 :             QueuePartitionConstraintValidation(wqueue, part_rel,
   20265             :                                                thisPartConstraint,
   20266             :                                                validate_default);
   20267         426 :             table_close(part_rel, NoLock);  /* keep lock till commit */
   20268             :         }
   20269             :     }
   20270             : }
   20271             : 
   20272             : /*
   20273             :  * attachPartitionTable: attach a new partition to the partitioned table
   20274             :  *
   20275             :  * wqueue: the ALTER TABLE work queue; can be NULL when not running as part
   20276             :  *   of an ALTER TABLE sequence.
   20277             :  * rel: partitioned relation;
   20278             :  * attachrel: relation of attached partition;
   20279             :  * bound: bounds of attached relation.
   20280             :  */
   20281             : static void
   20282        3016 : attachPartitionTable(List **wqueue, Relation rel, Relation attachrel, PartitionBoundSpec *bound)
   20283             : {
   20284             :     /*
   20285             :      * Create an inheritance; the relevant checks are performed inside the
   20286             :      * function.
   20287             :      */
   20288        3016 :     CreateInheritance(attachrel, rel, true);
   20289             : 
   20290             :     /* Update the pg_class entry. */
   20291        2908 :     StorePartitionBound(attachrel, rel, bound);
   20292             : 
   20293             :     /* Ensure there exists a correct set of indexes in the partition. */
   20294        2908 :     AttachPartitionEnsureIndexes(wqueue, rel, attachrel);
   20295             : 
   20296             :     /* and triggers */
   20297        2878 :     CloneRowTriggersToPartition(rel, attachrel);
   20298             : 
   20299             :     /*
   20300             :      * Clone foreign key constraints.  Callee is responsible for setting up
   20301             :      * for phase 3 constraint verification.
   20302             :      */
   20303        2872 :     CloneForeignKeyConstraints(wqueue, rel, attachrel);
   20304        2854 : }
   20305             : 
   20306             : /*
   20307             :  * ALTER TABLE <name> ATTACH PARTITION <partition-name> FOR VALUES
   20308             :  *
   20309             :  * Return the address of the newly attached partition.
   20310             :  */
   20311             : static ObjectAddress
   20312        2524 : ATExecAttachPartition(List **wqueue, Relation rel, PartitionCmd *cmd,
   20313             :                       AlterTableUtilityContext *context)
   20314             : {
   20315             :     Relation    attachrel,
   20316             :                 catalog;
   20317             :     List       *attachrel_children;
   20318             :     List       *partConstraint;
   20319             :     SysScanDesc scan;
   20320             :     ScanKeyData skey;
   20321             :     AttrNumber  attno;
   20322             :     int         natts;
   20323             :     TupleDesc   tupleDesc;
   20324             :     ObjectAddress address;
   20325             :     const char *trigger_name;
   20326             :     Oid         defaultPartOid;
   20327             :     List       *partBoundConstraint;
   20328        2524 :     ParseState *pstate = make_parsestate(NULL);
   20329             : 
   20330        2524 :     pstate->p_sourcetext = context->queryString;
   20331             : 
   20332             :     /*
   20333             :      * We must lock the default partition if one exists, because attaching a
   20334             :      * new partition will change its partition constraint.
   20335             :      */
   20336             :     defaultPartOid =
   20337        2524 :         get_default_oid_from_partdesc(RelationGetPartitionDesc(rel, true));
   20338        2524 :     if (OidIsValid(defaultPartOid))
   20339         182 :         LockRelationOid(defaultPartOid, AccessExclusiveLock);
   20340             : 
   20341        2524 :     attachrel = table_openrv(cmd->name, AccessExclusiveLock);
   20342             : 
   20343             :     /*
   20344             :      * XXX I think it'd be a good idea to grab locks on all tables referenced
   20345             :      * by FKs at this point also.
   20346             :      */
   20347             : 
   20348             :     /*
   20349             :      * Must be owner of both parent and source table -- parent was checked by
   20350             :      * ATSimplePermissions call in ATPrepCmd
   20351             :      */
   20352        2518 :     ATSimplePermissions(AT_AttachPartition, attachrel,
   20353             :                         ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
   20354             : 
   20355             :     /* A partition can only have one parent */
   20356        2512 :     if (attachrel->rd_rel->relispartition)
   20357           6 :         ereport(ERROR,
   20358             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   20359             :                  errmsg("\"%s\" is already a partition",
   20360             :                         RelationGetRelationName(attachrel))));
   20361             : 
   20362        2506 :     if (OidIsValid(attachrel->rd_rel->reloftype))
   20363           6 :         ereport(ERROR,
   20364             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   20365             :                  errmsg("cannot attach a typed table as partition")));
   20366             : 
   20367             :     /*
   20368             :      * Table being attached should not already be part of inheritance; either
   20369             :      * as a child table...
   20370             :      */
   20371        2500 :     catalog = table_open(InheritsRelationId, AccessShareLock);
   20372        2500 :     ScanKeyInit(&skey,
   20373             :                 Anum_pg_inherits_inhrelid,
   20374             :                 BTEqualStrategyNumber, F_OIDEQ,
   20375             :                 ObjectIdGetDatum(RelationGetRelid(attachrel)));
   20376        2500 :     scan = systable_beginscan(catalog, InheritsRelidSeqnoIndexId, true,
   20377             :                               NULL, 1, &skey);
   20378        2500 :     if (HeapTupleIsValid(systable_getnext(scan)))
   20379           6 :         ereport(ERROR,
   20380             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   20381             :                  errmsg("cannot attach inheritance child as partition")));
   20382        2494 :     systable_endscan(scan);
   20383             : 
   20384             :     /* ...or as a parent table (except the case when it is partitioned) */
   20385        2494 :     ScanKeyInit(&skey,
   20386             :                 Anum_pg_inherits_inhparent,
   20387             :                 BTEqualStrategyNumber, F_OIDEQ,
   20388             :                 ObjectIdGetDatum(RelationGetRelid(attachrel)));
   20389        2494 :     scan = systable_beginscan(catalog, InheritsParentIndexId, true, NULL,
   20390             :                               1, &skey);
   20391        2494 :     if (HeapTupleIsValid(systable_getnext(scan)) &&
   20392         280 :         attachrel->rd_rel->relkind == RELKIND_RELATION)
   20393           6 :         ereport(ERROR,
   20394             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   20395             :                  errmsg("cannot attach inheritance parent as partition")));
   20396        2488 :     systable_endscan(scan);
   20397        2488 :     table_close(catalog, AccessShareLock);
   20398             : 
   20399             :     /*
   20400             :      * Prevent circularity by seeing if rel is a partition of attachrel. (In
   20401             :      * particular, this disallows making a rel a partition of itself.)
   20402             :      *
   20403             :      * We do that by checking if rel is a member of the list of attachrel's
   20404             :      * partitions provided the latter is partitioned at all.  We want to avoid
   20405             :      * having to construct this list again, so we request the strongest lock
   20406             :      * on all partitions.  We need the strongest lock, because we may decide
   20407             :      * to scan them if we find out that the table being attached (or its leaf
   20408             :      * partitions) may contain rows that violate the partition constraint. If
   20409             :      * the table has a constraint that would prevent such rows, which by
   20410             :      * definition is present in all the partitions, we need not scan the
   20411             :      * table, nor its partitions.  But we cannot risk a deadlock by taking a
   20412             :      * weaker lock now and the stronger one only when needed.
   20413             :      */
   20414        2488 :     attachrel_children = find_all_inheritors(RelationGetRelid(attachrel),
   20415             :                                              AccessExclusiveLock, NULL);
   20416        2488 :     if (list_member_oid(attachrel_children, RelationGetRelid(rel)))
   20417          12 :         ereport(ERROR,
   20418             :                 (errcode(ERRCODE_DUPLICATE_TABLE),
   20419             :                  errmsg("circular inheritance not allowed"),
   20420             :                  errdetail("\"%s\" is already a child of \"%s\".",
   20421             :                            RelationGetRelationName(rel),
   20422             :                            RelationGetRelationName(attachrel))));
   20423             : 
   20424             :     /* If the parent is permanent, so must be all of its partitions. */
   20425        2476 :     if (rel->rd_rel->relpersistence != RELPERSISTENCE_TEMP &&
   20426        2434 :         attachrel->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
   20427           6 :         ereport(ERROR,
   20428             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   20429             :                  errmsg("cannot attach a temporary relation as partition of permanent relation \"%s\"",
   20430             :                         RelationGetRelationName(rel))));
   20431             : 
   20432             :     /* Temp parent cannot have a partition that is itself not a temp */
   20433        2470 :     if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
   20434          42 :         attachrel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
   20435          18 :         ereport(ERROR,
   20436             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   20437             :                  errmsg("cannot attach a permanent relation as partition of temporary relation \"%s\"",
   20438             :                         RelationGetRelationName(rel))));
   20439             : 
   20440             :     /* If the parent is temp, it must belong to this session */
   20441        2452 :     if (RELATION_IS_OTHER_TEMP(rel))
   20442           0 :         ereport(ERROR,
   20443             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   20444             :                  errmsg("cannot attach as partition of temporary relation of another session")));
   20445             : 
   20446             :     /* Ditto for the partition */
   20447        2452 :     if (RELATION_IS_OTHER_TEMP(attachrel))
   20448           0 :         ereport(ERROR,
   20449             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   20450             :                  errmsg("cannot attach temporary relation of another session as partition")));
   20451             : 
   20452             :     /*
   20453             :      * Check if attachrel has any identity columns or any columns that aren't
   20454             :      * in the parent.
   20455             :      */
   20456        2452 :     tupleDesc = RelationGetDescr(attachrel);
   20457        2452 :     natts = tupleDesc->natts;
   20458        8402 :     for (attno = 1; attno <= natts; attno++)
   20459             :     {
   20460        5992 :         Form_pg_attribute attribute = TupleDescAttr(tupleDesc, attno - 1);
   20461        5992 :         char       *attributeName = NameStr(attribute->attname);
   20462             : 
   20463             :         /* Ignore dropped */
   20464        5992 :         if (attribute->attisdropped)
   20465         616 :             continue;
   20466             : 
   20467        5376 :         if (attribute->attidentity)
   20468          24 :             ereport(ERROR,
   20469             :                     errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
   20470             :                     errmsg("table \"%s\" being attached contains an identity column \"%s\"",
   20471             :                            RelationGetRelationName(attachrel), attributeName),
   20472             :                     errdetail("The new partition may not contain an identity column."));
   20473             : 
   20474             :         /* Try to find the column in parent (matching on column name) */
   20475        5352 :         if (!SearchSysCacheExists2(ATTNAME,
   20476             :                                    ObjectIdGetDatum(RelationGetRelid(rel)),
   20477             :                                    CStringGetDatum(attributeName)))
   20478          18 :             ereport(ERROR,
   20479             :                     (errcode(ERRCODE_DATATYPE_MISMATCH),
   20480             :                      errmsg("table \"%s\" contains column \"%s\" not found in parent \"%s\"",
   20481             :                             RelationGetRelationName(attachrel), attributeName,
   20482             :                             RelationGetRelationName(rel)),
   20483             :                      errdetail("The new partition may contain only the columns present in parent.")));
   20484             :     }
   20485             : 
   20486             :     /*
   20487             :      * If child_rel has row-level triggers with transition tables, we
   20488             :      * currently don't allow it to become a partition.  See also prohibitions
   20489             :      * in ATExecAddInherit() and CreateTrigger().
   20490             :      */
   20491        2410 :     trigger_name = FindTriggerIncompatibleWithInheritance(attachrel->trigdesc);
   20492        2410 :     if (trigger_name != NULL)
   20493           6 :         ereport(ERROR,
   20494             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   20495             :                  errmsg("trigger \"%s\" prevents table \"%s\" from becoming a partition",
   20496             :                         trigger_name, RelationGetRelationName(attachrel)),
   20497             :                  errdetail("ROW triggers with transition tables are not supported on partitions.")));
   20498             : 
   20499             :     /*
   20500             :      * Check that the new partition's bound is valid and does not overlap any
   20501             :      * of existing partitions of the parent - note that it does not return on
   20502             :      * error.
   20503             :      */
   20504        2404 :     check_new_partition_bound(RelationGetRelationName(attachrel), rel,
   20505             :                               cmd->bound, pstate);
   20506             : 
   20507        2368 :     attachPartitionTable(wqueue, rel, attachrel, cmd->bound);
   20508             : 
   20509             :     /*
   20510             :      * Generate a partition constraint from the partition bound specification.
   20511             :      * If the parent itself is a partition, make sure to include its
   20512             :      * constraint as well.
   20513             :      */
   20514        2206 :     partBoundConstraint = get_qual_from_partbound(rel, cmd->bound);
   20515             : 
   20516             :     /*
   20517             :      * Use list_concat_copy() to avoid modifying partBoundConstraint in place,
   20518             :      * since it's needed later to construct the constraint expression for
   20519             :      * validating against the default partition, if any.
   20520             :      */
   20521        2206 :     partConstraint = list_concat_copy(partBoundConstraint,
   20522        2206 :                                       RelationGetPartitionQual(rel));
   20523             : 
   20524             :     /* Skip validation if there are no constraints to validate. */
   20525        2206 :     if (partConstraint)
   20526             :     {
   20527             :         /*
   20528             :          * Run the partition quals through const-simplification similar to
   20529             :          * check constraints.  We skip canonicalize_qual, though, because
   20530             :          * partition quals should be in canonical form already.
   20531             :          */
   20532             :         partConstraint =
   20533        2158 :             (List *) eval_const_expressions(NULL,
   20534             :                                             (Node *) partConstraint);
   20535             : 
   20536             :         /* XXX this sure looks wrong */
   20537        2158 :         partConstraint = list_make1(make_ands_explicit(partConstraint));
   20538             : 
   20539             :         /*
   20540             :          * Adjust the generated constraint to match this partition's attribute
   20541             :          * numbers.
   20542             :          */
   20543        2158 :         partConstraint = map_partition_varattnos(partConstraint, 1, attachrel,
   20544             :                                                  rel);
   20545             : 
   20546             :         /* Validate partition constraints against the table being attached. */
   20547        2158 :         QueuePartitionConstraintValidation(wqueue, attachrel, partConstraint,
   20548             :                                            false);
   20549             :     }
   20550             : 
   20551             :     /*
   20552             :      * If we're attaching a partition other than the default partition and a
   20553             :      * default one exists, then that partition's partition constraint changes,
   20554             :      * so add an entry to the work queue to validate it, too.  (We must not do
   20555             :      * this when the partition being attached is the default one; we already
   20556             :      * did it above!)
   20557             :      */
   20558        2206 :     if (OidIsValid(defaultPartOid))
   20559             :     {
   20560             :         Relation    defaultrel;
   20561             :         List       *defPartConstraint;
   20562             : 
   20563             :         Assert(!cmd->bound->is_default);
   20564             : 
   20565             :         /* we already hold a lock on the default partition */
   20566         146 :         defaultrel = table_open(defaultPartOid, NoLock);
   20567             :         defPartConstraint =
   20568         146 :             get_proposed_default_constraint(partBoundConstraint);
   20569             : 
   20570             :         /*
   20571             :          * Map the Vars in the constraint expression from rel's attnos to
   20572             :          * defaultrel's.
   20573             :          */
   20574             :         defPartConstraint =
   20575         146 :             map_partition_varattnos(defPartConstraint,
   20576             :                                     1, defaultrel, rel);
   20577         146 :         QueuePartitionConstraintValidation(wqueue, defaultrel,
   20578             :                                            defPartConstraint, true);
   20579             : 
   20580             :         /* keep our lock until commit. */
   20581         146 :         table_close(defaultrel, NoLock);
   20582             :     }
   20583             : 
   20584        2206 :     ObjectAddressSet(address, RelationRelationId, RelationGetRelid(attachrel));
   20585             : 
   20586             :     /*
   20587             :      * If the partition we just attached is partitioned itself, invalidate
   20588             :      * relcache for all descendent partitions too to ensure that their
   20589             :      * rd_partcheck expression trees are rebuilt; partitions already locked at
   20590             :      * the beginning of this function.
   20591             :      */
   20592        2206 :     if (attachrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
   20593             :     {
   20594             :         ListCell   *l;
   20595             : 
   20596        1108 :         foreach(l, attachrel_children)
   20597             :         {
   20598         746 :             CacheInvalidateRelcacheByRelid(lfirst_oid(l));
   20599             :         }
   20600             :     }
   20601             : 
   20602             :     /* keep our lock until commit */
   20603        2206 :     table_close(attachrel, NoLock);
   20604             : 
   20605        2206 :     return address;
   20606             : }
   20607             : 
   20608             : /*
   20609             :  * AttachPartitionEnsureIndexes
   20610             :  *      subroutine for ATExecAttachPartition to create/match indexes
   20611             :  *
   20612             :  * Enforce the indexing rule for partitioned tables during ALTER TABLE / ATTACH
   20613             :  * PARTITION: every partition must have an index attached to each index on the
   20614             :  * partitioned table.
   20615             :  */
   20616             : static void
   20617        2908 : AttachPartitionEnsureIndexes(List **wqueue, Relation rel, Relation attachrel)
   20618             : {
   20619             :     List       *idxes;
   20620             :     List       *attachRelIdxs;
   20621             :     Relation   *attachrelIdxRels;
   20622             :     IndexInfo **attachInfos;
   20623             :     ListCell   *cell;
   20624             :     MemoryContext cxt;
   20625             :     MemoryContext oldcxt;
   20626             : 
   20627        2908 :     cxt = AllocSetContextCreate(CurrentMemoryContext,
   20628             :                                 "AttachPartitionEnsureIndexes",
   20629             :                                 ALLOCSET_DEFAULT_SIZES);
   20630        2908 :     oldcxt = MemoryContextSwitchTo(cxt);
   20631             : 
   20632        2908 :     idxes = RelationGetIndexList(rel);
   20633        2908 :     attachRelIdxs = RelationGetIndexList(attachrel);
   20634        2908 :     attachrelIdxRels = palloc_array(Relation, list_length(attachRelIdxs));
   20635        2908 :     attachInfos = palloc_array(IndexInfo *, list_length(attachRelIdxs));
   20636             : 
   20637             :     /* Build arrays of all existing indexes and their IndexInfos */
   20638        6222 :     foreach_oid(cldIdxId, attachRelIdxs)
   20639             :     {
   20640         406 :         int         i = foreach_current_index(cldIdxId);
   20641             : 
   20642         406 :         attachrelIdxRels[i] = index_open(cldIdxId, AccessShareLock);
   20643         406 :         attachInfos[i] = BuildIndexInfo(attachrelIdxRels[i]);
   20644             :     }
   20645             : 
   20646             :     /*
   20647             :      * If we're attaching a foreign table, we must fail if any of the indexes
   20648             :      * is a constraint index; otherwise, there's nothing to do here.  Do this
   20649             :      * before starting work, to avoid wasting the effort of building a few
   20650             :      * non-unique indexes before coming across a unique one.
   20651             :      */
   20652        2908 :     if (attachrel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
   20653             :     {
   20654          88 :         foreach(cell, idxes)
   20655             :         {
   20656          36 :             Oid         idx = lfirst_oid(cell);
   20657          36 :             Relation    idxRel = index_open(idx, AccessShareLock);
   20658             : 
   20659          36 :             if (idxRel->rd_index->indisunique ||
   20660          24 :                 idxRel->rd_index->indisprimary)
   20661          12 :                 ereport(ERROR,
   20662             :                         (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   20663             :                          errmsg("cannot attach foreign table \"%s\" as partition of partitioned table \"%s\"",
   20664             :                                 RelationGetRelationName(attachrel),
   20665             :                                 RelationGetRelationName(rel)),
   20666             :                          errdetail("Partitioned table \"%s\" contains unique indexes.",
   20667             :                                    RelationGetRelationName(rel))));
   20668          24 :             index_close(idxRel, AccessShareLock);
   20669             :         }
   20670             : 
   20671          52 :         goto out;
   20672             :     }
   20673             : 
   20674             :     /*
   20675             :      * For each index on the partitioned table, find a matching one in the
   20676             :      * partition-to-be; if one is not found, create one.
   20677             :      */
   20678        3552 :     foreach(cell, idxes)
   20679             :     {
   20680         726 :         Oid         idx = lfirst_oid(cell);
   20681         726 :         Relation    idxRel = index_open(idx, AccessShareLock);
   20682             :         IndexInfo  *info;
   20683             :         AttrMap    *attmap;
   20684         726 :         bool        found = false;
   20685             :         Oid         constraintOid;
   20686             : 
   20687             :         /*
   20688             :          * Ignore indexes in the partitioned table other than partitioned
   20689             :          * indexes.
   20690             :          */
   20691         726 :         if (idxRel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
   20692             :         {
   20693           0 :             index_close(idxRel, AccessShareLock);
   20694           0 :             continue;
   20695             :         }
   20696             : 
   20697             :         /* construct an indexinfo to compare existing indexes against */
   20698         726 :         info = BuildIndexInfo(idxRel);
   20699         726 :         attmap = build_attrmap_by_name(RelationGetDescr(attachrel),
   20700             :                                        RelationGetDescr(rel),
   20701             :                                        false);
   20702         726 :         constraintOid = get_relation_idx_constraint_oid(RelationGetRelid(rel), idx);
   20703             : 
   20704             :         /*
   20705             :          * Scan the list of existing indexes in the partition-to-be, and mark
   20706             :          * the first matching, valid, unattached one we find, if any, as
   20707             :          * partition of the parent index.  If we find one, we're done.
   20708             :          */
   20709         786 :         for (int i = 0; i < list_length(attachRelIdxs); i++)
   20710             :         {
   20711         298 :             Oid         cldIdxId = RelationGetRelid(attachrelIdxRels[i]);
   20712         298 :             Oid         cldConstrOid = InvalidOid;
   20713             : 
   20714             :             /* does this index have a parent?  if so, can't use it */
   20715         298 :             if (attachrelIdxRels[i]->rd_rel->relispartition)
   20716          12 :                 continue;
   20717             : 
   20718             :             /* If this index is invalid, can't use it */
   20719         286 :             if (!attachrelIdxRels[i]->rd_index->indisvalid)
   20720           6 :                 continue;
   20721             : 
   20722         280 :             if (CompareIndexInfo(attachInfos[i], info,
   20723         280 :                                  attachrelIdxRels[i]->rd_indcollation,
   20724         280 :                                  idxRel->rd_indcollation,
   20725         280 :                                  attachrelIdxRels[i]->rd_opfamily,
   20726         280 :                                  idxRel->rd_opfamily,
   20727             :                                  attmap))
   20728             :             {
   20729             :                 /*
   20730             :                  * If this index is being created in the parent because of a
   20731             :                  * constraint, then the child needs to have a constraint also,
   20732             :                  * so look for one.  If there is no such constraint, this
   20733             :                  * index is no good, so keep looking.
   20734             :                  */
   20735         244 :                 if (OidIsValid(constraintOid))
   20736             :                 {
   20737             :                     cldConstrOid =
   20738         146 :                         get_relation_idx_constraint_oid(RelationGetRelid(attachrel),
   20739             :                                                         cldIdxId);
   20740             :                     /* no dice */
   20741         146 :                     if (!OidIsValid(cldConstrOid))
   20742           6 :                         continue;
   20743             : 
   20744             :                     /* Ensure they're both the same type of constraint */
   20745         280 :                     if (get_constraint_type(constraintOid) !=
   20746         140 :                         get_constraint_type(cldConstrOid))
   20747           0 :                         continue;
   20748             :                 }
   20749             : 
   20750             :                 /* bingo. */
   20751         238 :                 IndexSetParentIndex(attachrelIdxRels[i], idx);
   20752         238 :                 if (OidIsValid(constraintOid))
   20753         140 :                     ConstraintSetParentConstraint(cldConstrOid, constraintOid,
   20754             :                                                   RelationGetRelid(attachrel));
   20755         238 :                 found = true;
   20756             : 
   20757         238 :                 CommandCounterIncrement();
   20758         238 :                 break;
   20759             :             }
   20760             :         }
   20761             : 
   20762             :         /*
   20763             :          * If no suitable index was found in the partition-to-be, create one
   20764             :          * now.  Note that if this is a PK, not-null constraints must already
   20765             :          * exist.
   20766             :          */
   20767         726 :         if (!found)
   20768             :         {
   20769             :             IndexStmt  *stmt;
   20770             :             Oid         conOid;
   20771             : 
   20772         488 :             stmt = generateClonedIndexStmt(NULL,
   20773             :                                            idxRel, attmap,
   20774             :                                            &conOid);
   20775         488 :             DefineIndex(NULL,
   20776             :                         RelationGetRelid(attachrel), stmt, InvalidOid,
   20777             :                         RelationGetRelid(idxRel),
   20778             :                         conOid,
   20779             :                         -1,
   20780             :                         true, false, false, false, false);
   20781             :         }
   20782             : 
   20783         708 :         index_close(idxRel, AccessShareLock);
   20784             :     }
   20785             : 
   20786        2878 : out:
   20787             :     /* Clean up. */
   20788        3272 :     for (int i = 0; i < list_length(attachRelIdxs); i++)
   20789         394 :         index_close(attachrelIdxRels[i], AccessShareLock);
   20790        2878 :     MemoryContextSwitchTo(oldcxt);
   20791        2878 :     MemoryContextDelete(cxt);
   20792        2878 : }
   20793             : 
   20794             : /*
   20795             :  * CloneRowTriggersToPartition
   20796             :  *      subroutine for ATExecAttachPartition/DefineRelation to create row
   20797             :  *      triggers on partitions
   20798             :  */
   20799             : static void
   20800        3352 : CloneRowTriggersToPartition(Relation parent, Relation partition)
   20801             : {
   20802             :     Relation    pg_trigger;
   20803             :     ScanKeyData key;
   20804             :     SysScanDesc scan;
   20805             :     HeapTuple   tuple;
   20806             :     MemoryContext perTupCxt;
   20807             : 
   20808        3352 :     ScanKeyInit(&key, Anum_pg_trigger_tgrelid, BTEqualStrategyNumber,
   20809             :                 F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(parent)));
   20810        3352 :     pg_trigger = table_open(TriggerRelationId, RowExclusiveLock);
   20811        3352 :     scan = systable_beginscan(pg_trigger, TriggerRelidNameIndexId,
   20812             :                               true, NULL, 1, &key);
   20813             : 
   20814        3352 :     perTupCxt = AllocSetContextCreate(CurrentMemoryContext,
   20815             :                                       "clone trig", ALLOCSET_SMALL_SIZES);
   20816             : 
   20817        5424 :     while (HeapTupleIsValid(tuple = systable_getnext(scan)))
   20818             :     {
   20819        2078 :         Form_pg_trigger trigForm = (Form_pg_trigger) GETSTRUCT(tuple);
   20820             :         CreateTrigStmt *trigStmt;
   20821        2078 :         Node       *qual = NULL;
   20822             :         Datum       value;
   20823             :         bool        isnull;
   20824        2078 :         List       *cols = NIL;
   20825        2078 :         List       *trigargs = NIL;
   20826             :         MemoryContext oldcxt;
   20827             : 
   20828             :         /*
   20829             :          * Ignore statement-level triggers; those are not cloned.
   20830             :          */
   20831        2078 :         if (!TRIGGER_FOR_ROW(trigForm->tgtype))
   20832        1880 :             continue;
   20833             : 
   20834             :         /*
   20835             :          * Don't clone internal triggers, because the constraint cloning code
   20836             :          * will.
   20837             :          */
   20838        2036 :         if (trigForm->tgisinternal)
   20839        1838 :             continue;
   20840             : 
   20841             :         /*
   20842             :          * Complain if we find an unexpected trigger type.
   20843             :          */
   20844         198 :         if (!TRIGGER_FOR_BEFORE(trigForm->tgtype) &&
   20845         162 :             !TRIGGER_FOR_AFTER(trigForm->tgtype))
   20846           0 :             elog(ERROR, "unexpected trigger \"%s\" found",
   20847             :                  NameStr(trigForm->tgname));
   20848             : 
   20849             :         /* Use short-lived context for CREATE TRIGGER */
   20850         198 :         oldcxt = MemoryContextSwitchTo(perTupCxt);
   20851             : 
   20852             :         /*
   20853             :          * If there is a WHEN clause, generate a 'cooked' version of it that's
   20854             :          * appropriate for the partition.
   20855             :          */
   20856         198 :         value = heap_getattr(tuple, Anum_pg_trigger_tgqual,
   20857             :                              RelationGetDescr(pg_trigger), &isnull);
   20858         198 :         if (!isnull)
   20859             :         {
   20860           6 :             qual = stringToNode(TextDatumGetCString(value));
   20861           6 :             qual = (Node *) map_partition_varattnos((List *) qual, PRS2_OLD_VARNO,
   20862             :                                                     partition, parent);
   20863           6 :             qual = (Node *) map_partition_varattnos((List *) qual, PRS2_NEW_VARNO,
   20864             :                                                     partition, parent);
   20865             :         }
   20866             : 
   20867             :         /*
   20868             :          * If there is a column list, transform it to a list of column names.
   20869             :          * Note we don't need to map this list in any way ...
   20870             :          */
   20871         198 :         if (trigForm->tgattr.dim1 > 0)
   20872             :         {
   20873             :             int         i;
   20874             : 
   20875          12 :             for (i = 0; i < trigForm->tgattr.dim1; i++)
   20876             :             {
   20877             :                 Form_pg_attribute col;
   20878             : 
   20879           6 :                 col = TupleDescAttr(parent->rd_att,
   20880           6 :                                     trigForm->tgattr.values[i] - 1);
   20881           6 :                 cols = lappend(cols,
   20882           6 :                                makeString(pstrdup(NameStr(col->attname))));
   20883             :             }
   20884             :         }
   20885             : 
   20886             :         /* Reconstruct trigger arguments list. */
   20887         198 :         if (trigForm->tgnargs > 0)
   20888             :         {
   20889             :             char       *p;
   20890             : 
   20891          54 :             value = heap_getattr(tuple, Anum_pg_trigger_tgargs,
   20892             :                                  RelationGetDescr(pg_trigger), &isnull);
   20893          54 :             if (isnull)
   20894           0 :                 elog(ERROR, "tgargs is null for trigger \"%s\" in partition \"%s\"",
   20895             :                      NameStr(trigForm->tgname), RelationGetRelationName(partition));
   20896             : 
   20897          54 :             p = (char *) VARDATA_ANY(DatumGetByteaPP(value));
   20898             : 
   20899         120 :             for (int i = 0; i < trigForm->tgnargs; i++)
   20900             :             {
   20901          66 :                 trigargs = lappend(trigargs, makeString(pstrdup(p)));
   20902          66 :                 p += strlen(p) + 1;
   20903             :             }
   20904             :         }
   20905             : 
   20906         198 :         trigStmt = makeNode(CreateTrigStmt);
   20907         198 :         trigStmt->replace = false;
   20908         198 :         trigStmt->isconstraint = OidIsValid(trigForm->tgconstraint);
   20909         198 :         trigStmt->trigname = NameStr(trigForm->tgname);
   20910         198 :         trigStmt->relation = NULL;
   20911         198 :         trigStmt->funcname = NULL;   /* passed separately */
   20912         198 :         trigStmt->args = trigargs;
   20913         198 :         trigStmt->row = true;
   20914         198 :         trigStmt->timing = trigForm->tgtype & TRIGGER_TYPE_TIMING_MASK;
   20915         198 :         trigStmt->events = trigForm->tgtype & TRIGGER_TYPE_EVENT_MASK;
   20916         198 :         trigStmt->columns = cols;
   20917         198 :         trigStmt->whenClause = NULL; /* passed separately */
   20918         198 :         trigStmt->transitionRels = NIL; /* not supported at present */
   20919         198 :         trigStmt->deferrable = trigForm->tgdeferrable;
   20920         198 :         trigStmt->initdeferred = trigForm->tginitdeferred;
   20921         198 :         trigStmt->constrrel = NULL; /* passed separately */
   20922             : 
   20923         198 :         CreateTriggerFiringOn(trigStmt, NULL, RelationGetRelid(partition),
   20924             :                               trigForm->tgconstrrelid, InvalidOid, InvalidOid,
   20925             :                               trigForm->tgfoid, trigForm->oid, qual,
   20926         198 :                               false, true, trigForm->tgenabled);
   20927             : 
   20928         192 :         MemoryContextSwitchTo(oldcxt);
   20929         192 :         MemoryContextReset(perTupCxt);
   20930             :     }
   20931             : 
   20932        3346 :     MemoryContextDelete(perTupCxt);
   20933             : 
   20934        3346 :     systable_endscan(scan);
   20935        3346 :     table_close(pg_trigger, RowExclusiveLock);
   20936        3346 : }
   20937             : 
   20938             : /*
   20939             :  * ALTER TABLE DETACH PARTITION
   20940             :  *
   20941             :  * Return the address of the relation that is no longer a partition of rel.
   20942             :  *
   20943             :  * If concurrent mode is requested, we run in two transactions.  A side-
   20944             :  * effect is that this command cannot run in a multi-part ALTER TABLE.
   20945             :  * Currently, that's enforced by the grammar.
   20946             :  *
   20947             :  * The strategy for concurrency is to first modify the partition's
   20948             :  * pg_inherit catalog row to make it visible to everyone that the
   20949             :  * partition is detached, lock the partition against writes, and commit
   20950             :  * the transaction; anyone who requests the partition descriptor from
   20951             :  * that point onwards has to ignore such a partition.  In a second
   20952             :  * transaction, we wait until all transactions that could have seen the
   20953             :  * partition as attached are gone, then we remove the rest of partition
   20954             :  * metadata (pg_inherits and pg_class.relpartbounds).
   20955             :  */
   20956             : static ObjectAddress
   20957         584 : ATExecDetachPartition(List **wqueue, AlteredTableInfo *tab, Relation rel,
   20958             :                       RangeVar *name, bool concurrent)
   20959             : {
   20960             :     Relation    partRel;
   20961             :     ObjectAddress address;
   20962             :     Oid         defaultPartOid;
   20963             : 
   20964             :     /*
   20965             :      * We must lock the default partition, because detaching this partition
   20966             :      * will change its partition constraint.
   20967             :      */
   20968             :     defaultPartOid =
   20969         584 :         get_default_oid_from_partdesc(RelationGetPartitionDesc(rel, true));
   20970         584 :     if (OidIsValid(defaultPartOid))
   20971             :     {
   20972             :         /*
   20973             :          * Concurrent detaching when a default partition exists is not
   20974             :          * supported. The main problem is that the default partition
   20975             :          * constraint would change.  And there's a definitional problem: what
   20976             :          * should happen to the tuples that are being inserted that belong to
   20977             :          * the partition being detached?  Putting them on the partition being
   20978             :          * detached would be wrong, since they'd become "lost" after the
   20979             :          * detaching completes but we cannot put them in the default partition
   20980             :          * either until we alter its partition constraint.
   20981             :          *
   20982             :          * I think we could solve this problem if we effected the constraint
   20983             :          * change before committing the first transaction.  But the lock would
   20984             :          * have to remain AEL and it would cause concurrent query planning to
   20985             :          * be blocked, so changing it that way would be even worse.
   20986             :          */
   20987         112 :         if (concurrent)
   20988          12 :             ereport(ERROR,
   20989             :                     (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
   20990             :                      errmsg("cannot detach partitions concurrently when a default partition exists")));
   20991         100 :         LockRelationOid(defaultPartOid, AccessExclusiveLock);
   20992             :     }
   20993             : 
   20994             :     /*
   20995             :      * In concurrent mode, the partition is locked with share-update-exclusive
   20996             :      * in the first transaction.  This allows concurrent transactions to be
   20997             :      * doing DML to the partition.
   20998             :      */
   20999         572 :     partRel = table_openrv(name, concurrent ? ShareUpdateExclusiveLock :
   21000             :                            AccessExclusiveLock);
   21001             : 
   21002             :     /*
   21003             :      * Check inheritance conditions and either delete the pg_inherits row (in
   21004             :      * non-concurrent mode) or just set the inhdetachpending flag.
   21005             :      */
   21006         560 :     if (!concurrent)
   21007         414 :         RemoveInheritance(partRel, rel, false);
   21008             :     else
   21009         146 :         MarkInheritDetached(partRel, rel);
   21010             : 
   21011             :     /*
   21012             :      * Ensure that foreign keys still hold after this detach.  This keeps
   21013             :      * locks on the referencing tables, which prevents concurrent transactions
   21014             :      * from adding rows that we wouldn't see.  For this to work in concurrent
   21015             :      * mode, it is critical that the partition appears as no longer attached
   21016             :      * for the RI queries as soon as the first transaction commits.
   21017             :      */
   21018         540 :     ATDetachCheckNoForeignKeyRefs(partRel);
   21019             : 
   21020             :     /*
   21021             :      * Concurrent mode has to work harder; first we add a new constraint to
   21022             :      * the partition that matches the partition constraint.  Then we close our
   21023             :      * existing transaction, and in a new one wait for all processes to catch
   21024             :      * up on the catalog updates we've done so far; at that point we can
   21025             :      * complete the operation.
   21026             :      */
   21027         506 :     if (concurrent)
   21028             :     {
   21029             :         Oid         partrelid,
   21030             :                     parentrelid;
   21031             :         LOCKTAG     tag;
   21032             :         char       *parentrelname;
   21033             :         char       *partrelname;
   21034             : 
   21035             :         /*
   21036             :          * We're almost done now; the only traces that remain are the
   21037             :          * pg_inherits tuple and the partition's relpartbounds.  Before we can
   21038             :          * remove those, we need to wait until all transactions that know that
   21039             :          * this is a partition are gone.
   21040             :          */
   21041             : 
   21042             :         /*
   21043             :          * Remember relation OIDs to re-acquire them later; and relation names
   21044             :          * too, for error messages if something is dropped in between.
   21045             :          */
   21046         140 :         partrelid = RelationGetRelid(partRel);
   21047         140 :         parentrelid = RelationGetRelid(rel);
   21048         140 :         parentrelname = MemoryContextStrdup(PortalContext,
   21049         140 :                                             RelationGetRelationName(rel));
   21050         140 :         partrelname = MemoryContextStrdup(PortalContext,
   21051         140 :                                           RelationGetRelationName(partRel));
   21052             : 
   21053             :         /* Invalidate relcache entries for the parent -- must be before close */
   21054         140 :         CacheInvalidateRelcache(rel);
   21055             : 
   21056         140 :         table_close(partRel, NoLock);
   21057         140 :         table_close(rel, NoLock);
   21058         140 :         tab->rel = NULL;
   21059             : 
   21060             :         /* Make updated catalog entry visible */
   21061         140 :         PopActiveSnapshot();
   21062         140 :         CommitTransactionCommand();
   21063             : 
   21064         140 :         StartTransactionCommand();
   21065             : 
   21066             :         /*
   21067             :          * Now wait.  This ensures that all queries that were planned
   21068             :          * including the partition are finished before we remove the rest of
   21069             :          * catalog entries.  We don't need or indeed want to acquire this
   21070             :          * lock, though -- that would block later queries.
   21071             :          *
   21072             :          * We don't need to concern ourselves with waiting for a lock on the
   21073             :          * partition itself, since we will acquire AccessExclusiveLock below.
   21074             :          */
   21075         140 :         SET_LOCKTAG_RELATION(tag, MyDatabaseId, parentrelid);
   21076         140 :         WaitForLockersMultiple(list_make1(&tag), AccessExclusiveLock, false);
   21077             : 
   21078             :         /*
   21079             :          * Now acquire locks in both relations again.  Note they may have been
   21080             :          * removed in the meantime, so care is required.
   21081             :          */
   21082          90 :         rel = try_relation_open(parentrelid, ShareUpdateExclusiveLock);
   21083          90 :         partRel = try_relation_open(partrelid, AccessExclusiveLock);
   21084             : 
   21085             :         /* If the relations aren't there, something bad happened; bail out */
   21086          90 :         if (rel == NULL)
   21087             :         {
   21088           0 :             if (partRel != NULL)    /* shouldn't happen */
   21089           0 :                 elog(WARNING, "dangling partition \"%s\" remains, can't fix",
   21090             :                      partrelname);
   21091           0 :             ereport(ERROR,
   21092             :                     (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
   21093             :                      errmsg("partitioned table \"%s\" was removed concurrently",
   21094             :                             parentrelname)));
   21095             :         }
   21096          90 :         if (partRel == NULL)
   21097           0 :             ereport(ERROR,
   21098             :                     (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
   21099             :                      errmsg("partition \"%s\" was removed concurrently", partrelname)));
   21100             : 
   21101          90 :         tab->rel = rel;
   21102             :     }
   21103             : 
   21104             :     /*
   21105             :      * Detaching the partition might involve TOAST table access, so ensure we
   21106             :      * have a valid snapshot.
   21107             :      */
   21108         456 :     PushActiveSnapshot(GetTransactionSnapshot());
   21109             : 
   21110             :     /* Do the final part of detaching */
   21111         456 :     DetachPartitionFinalize(rel, partRel, concurrent, defaultPartOid);
   21112             : 
   21113         454 :     PopActiveSnapshot();
   21114             : 
   21115         454 :     ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partRel));
   21116             : 
   21117             :     /* keep our lock until commit */
   21118         454 :     table_close(partRel, NoLock);
   21119             : 
   21120         454 :     return address;
   21121             : }
   21122             : 
   21123             : /*
   21124             :  * Second part of ALTER TABLE .. DETACH.
   21125             :  *
   21126             :  * This is separate so that it can be run independently when the second
   21127             :  * transaction of the concurrent algorithm fails (crash or abort).
   21128             :  */
   21129             : static void
   21130        1040 : DetachPartitionFinalize(Relation rel, Relation partRel, bool concurrent,
   21131             :                         Oid defaultPartOid)
   21132             : {
   21133             :     Relation    classRel;
   21134             :     List       *fks;
   21135             :     ListCell   *cell;
   21136             :     List       *indexes;
   21137             :     Datum       new_val[Natts_pg_class];
   21138             :     bool        new_null[Natts_pg_class],
   21139             :                 new_repl[Natts_pg_class];
   21140             :     HeapTuple   tuple,
   21141             :                 newtuple;
   21142        1040 :     Relation    trigrel = NULL;
   21143        1040 :     List       *fkoids = NIL;
   21144             : 
   21145        1040 :     if (concurrent)
   21146             :     {
   21147             :         /*
   21148             :          * We can remove the pg_inherits row now. (In the non-concurrent case,
   21149             :          * this was already done).
   21150             :          */
   21151         104 :         RemoveInheritance(partRel, rel, true);
   21152             :     }
   21153             : 
   21154             :     /* Drop any triggers that were cloned on creation/attach. */
   21155        1040 :     DropClonedTriggersFromPartition(RelationGetRelid(partRel));
   21156             : 
   21157             :     /*
   21158             :      * Detach any foreign keys that are inherited.  This includes creating
   21159             :      * additional action triggers.
   21160             :      */
   21161        1040 :     fks = copyObject(RelationGetFKeyList(partRel));
   21162        1040 :     if (fks != NIL)
   21163          90 :         trigrel = table_open(TriggerRelationId, RowExclusiveLock);
   21164             : 
   21165             :     /*
   21166             :      * It's possible that the partition being detached has a foreign key that
   21167             :      * references a partitioned table.  When that happens, there are multiple
   21168             :      * pg_constraint rows for the partition: one points to the partitioned
   21169             :      * table itself, while the others point to each of its partitions.  Only
   21170             :      * the topmost one is to be considered here; the child constraints must be
   21171             :      * left alone, because conceptually those aren't coming from our parent
   21172             :      * partitioned table, but from this partition itself.
   21173             :      *
   21174             :      * We implement this by collecting all the constraint OIDs in a first scan
   21175             :      * of the FK array, and skipping in the loop below those constraints whose
   21176             :      * parents are listed here.
   21177             :      */
   21178        2254 :     foreach_node(ForeignKeyCacheInfo, fk, fks)
   21179         174 :         fkoids = lappend_oid(fkoids, fk->conoid);
   21180             : 
   21181        1214 :     foreach(cell, fks)
   21182             :     {
   21183         174 :         ForeignKeyCacheInfo *fk = lfirst(cell);
   21184             :         HeapTuple   contup;
   21185             :         Form_pg_constraint conform;
   21186             : 
   21187         174 :         contup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(fk->conoid));
   21188         174 :         if (!HeapTupleIsValid(contup))
   21189           0 :             elog(ERROR, "cache lookup failed for constraint %u", fk->conoid);
   21190         174 :         conform = (Form_pg_constraint) GETSTRUCT(contup);
   21191             : 
   21192             :         /*
   21193             :          * Consider only inherited foreign keys, and only if their parents
   21194             :          * aren't in the list.
   21195             :          */
   21196         174 :         if (conform->contype != CONSTRAINT_FOREIGN ||
   21197         324 :             !OidIsValid(conform->conparentid) ||
   21198         150 :             list_member_oid(fkoids, conform->conparentid))
   21199             :         {
   21200          66 :             ReleaseSysCache(contup);
   21201          66 :             continue;
   21202             :         }
   21203             : 
   21204             :         /*
   21205             :          * The constraint on this table must be marked no longer a child of
   21206             :          * the parent's constraint, as do its check triggers.
   21207             :          */
   21208         108 :         ConstraintSetParentConstraint(fk->conoid, InvalidOid, InvalidOid);
   21209             : 
   21210             :         /*
   21211             :          * Also, look up the partition's "check" triggers corresponding to the
   21212             :          * ENFORCED constraint being detached and detach them from the parent
   21213             :          * triggers. NOT ENFORCED constraints do not have these triggers;
   21214             :          * therefore, this step is not needed.
   21215             :          */
   21216         108 :         if (fk->conenforced)
   21217             :         {
   21218             :             Oid         insertTriggerOid,
   21219             :                         updateTriggerOid;
   21220             : 
   21221         108 :             GetForeignKeyCheckTriggers(trigrel,
   21222             :                                        fk->conoid, fk->confrelid, fk->conrelid,
   21223             :                                        &insertTriggerOid, &updateTriggerOid);
   21224             :             Assert(OidIsValid(insertTriggerOid));
   21225         108 :             TriggerSetParentTrigger(trigrel, insertTriggerOid, InvalidOid,
   21226             :                                     RelationGetRelid(partRel));
   21227             :             Assert(OidIsValid(updateTriggerOid));
   21228         108 :             TriggerSetParentTrigger(trigrel, updateTriggerOid, InvalidOid,
   21229             :                                     RelationGetRelid(partRel));
   21230             :         }
   21231             : 
   21232             :         /*
   21233             :          * Lastly, create the action triggers on the referenced table, using
   21234             :          * addFkRecurseReferenced, which requires some elaborate setup (so put
   21235             :          * it in a separate block).  While at it, if the table is partitioned,
   21236             :          * that function will recurse to create the pg_constraint rows and
   21237             :          * action triggers for each partition.
   21238             :          *
   21239             :          * Note there's no need to do addFkConstraint() here, because the
   21240             :          * pg_constraint row already exists.
   21241             :          */
   21242             :         {
   21243             :             Constraint *fkconstraint;
   21244             :             int         numfks;
   21245             :             AttrNumber  conkey[INDEX_MAX_KEYS];
   21246             :             AttrNumber  confkey[INDEX_MAX_KEYS];
   21247             :             Oid         conpfeqop[INDEX_MAX_KEYS];
   21248             :             Oid         conppeqop[INDEX_MAX_KEYS];
   21249             :             Oid         conffeqop[INDEX_MAX_KEYS];
   21250             :             int         numfkdelsetcols;
   21251             :             AttrNumber  confdelsetcols[INDEX_MAX_KEYS];
   21252             :             Relation    refdRel;
   21253             : 
   21254         108 :             DeconstructFkConstraintRow(contup,
   21255             :                                        &numfks,
   21256             :                                        conkey,
   21257             :                                        confkey,
   21258             :                                        conpfeqop,
   21259             :                                        conppeqop,
   21260             :                                        conffeqop,
   21261             :                                        &numfkdelsetcols,
   21262             :                                        confdelsetcols);
   21263             : 
   21264             :             /* Create a synthetic node we'll use throughout */
   21265         108 :             fkconstraint = makeNode(Constraint);
   21266         108 :             fkconstraint->contype = CONSTRAINT_FOREIGN;
   21267         108 :             fkconstraint->conname = pstrdup(NameStr(conform->conname));
   21268         108 :             fkconstraint->deferrable = conform->condeferrable;
   21269         108 :             fkconstraint->initdeferred = conform->condeferred;
   21270         108 :             fkconstraint->is_enforced = conform->conenforced;
   21271         108 :             fkconstraint->skip_validation = true;
   21272         108 :             fkconstraint->initially_valid = conform->convalidated;
   21273             :             /* a few irrelevant fields omitted here */
   21274         108 :             fkconstraint->pktable = NULL;
   21275         108 :             fkconstraint->fk_attrs = NIL;
   21276         108 :             fkconstraint->pk_attrs = NIL;
   21277         108 :             fkconstraint->fk_matchtype = conform->confmatchtype;
   21278         108 :             fkconstraint->fk_upd_action = conform->confupdtype;
   21279         108 :             fkconstraint->fk_del_action = conform->confdeltype;
   21280         108 :             fkconstraint->fk_del_set_cols = NIL;
   21281         108 :             fkconstraint->old_conpfeqop = NIL;
   21282         108 :             fkconstraint->old_pktable_oid = InvalidOid;
   21283         108 :             fkconstraint->location = -1;
   21284             : 
   21285             :             /* set up colnames, used to generate the constraint name */
   21286         264 :             for (int i = 0; i < numfks; i++)
   21287             :             {
   21288             :                 Form_pg_attribute att;
   21289             : 
   21290         156 :                 att = TupleDescAttr(RelationGetDescr(partRel),
   21291         156 :                                     conkey[i] - 1);
   21292             : 
   21293         156 :                 fkconstraint->fk_attrs = lappend(fkconstraint->fk_attrs,
   21294         156 :                                                  makeString(NameStr(att->attname)));
   21295             :             }
   21296             : 
   21297         108 :             refdRel = table_open(fk->confrelid, ShareRowExclusiveLock);
   21298             : 
   21299         108 :             addFkRecurseReferenced(fkconstraint, partRel,
   21300             :                                    refdRel,
   21301             :                                    conform->conindid,
   21302             :                                    fk->conoid,
   21303             :                                    numfks,
   21304             :                                    confkey,
   21305             :                                    conkey,
   21306             :                                    conpfeqop,
   21307             :                                    conppeqop,
   21308             :                                    conffeqop,
   21309             :                                    numfkdelsetcols,
   21310             :                                    confdelsetcols,
   21311             :                                    true,
   21312             :                                    InvalidOid, InvalidOid,
   21313         108 :                                    conform->conperiod);
   21314         108 :             table_close(refdRel, NoLock);   /* keep lock till end of xact */
   21315             :         }
   21316             : 
   21317         108 :         ReleaseSysCache(contup);
   21318             :     }
   21319        1040 :     list_free_deep(fks);
   21320        1040 :     if (trigrel)
   21321          90 :         table_close(trigrel, RowExclusiveLock);
   21322             : 
   21323             :     /*
   21324             :      * Any sub-constraints that are in the referenced-side of a larger
   21325             :      * constraint have to be removed.  This partition is no longer part of the
   21326             :      * key space of the constraint.
   21327             :      */
   21328        1130 :     foreach(cell, GetParentedForeignKeyRefs(partRel))
   21329             :     {
   21330          92 :         Oid         constrOid = lfirst_oid(cell);
   21331             :         ObjectAddress constraint;
   21332             : 
   21333          92 :         ConstraintSetParentConstraint(constrOid, InvalidOid, InvalidOid);
   21334          92 :         deleteDependencyRecordsForClass(ConstraintRelationId,
   21335             :                                         constrOid,
   21336             :                                         ConstraintRelationId,
   21337             :                                         DEPENDENCY_INTERNAL);
   21338          92 :         CommandCounterIncrement();
   21339             : 
   21340          92 :         ObjectAddressSet(constraint, ConstraintRelationId, constrOid);
   21341          92 :         performDeletion(&constraint, DROP_RESTRICT, 0);
   21342             :     }
   21343             : 
   21344             :     /* Now we can detach indexes */
   21345        1038 :     indexes = RelationGetIndexList(partRel);
   21346        1478 :     foreach(cell, indexes)
   21347             :     {
   21348         440 :         Oid         idxid = lfirst_oid(cell);
   21349             :         Oid         parentidx;
   21350             :         Relation    idx;
   21351             :         Oid         constrOid;
   21352             :         Oid         parentConstrOid;
   21353             : 
   21354         440 :         if (!has_superclass(idxid))
   21355          12 :             continue;
   21356             : 
   21357         428 :         parentidx = get_partition_parent(idxid, false);
   21358             :         Assert((IndexGetRelation(parentidx, false) == RelationGetRelid(rel)));
   21359             : 
   21360         428 :         idx = index_open(idxid, AccessExclusiveLock);
   21361         428 :         IndexSetParentIndex(idx, InvalidOid);
   21362             : 
   21363             :         /*
   21364             :          * If there's a constraint associated with the index, detach it too.
   21365             :          * Careful: it is possible for a constraint index in a partition to be
   21366             :          * the child of a non-constraint index, so verify whether the parent
   21367             :          * index does actually have a constraint.
   21368             :          */
   21369         428 :         constrOid = get_relation_idx_constraint_oid(RelationGetRelid(partRel),
   21370             :                                                     idxid);
   21371         428 :         parentConstrOid = get_relation_idx_constraint_oid(RelationGetRelid(rel),
   21372             :                                                           parentidx);
   21373         428 :         if (OidIsValid(parentConstrOid) && OidIsValid(constrOid))
   21374         198 :             ConstraintSetParentConstraint(constrOid, InvalidOid, InvalidOid);
   21375             : 
   21376         428 :         index_close(idx, NoLock);
   21377             :     }
   21378             : 
   21379             :     /* Update pg_class tuple */
   21380        1038 :     classRel = table_open(RelationRelationId, RowExclusiveLock);
   21381        1038 :     tuple = SearchSysCacheCopy1(RELOID,
   21382             :                                 ObjectIdGetDatum(RelationGetRelid(partRel)));
   21383        1038 :     if (!HeapTupleIsValid(tuple))
   21384           0 :         elog(ERROR, "cache lookup failed for relation %u",
   21385             :              RelationGetRelid(partRel));
   21386             :     Assert(((Form_pg_class) GETSTRUCT(tuple))->relispartition);
   21387             : 
   21388             :     /* Clear relpartbound and reset relispartition */
   21389        1038 :     memset(new_val, 0, sizeof(new_val));
   21390        1038 :     memset(new_null, false, sizeof(new_null));
   21391        1038 :     memset(new_repl, false, sizeof(new_repl));
   21392        1038 :     new_val[Anum_pg_class_relpartbound - 1] = (Datum) 0;
   21393        1038 :     new_null[Anum_pg_class_relpartbound - 1] = true;
   21394        1038 :     new_repl[Anum_pg_class_relpartbound - 1] = true;
   21395        1038 :     newtuple = heap_modify_tuple(tuple, RelationGetDescr(classRel),
   21396             :                                  new_val, new_null, new_repl);
   21397             : 
   21398        1038 :     ((Form_pg_class) GETSTRUCT(newtuple))->relispartition = false;
   21399        1038 :     CatalogTupleUpdate(classRel, &newtuple->t_self, newtuple);
   21400        1038 :     heap_freetuple(newtuple);
   21401        1038 :     table_close(classRel, RowExclusiveLock);
   21402             : 
   21403             :     /*
   21404             :      * Drop identity property from all identity columns of partition.
   21405             :      */
   21406        3328 :     for (int attno = 0; attno < RelationGetNumberOfAttributes(partRel); attno++)
   21407             :     {
   21408        2290 :         Form_pg_attribute attr = TupleDescAttr(partRel->rd_att, attno);
   21409             : 
   21410        2290 :         if (!attr->attisdropped && attr->attidentity)
   21411          30 :             ATExecDropIdentity(partRel, NameStr(attr->attname), false,
   21412             :                                AccessExclusiveLock, true, true);
   21413             :     }
   21414             : 
   21415        1038 :     if (OidIsValid(defaultPartOid))
   21416             :     {
   21417             :         /*
   21418             :          * If the relation being detached is the default partition itself,
   21419             :          * remove it from the parent's pg_partitioned_table entry.
   21420             :          *
   21421             :          * If not, we must invalidate default partition's relcache entry, as
   21422             :          * in StorePartitionBound: its partition constraint depends on every
   21423             :          * other partition's partition constraint.
   21424             :          */
   21425         256 :         if (RelationGetRelid(partRel) == defaultPartOid)
   21426          44 :             update_default_partition_oid(RelationGetRelid(rel), InvalidOid);
   21427             :         else
   21428         212 :             CacheInvalidateRelcacheByRelid(defaultPartOid);
   21429             :     }
   21430             : 
   21431             :     /*
   21432             :      * Invalidate the parent's relcache so that the partition is no longer
   21433             :      * included in its partition descriptor.
   21434             :      */
   21435        1038 :     CacheInvalidateRelcache(rel);
   21436             : 
   21437             :     /*
   21438             :      * If the partition we just detached is partitioned itself, invalidate
   21439             :      * relcache for all descendent partitions too to ensure that their
   21440             :      * rd_partcheck expression trees are rebuilt; must lock partitions before
   21441             :      * doing so, using the same lockmode as what partRel has been locked with
   21442             :      * by the caller.
   21443             :      */
   21444        1038 :     if (partRel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
   21445             :     {
   21446             :         List       *children;
   21447             : 
   21448          62 :         children = find_all_inheritors(RelationGetRelid(partRel),
   21449             :                                        AccessExclusiveLock, NULL);
   21450         204 :         foreach(cell, children)
   21451             :         {
   21452         142 :             CacheInvalidateRelcacheByRelid(lfirst_oid(cell));
   21453             :         }
   21454             :     }
   21455        1038 : }
   21456             : 
   21457             : /*
   21458             :  * ALTER TABLE ... DETACH PARTITION ... FINALIZE
   21459             :  *
   21460             :  * To use when a DETACH PARTITION command previously did not run to
   21461             :  * completion; this completes the detaching process.
   21462             :  */
   21463             : static ObjectAddress
   21464          14 : ATExecDetachPartitionFinalize(Relation rel, RangeVar *name)
   21465             : {
   21466             :     Relation    partRel;
   21467             :     ObjectAddress address;
   21468          14 :     Snapshot    snap = GetActiveSnapshot();
   21469             : 
   21470          14 :     partRel = table_openrv(name, AccessExclusiveLock);
   21471             : 
   21472             :     /*
   21473             :      * Wait until existing snapshots are gone.  This is important if the
   21474             :      * second transaction of DETACH PARTITION CONCURRENTLY is canceled: the
   21475             :      * user could immediately run DETACH FINALIZE without actually waiting for
   21476             :      * existing transactions.  We must not complete the detach action until
   21477             :      * all such queries are complete (otherwise we would present them with an
   21478             :      * inconsistent view of catalogs).
   21479             :      */
   21480          14 :     WaitForOlderSnapshots(snap->xmin, false);
   21481             : 
   21482          14 :     DetachPartitionFinalize(rel, partRel, true, InvalidOid);
   21483             : 
   21484          14 :     ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partRel));
   21485             : 
   21486          14 :     table_close(partRel, NoLock);
   21487             : 
   21488          14 :     return address;
   21489             : }
   21490             : 
   21491             : /*
   21492             :  * DropClonedTriggersFromPartition
   21493             :  *      subroutine for ATExecDetachPartition to remove any triggers that were
   21494             :  *      cloned to the partition when it was created-as-partition or attached.
   21495             :  *      This undoes what CloneRowTriggersToPartition did.
   21496             :  */
   21497             : static void
   21498        1040 : DropClonedTriggersFromPartition(Oid partitionId)
   21499             : {
   21500             :     ScanKeyData skey;
   21501             :     SysScanDesc scan;
   21502             :     HeapTuple   trigtup;
   21503             :     Relation    tgrel;
   21504             :     ObjectAddresses *objects;
   21505             : 
   21506        1040 :     objects = new_object_addresses();
   21507             : 
   21508             :     /*
   21509             :      * Scan pg_trigger to search for all triggers on this rel.
   21510             :      */
   21511        1040 :     ScanKeyInit(&skey, Anum_pg_trigger_tgrelid, BTEqualStrategyNumber,
   21512             :                 F_OIDEQ, ObjectIdGetDatum(partitionId));
   21513        1040 :     tgrel = table_open(TriggerRelationId, RowExclusiveLock);
   21514        1040 :     scan = systable_beginscan(tgrel, TriggerRelidNameIndexId,
   21515             :                               true, NULL, 1, &skey);
   21516        1560 :     while (HeapTupleIsValid(trigtup = systable_getnext(scan)))
   21517             :     {
   21518         520 :         Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(trigtup);
   21519             :         ObjectAddress trig;
   21520             : 
   21521             :         /* Ignore triggers that weren't cloned */
   21522         520 :         if (!OidIsValid(pg_trigger->tgparentid))
   21523         460 :             continue;
   21524             : 
   21525             :         /*
   21526             :          * Ignore internal triggers that are implementation objects of foreign
   21527             :          * keys, because these will be detached when the foreign keys
   21528             :          * themselves are.
   21529             :          */
   21530         436 :         if (OidIsValid(pg_trigger->tgconstrrelid))
   21531         376 :             continue;
   21532             : 
   21533             :         /*
   21534             :          * This is ugly, but necessary: remove the dependency markings on the
   21535             :          * trigger so that it can be removed.
   21536             :          */
   21537          60 :         deleteDependencyRecordsForClass(TriggerRelationId, pg_trigger->oid,
   21538             :                                         TriggerRelationId,
   21539             :                                         DEPENDENCY_PARTITION_PRI);
   21540          60 :         deleteDependencyRecordsForClass(TriggerRelationId, pg_trigger->oid,
   21541             :                                         RelationRelationId,
   21542             :                                         DEPENDENCY_PARTITION_SEC);
   21543             : 
   21544             :         /* remember this trigger to remove it below */
   21545          60 :         ObjectAddressSet(trig, TriggerRelationId, pg_trigger->oid);
   21546          60 :         add_exact_object_address(&trig, objects);
   21547             :     }
   21548             : 
   21549             :     /* make the dependency removal visible to the deletion below */
   21550        1040 :     CommandCounterIncrement();
   21551        1040 :     performMultipleDeletions(objects, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
   21552             : 
   21553             :     /* done */
   21554        1040 :     free_object_addresses(objects);
   21555        1040 :     systable_endscan(scan);
   21556        1040 :     table_close(tgrel, RowExclusiveLock);
   21557        1040 : }
   21558             : 
   21559             : /*
   21560             :  * Before acquiring lock on an index, acquire the same lock on the owning
   21561             :  * table.
   21562             :  */
   21563             : struct AttachIndexCallbackState
   21564             : {
   21565             :     Oid         partitionOid;
   21566             :     Oid         parentTblOid;
   21567             :     bool        lockedParentTbl;
   21568             : };
   21569             : 
   21570             : static void
   21571         412 : RangeVarCallbackForAttachIndex(const RangeVar *rv, Oid relOid, Oid oldRelOid,
   21572             :                                void *arg)
   21573             : {
   21574             :     struct AttachIndexCallbackState *state;
   21575             :     Form_pg_class classform;
   21576             :     HeapTuple   tuple;
   21577             : 
   21578         412 :     state = (struct AttachIndexCallbackState *) arg;
   21579             : 
   21580         412 :     if (!state->lockedParentTbl)
   21581             :     {
   21582         396 :         LockRelationOid(state->parentTblOid, AccessShareLock);
   21583         396 :         state->lockedParentTbl = true;
   21584             :     }
   21585             : 
   21586             :     /*
   21587             :      * If we previously locked some other heap, and the name we're looking up
   21588             :      * no longer refers to an index on that relation, release the now-useless
   21589             :      * lock.  XXX maybe we should do *after* we verify whether the index does
   21590             :      * not actually belong to the same relation ...
   21591             :      */
   21592         412 :     if (relOid != oldRelOid && OidIsValid(state->partitionOid))
   21593             :     {
   21594           0 :         UnlockRelationOid(state->partitionOid, AccessShareLock);
   21595           0 :         state->partitionOid = InvalidOid;
   21596             :     }
   21597             : 
   21598             :     /* Didn't find a relation, so no need for locking or permission checks. */
   21599         412 :     if (!OidIsValid(relOid))
   21600           6 :         return;
   21601             : 
   21602         406 :     tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid));
   21603         406 :     if (!HeapTupleIsValid(tuple))
   21604           0 :         return;                 /* concurrently dropped, so nothing to do */
   21605         406 :     classform = (Form_pg_class) GETSTRUCT(tuple);
   21606         406 :     if (classform->relkind != RELKIND_PARTITIONED_INDEX &&
   21607         310 :         classform->relkind != RELKIND_INDEX)
   21608           6 :         ereport(ERROR,
   21609             :                 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
   21610             :                  errmsg("\"%s\" is not an index", rv->relname)));
   21611         400 :     ReleaseSysCache(tuple);
   21612             : 
   21613             :     /*
   21614             :      * Since we need only examine the heap's tupledesc, an access share lock
   21615             :      * on it (preventing any DDL) is sufficient.
   21616             :      */
   21617         400 :     state->partitionOid = IndexGetRelation(relOid, false);
   21618         400 :     LockRelationOid(state->partitionOid, AccessShareLock);
   21619             : }
   21620             : 
   21621             : /*
   21622             :  * ALTER INDEX i1 ATTACH PARTITION i2
   21623             :  */
   21624             : static ObjectAddress
   21625         396 : ATExecAttachPartitionIdx(List **wqueue, Relation parentIdx, RangeVar *name)
   21626             : {
   21627             :     Relation    partIdx;
   21628             :     Relation    partTbl;
   21629             :     Relation    parentTbl;
   21630             :     ObjectAddress address;
   21631             :     Oid         partIdxId;
   21632             :     Oid         currParent;
   21633             :     struct AttachIndexCallbackState state;
   21634             : 
   21635             :     /*
   21636             :      * We need to obtain lock on the index 'name' to modify it, but we also
   21637             :      * need to read its owning table's tuple descriptor -- so we need to lock
   21638             :      * both.  To avoid deadlocks, obtain lock on the table before doing so on
   21639             :      * the index.  Furthermore, we need to examine the parent table of the
   21640             :      * partition, so lock that one too.
   21641             :      */
   21642         396 :     state.partitionOid = InvalidOid;
   21643         396 :     state.parentTblOid = parentIdx->rd_index->indrelid;
   21644         396 :     state.lockedParentTbl = false;
   21645             :     partIdxId =
   21646         396 :         RangeVarGetRelidExtended(name, AccessExclusiveLock, 0,
   21647             :                                  RangeVarCallbackForAttachIndex,
   21648             :                                  &state);
   21649             :     /* Not there? */
   21650         384 :     if (!OidIsValid(partIdxId))
   21651           0 :         ereport(ERROR,
   21652             :                 (errcode(ERRCODE_UNDEFINED_OBJECT),
   21653             :                  errmsg("index \"%s\" does not exist", name->relname)));
   21654             : 
   21655             :     /* no deadlock risk: RangeVarGetRelidExtended already acquired the lock */
   21656         384 :     partIdx = relation_open(partIdxId, AccessExclusiveLock);
   21657             : 
   21658             :     /* we already hold locks on both tables, so this is safe: */
   21659         384 :     parentTbl = relation_open(parentIdx->rd_index->indrelid, AccessShareLock);
   21660         384 :     partTbl = relation_open(partIdx->rd_index->indrelid, NoLock);
   21661             : 
   21662         384 :     ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partIdx));
   21663             : 
   21664             :     /* Silently do nothing if already in the right state */
   21665         768 :     currParent = partIdx->rd_rel->relispartition ?
   21666         384 :         get_partition_parent(partIdxId, false) : InvalidOid;
   21667         384 :     if (currParent != RelationGetRelid(parentIdx))
   21668             :     {
   21669             :         IndexInfo  *childInfo;
   21670             :         IndexInfo  *parentInfo;
   21671             :         AttrMap    *attmap;
   21672             :         bool        found;
   21673             :         int         i;
   21674             :         PartitionDesc partDesc;
   21675             :         Oid         constraintOid,
   21676         360 :                     cldConstrId = InvalidOid;
   21677             : 
   21678             :         /*
   21679             :          * If this partition already has an index attached, refuse the
   21680             :          * operation.
   21681             :          */
   21682         360 :         refuseDupeIndexAttach(parentIdx, partIdx, partTbl);
   21683             : 
   21684         354 :         if (OidIsValid(currParent))
   21685           0 :             ereport(ERROR,
   21686             :                     (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
   21687             :                      errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
   21688             :                             RelationGetRelationName(partIdx),
   21689             :                             RelationGetRelationName(parentIdx)),
   21690             :                      errdetail("Index \"%s\" is already attached to another index.",
   21691             :                                RelationGetRelationName(partIdx))));
   21692             : 
   21693             :         /* Make sure it indexes a partition of the other index's table */
   21694         354 :         partDesc = RelationGetPartitionDesc(parentTbl, true);
   21695         354 :         found = false;
   21696         556 :         for (i = 0; i < partDesc->nparts; i++)
   21697             :         {
   21698         550 :             if (partDesc->oids[i] == state.partitionOid)
   21699             :             {
   21700         348 :                 found = true;
   21701         348 :                 break;
   21702             :             }
   21703             :         }
   21704         354 :         if (!found)
   21705           6 :             ereport(ERROR,
   21706             :                     (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
   21707             :                      errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
   21708             :                             RelationGetRelationName(partIdx),
   21709             :                             RelationGetRelationName(parentIdx)),
   21710             :                      errdetail("Index \"%s\" is not an index on any partition of table \"%s\".",
   21711             :                                RelationGetRelationName(partIdx),
   21712             :                                RelationGetRelationName(parentTbl))));
   21713             : 
   21714             :         /* Ensure the indexes are compatible */
   21715         348 :         childInfo = BuildIndexInfo(partIdx);
   21716         348 :         parentInfo = BuildIndexInfo(parentIdx);
   21717         348 :         attmap = build_attrmap_by_name(RelationGetDescr(partTbl),
   21718             :                                        RelationGetDescr(parentTbl),
   21719             :                                        false);
   21720         348 :         if (!CompareIndexInfo(childInfo, parentInfo,
   21721         348 :                               partIdx->rd_indcollation,
   21722         348 :                               parentIdx->rd_indcollation,
   21723         348 :                               partIdx->rd_opfamily,
   21724         348 :                               parentIdx->rd_opfamily,
   21725             :                               attmap))
   21726          42 :             ereport(ERROR,
   21727             :                     (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
   21728             :                      errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
   21729             :                             RelationGetRelationName(partIdx),
   21730             :                             RelationGetRelationName(parentIdx)),
   21731             :                      errdetail("The index definitions do not match.")));
   21732             : 
   21733             :         /*
   21734             :          * If there is a constraint in the parent, make sure there is one in
   21735             :          * the child too.
   21736             :          */
   21737         306 :         constraintOid = get_relation_idx_constraint_oid(RelationGetRelid(parentTbl),
   21738             :                                                         RelationGetRelid(parentIdx));
   21739             : 
   21740         306 :         if (OidIsValid(constraintOid))
   21741             :         {
   21742         126 :             cldConstrId = get_relation_idx_constraint_oid(RelationGetRelid(partTbl),
   21743             :                                                           partIdxId);
   21744         126 :             if (!OidIsValid(cldConstrId))
   21745           6 :                 ereport(ERROR,
   21746             :                         (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
   21747             :                          errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
   21748             :                                 RelationGetRelationName(partIdx),
   21749             :                                 RelationGetRelationName(parentIdx)),
   21750             :                          errdetail("The index \"%s\" belongs to a constraint in table \"%s\" but no constraint exists for index \"%s\".",
   21751             :                                    RelationGetRelationName(parentIdx),
   21752             :                                    RelationGetRelationName(parentTbl),
   21753             :                                    RelationGetRelationName(partIdx))));
   21754             :         }
   21755             : 
   21756             :         /*
   21757             :          * If it's a primary key, make sure the columns in the partition are
   21758             :          * NOT NULL.
   21759             :          */
   21760         300 :         if (parentIdx->rd_index->indisprimary)
   21761          96 :             verifyPartitionIndexNotNull(childInfo, partTbl);
   21762             : 
   21763             :         /* All good -- do it */
   21764         300 :         IndexSetParentIndex(partIdx, RelationGetRelid(parentIdx));
   21765         300 :         if (OidIsValid(constraintOid))
   21766         120 :             ConstraintSetParentConstraint(cldConstrId, constraintOid,
   21767             :                                           RelationGetRelid(partTbl));
   21768             : 
   21769         300 :         free_attrmap(attmap);
   21770             : 
   21771         300 :         validatePartitionedIndex(parentIdx, parentTbl);
   21772             :     }
   21773             : 
   21774         324 :     relation_close(parentTbl, AccessShareLock);
   21775             :     /* keep these locks till commit */
   21776         324 :     relation_close(partTbl, NoLock);
   21777         324 :     relation_close(partIdx, NoLock);
   21778             : 
   21779         324 :     return address;
   21780             : }
   21781             : 
   21782             : /*
   21783             :  * Verify whether the given partition already contains an index attached
   21784             :  * to the given partitioned index.  If so, raise an error.
   21785             :  */
   21786             : static void
   21787         360 : refuseDupeIndexAttach(Relation parentIdx, Relation partIdx, Relation partitionTbl)
   21788             : {
   21789             :     Oid         existingIdx;
   21790             : 
   21791         360 :     existingIdx = index_get_partition(partitionTbl,
   21792             :                                       RelationGetRelid(parentIdx));
   21793         360 :     if (OidIsValid(existingIdx))
   21794           6 :         ereport(ERROR,
   21795             :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
   21796             :                  errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
   21797             :                         RelationGetRelationName(partIdx),
   21798             :                         RelationGetRelationName(parentIdx)),
   21799             :                  errdetail("Another index \"%s\" is already attached for partition \"%s\".",
   21800             :                            get_rel_name(existingIdx),
   21801             :                            RelationGetRelationName(partitionTbl))));
   21802         354 : }
   21803             : 
   21804             : /*
   21805             :  * Verify whether the set of attached partition indexes to a parent index on
   21806             :  * a partitioned table is complete.  If it is, mark the parent index valid.
   21807             :  *
   21808             :  * This should be called each time a partition index is attached.
   21809             :  */
   21810             : static void
   21811         342 : validatePartitionedIndex(Relation partedIdx, Relation partedTbl)
   21812             : {
   21813             :     Relation    inheritsRel;
   21814             :     SysScanDesc scan;
   21815             :     ScanKeyData key;
   21816         342 :     int         tuples = 0;
   21817             :     HeapTuple   inhTup;
   21818         342 :     bool        updated = false;
   21819             : 
   21820             :     Assert(partedIdx->rd_rel->relkind == RELKIND_PARTITIONED_INDEX);
   21821             : 
   21822             :     /*
   21823             :      * Scan pg_inherits for this parent index.  Count each valid index we find
   21824             :      * (verifying the pg_index entry for each), and if we reach the total
   21825             :      * amount we expect, we can mark this parent index as valid.
   21826             :      */
   21827         342 :     inheritsRel = table_open(InheritsRelationId, AccessShareLock);
   21828         342 :     ScanKeyInit(&key, Anum_pg_inherits_inhparent,
   21829             :                 BTEqualStrategyNumber, F_OIDEQ,
   21830             :                 ObjectIdGetDatum(RelationGetRelid(partedIdx)));
   21831         342 :     scan = systable_beginscan(inheritsRel, InheritsParentIndexId, true,
   21832             :                               NULL, 1, &key);
   21833         888 :     while ((inhTup = systable_getnext(scan)) != NULL)
   21834             :     {
   21835         546 :         Form_pg_inherits inhForm = (Form_pg_inherits) GETSTRUCT(inhTup);
   21836             :         HeapTuple   indTup;
   21837             :         Form_pg_index indexForm;
   21838             : 
   21839         546 :         indTup = SearchSysCache1(INDEXRELID,
   21840             :                                  ObjectIdGetDatum(inhForm->inhrelid));
   21841         546 :         if (!HeapTupleIsValid(indTup))
   21842           0 :             elog(ERROR, "cache lookup failed for index %u", inhForm->inhrelid);
   21843         546 :         indexForm = (Form_pg_index) GETSTRUCT(indTup);
   21844         546 :         if (indexForm->indisvalid)
   21845         488 :             tuples += 1;
   21846         546 :         ReleaseSysCache(indTup);
   21847             :     }
   21848             : 
   21849             :     /* Done with pg_inherits */
   21850         342 :     systable_endscan(scan);
   21851         342 :     table_close(inheritsRel, AccessShareLock);
   21852             : 
   21853             :     /*
   21854             :      * If we found as many inherited indexes as the partitioned table has
   21855             :      * partitions, we're good; update pg_index to set indisvalid.
   21856             :      */
   21857         342 :     if (tuples == RelationGetPartitionDesc(partedTbl, true)->nparts)
   21858             :     {
   21859             :         Relation    idxRel;
   21860             :         HeapTuple   indTup;
   21861             :         Form_pg_index indexForm;
   21862             : 
   21863         172 :         idxRel = table_open(IndexRelationId, RowExclusiveLock);
   21864         172 :         indTup = SearchSysCacheCopy1(INDEXRELID,
   21865             :                                      ObjectIdGetDatum(RelationGetRelid(partedIdx)));
   21866         172 :         if (!HeapTupleIsValid(indTup))
   21867           0 :             elog(ERROR, "cache lookup failed for index %u",
   21868             :                  RelationGetRelid(partedIdx));
   21869         172 :         indexForm = (Form_pg_index) GETSTRUCT(indTup);
   21870             : 
   21871         172 :         indexForm->indisvalid = true;
   21872         172 :         updated = true;
   21873             : 
   21874         172 :         CatalogTupleUpdate(idxRel, &indTup->t_self, indTup);
   21875             : 
   21876         172 :         table_close(idxRel, RowExclusiveLock);
   21877         172 :         heap_freetuple(indTup);
   21878             :     }
   21879             : 
   21880             :     /*
   21881             :      * If this index is in turn a partition of a larger index, validating it
   21882             :      * might cause the parent to become valid also.  Try that.
   21883             :      */
   21884         342 :     if (updated && partedIdx->rd_rel->relispartition)
   21885             :     {
   21886             :         Oid         parentIdxId,
   21887             :                     parentTblId;
   21888             :         Relation    parentIdx,
   21889             :                     parentTbl;
   21890             : 
   21891             :         /* make sure we see the validation we just did */
   21892          42 :         CommandCounterIncrement();
   21893             : 
   21894          42 :         parentIdxId = get_partition_parent(RelationGetRelid(partedIdx), false);
   21895          42 :         parentTblId = get_partition_parent(RelationGetRelid(partedTbl), false);
   21896          42 :         parentIdx = relation_open(parentIdxId, AccessExclusiveLock);
   21897          42 :         parentTbl = relation_open(parentTblId, AccessExclusiveLock);
   21898             :         Assert(!parentIdx->rd_index->indisvalid);
   21899             : 
   21900          42 :         validatePartitionedIndex(parentIdx, parentTbl);
   21901             : 
   21902          42 :         relation_close(parentIdx, AccessExclusiveLock);
   21903          42 :         relation_close(parentTbl, AccessExclusiveLock);
   21904             :     }
   21905         342 : }
   21906             : 
   21907             : /*
   21908             :  * When attaching an index as a partition of a partitioned index which is a
   21909             :  * primary key, verify that all the columns in the partition are marked NOT
   21910             :  * NULL.
   21911             :  */
   21912             : static void
   21913          96 : verifyPartitionIndexNotNull(IndexInfo *iinfo, Relation partition)
   21914             : {
   21915         194 :     for (int i = 0; i < iinfo->ii_NumIndexKeyAttrs; i++)
   21916             :     {
   21917          98 :         Form_pg_attribute att = TupleDescAttr(RelationGetDescr(partition),
   21918          98 :                                               iinfo->ii_IndexAttrNumbers[i] - 1);
   21919             : 
   21920          98 :         if (!att->attnotnull)
   21921           0 :             ereport(ERROR,
   21922             :                     errcode(ERRCODE_INVALID_TABLE_DEFINITION),
   21923             :                     errmsg("invalid primary key definition"),
   21924             :                     errdetail("Column \"%s\" of relation \"%s\" is not marked NOT NULL.",
   21925             :                               NameStr(att->attname),
   21926             :                               RelationGetRelationName(partition)));
   21927             :     }
   21928          96 : }
   21929             : 
   21930             : /*
   21931             :  * Return an OID list of constraints that reference the given relation
   21932             :  * that are marked as having a parent constraints.
   21933             :  */
   21934             : static List *
   21935        1580 : GetParentedForeignKeyRefs(Relation partition)
   21936             : {
   21937             :     Relation    pg_constraint;
   21938             :     HeapTuple   tuple;
   21939             :     SysScanDesc scan;
   21940             :     ScanKeyData key[2];
   21941        1580 :     List       *constraints = NIL;
   21942             : 
   21943             :     /*
   21944             :      * If no indexes, or no columns are referenceable by FKs, we can avoid the
   21945             :      * scan.
   21946             :      */
   21947        2218 :     if (RelationGetIndexList(partition) == NIL ||
   21948         638 :         bms_is_empty(RelationGetIndexAttrBitmap(partition,
   21949             :                                                 INDEX_ATTR_BITMAP_KEY)))
   21950        1194 :         return NIL;
   21951             : 
   21952             :     /* Search for constraints referencing this table */
   21953         386 :     pg_constraint = table_open(ConstraintRelationId, AccessShareLock);
   21954         386 :     ScanKeyInit(&key[0],
   21955             :                 Anum_pg_constraint_confrelid, BTEqualStrategyNumber,
   21956             :                 F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(partition)));
   21957         386 :     ScanKeyInit(&key[1],
   21958             :                 Anum_pg_constraint_contype, BTEqualStrategyNumber,
   21959             :                 F_CHAREQ, CharGetDatum(CONSTRAINT_FOREIGN));
   21960             : 
   21961             :     /* XXX This is a seqscan, as we don't have a usable index */
   21962         386 :     scan = systable_beginscan(pg_constraint, InvalidOid, true, NULL, 2, key);
   21963         588 :     while ((tuple = systable_getnext(scan)) != NULL)
   21964             :     {
   21965         202 :         Form_pg_constraint constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
   21966             : 
   21967             :         /*
   21968             :          * We only need to process constraints that are part of larger ones.
   21969             :          */
   21970         202 :         if (!OidIsValid(constrForm->conparentid))
   21971           0 :             continue;
   21972             : 
   21973         202 :         constraints = lappend_oid(constraints, constrForm->oid);
   21974             :     }
   21975             : 
   21976         386 :     systable_endscan(scan);
   21977         386 :     table_close(pg_constraint, AccessShareLock);
   21978             : 
   21979         386 :     return constraints;
   21980             : }
   21981             : 
   21982             : /*
   21983             :  * During DETACH PARTITION, verify that any foreign keys pointing to the
   21984             :  * partitioned table would not become invalid.  An error is raised if any
   21985             :  * referenced values exist.
   21986             :  */
   21987             : static void
   21988         540 : ATDetachCheckNoForeignKeyRefs(Relation partition)
   21989             : {
   21990             :     List       *constraints;
   21991             :     ListCell   *cell;
   21992             : 
   21993         540 :     constraints = GetParentedForeignKeyRefs(partition);
   21994             : 
   21995         616 :     foreach(cell, constraints)
   21996             :     {
   21997         110 :         Oid         constrOid = lfirst_oid(cell);
   21998             :         HeapTuple   tuple;
   21999             :         Form_pg_constraint constrForm;
   22000             :         Relation    rel;
   22001         110 :         Trigger     trig = {0};
   22002             : 
   22003         110 :         tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constrOid));
   22004         110 :         if (!HeapTupleIsValid(tuple))
   22005           0 :             elog(ERROR, "cache lookup failed for constraint %u", constrOid);
   22006         110 :         constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
   22007             : 
   22008             :         Assert(OidIsValid(constrForm->conparentid));
   22009             :         Assert(constrForm->confrelid == RelationGetRelid(partition));
   22010             : 
   22011             :         /* prevent data changes into the referencing table until commit */
   22012         110 :         rel = table_open(constrForm->conrelid, ShareLock);
   22013             : 
   22014         110 :         trig.tgoid = InvalidOid;
   22015         110 :         trig.tgname = NameStr(constrForm->conname);
   22016         110 :         trig.tgenabled = TRIGGER_FIRES_ON_ORIGIN;
   22017         110 :         trig.tgisinternal = true;
   22018         110 :         trig.tgconstrrelid = RelationGetRelid(partition);
   22019         110 :         trig.tgconstrindid = constrForm->conindid;
   22020         110 :         trig.tgconstraint = constrForm->oid;
   22021         110 :         trig.tgdeferrable = false;
   22022         110 :         trig.tginitdeferred = false;
   22023             :         /* we needn't fill in remaining fields */
   22024             : 
   22025         110 :         RI_PartitionRemove_Check(&trig, rel, partition);
   22026             : 
   22027          76 :         ReleaseSysCache(tuple);
   22028             : 
   22029          76 :         table_close(rel, NoLock);
   22030             :     }
   22031         506 : }
   22032             : 
   22033             : /*
   22034             :  * resolve column compression specification to compression method.
   22035             :  */
   22036             : static char
   22037      269882 : GetAttributeCompression(Oid atttypid, const char *compression)
   22038             : {
   22039             :     char        cmethod;
   22040             : 
   22041      269882 :     if (compression == NULL || strcmp(compression, "default") == 0)
   22042      269668 :         return InvalidCompressionMethod;
   22043             : 
   22044             :     /*
   22045             :      * To specify a nondefault method, the column data type must be toastable.
   22046             :      * Note this says nothing about whether the column's attstorage setting
   22047             :      * permits compression; we intentionally allow attstorage and
   22048             :      * attcompression to be independent.  But with a non-toastable type,
   22049             :      * attstorage could not be set to a value that would permit compression.
   22050             :      *
   22051             :      * We don't actually need to enforce this, since nothing bad would happen
   22052             :      * if attcompression were non-default; it would never be consulted.  But
   22053             :      * it seems more user-friendly to complain about a certainly-useless
   22054             :      * attempt to set the property.
   22055             :      */
   22056         214 :     if (!TypeIsToastable(atttypid))
   22057           6 :         ereport(ERROR,
   22058             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   22059             :                  errmsg("column data type %s does not support compression",
   22060             :                         format_type_be(atttypid))));
   22061             : 
   22062         208 :     cmethod = CompressionNameToMethod(compression);
   22063         208 :     if (!CompressionMethodIsValid(cmethod))
   22064          12 :         ereport(ERROR,
   22065             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
   22066             :                  errmsg("invalid compression method \"%s\"", compression)));
   22067             : 
   22068         196 :     return cmethod;
   22069             : }
   22070             : 
   22071             : /*
   22072             :  * resolve column storage specification
   22073             :  */
   22074             : static char
   22075         304 : GetAttributeStorage(Oid atttypid, const char *storagemode)
   22076             : {
   22077         304 :     char        cstorage = 0;
   22078             : 
   22079         304 :     if (pg_strcasecmp(storagemode, "plain") == 0)
   22080          56 :         cstorage = TYPSTORAGE_PLAIN;
   22081         248 :     else if (pg_strcasecmp(storagemode, "external") == 0)
   22082         176 :         cstorage = TYPSTORAGE_EXTERNAL;
   22083          72 :     else if (pg_strcasecmp(storagemode, "extended") == 0)
   22084          28 :         cstorage = TYPSTORAGE_EXTENDED;
   22085          44 :     else if (pg_strcasecmp(storagemode, "main") == 0)
   22086          38 :         cstorage = TYPSTORAGE_MAIN;
   22087           6 :     else if (pg_strcasecmp(storagemode, "default") == 0)
   22088           6 :         cstorage = get_typstorage(atttypid);
   22089             :     else
   22090           0 :         ereport(ERROR,
   22091             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
   22092             :                  errmsg("invalid storage type \"%s\"",
   22093             :                         storagemode)));
   22094             : 
   22095             :     /*
   22096             :      * safety check: do not allow toasted storage modes unless column datatype
   22097             :      * is TOAST-aware.
   22098             :      */
   22099         304 :     if (!(cstorage == TYPSTORAGE_PLAIN || TypeIsToastable(atttypid)))
   22100           6 :         ereport(ERROR,
   22101             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   22102             :                  errmsg("column data type %s can only have storage PLAIN",
   22103             :                         format_type_be(atttypid))));
   22104             : 
   22105         298 :     return cstorage;
   22106             : }
   22107             : 
   22108             : /*
   22109             :  * buildExpressionExecutionStates: build the needed expression execution states
   22110             :  * for new partition (newPartRel) checks and initialize expressions for
   22111             :  * generated columns. All expressions should be created in "tab"
   22112             :  * (AlteredTableInfo structure).
   22113             :  */
   22114             : static void
   22115         648 : buildExpressionExecutionStates(AlteredTableInfo *tab, Relation newPartRel, EState *estate)
   22116             : {
   22117             :     /*
   22118             :      * Build the needed expression execution states. Here, we expect only NOT
   22119             :      * NULL and CHECK constraint.
   22120             :      */
   22121        1320 :     foreach_ptr(NewConstraint, con, tab->constraints)
   22122             :     {
   22123          24 :         switch (con->contype)
   22124             :         {
   22125          24 :             case CONSTR_CHECK:
   22126             : 
   22127             :                 /*
   22128             :                  * We already expanded virtual expression in
   22129             :                  * createTableConstraints.
   22130             :                  */
   22131          24 :                 con->qualstate = ExecPrepareExpr((Expr *) con->qual, estate);
   22132          24 :                 break;
   22133           0 :             case CONSTR_NOTNULL:
   22134             :                 /* Nothing to do here. */
   22135           0 :                 break;
   22136           0 :             default:
   22137           0 :                 elog(ERROR, "unrecognized constraint type: %d",
   22138             :                      (int) con->contype);
   22139             :         }
   22140             :     }
   22141             : 
   22142             :     /* Expression already planned in createTableConstraints */
   22143        1362 :     foreach_ptr(NewColumnValue, ex, tab->newvals)
   22144          66 :         ex->exprstate = ExecInitExpr((Expr *) ex->expr, NULL);
   22145         648 : }
   22146             : 
   22147             : /*
   22148             :  * evaluateGeneratedExpressionsAndCheckConstraints: evaluate any generated
   22149             :  * expressions for "tab" (AlteredTableInfo structure) whose inputs come from
   22150             :  * the new tuple (insertslot) of the new partition (newPartRel).
   22151             :  */
   22152             : static void
   22153        1018 : evaluateGeneratedExpressionsAndCheckConstraints(AlteredTableInfo *tab,
   22154             :                                                 Relation newPartRel,
   22155             :                                                 TupleTableSlot *insertslot,
   22156             :                                                 ExprContext *econtext)
   22157             : {
   22158        1018 :     econtext->ecxt_scantuple = insertslot;
   22159             : 
   22160        2132 :     foreach_ptr(NewColumnValue, ex, tab->newvals)
   22161             :     {
   22162          96 :         if (!ex->is_generated)
   22163           0 :             continue;
   22164             : 
   22165          96 :         insertslot->tts_values[ex->attnum - 1]
   22166          96 :             = ExecEvalExpr(ex->exprstate,
   22167             :                            econtext,
   22168          96 :                            &insertslot->tts_isnull[ex->attnum - 1]);
   22169             :     }
   22170             : 
   22171        2072 :     foreach_ptr(NewConstraint, con, tab->constraints)
   22172             :     {
   22173          36 :         switch (con->contype)
   22174             :         {
   22175          36 :             case CONSTR_CHECK:
   22176          36 :                 if (!ExecCheck(con->qualstate, econtext))
   22177           0 :                     ereport(ERROR,
   22178             :                             errcode(ERRCODE_CHECK_VIOLATION),
   22179             :                             errmsg("check constraint \"%s\" of relation \"%s\" is violated by some row",
   22180             :                                    con->name, RelationGetRelationName(newPartRel)),
   22181             :                             errtableconstraint(newPartRel, con->name));
   22182          36 :                 break;
   22183           0 :             case CONSTR_NOTNULL:
   22184             :             case CONSTR_FOREIGN:
   22185             :                 /* Nothing to do here */
   22186           0 :                 break;
   22187           0 :             default:
   22188           0 :                 elog(ERROR, "unrecognized constraint type: %d",
   22189             :                      (int) con->contype);
   22190             :         }
   22191             :     }
   22192        1018 : }
   22193             : 
   22194             : /*
   22195             :  * getAttributesList: build a list of columns (ColumnDef) based on parent_rel
   22196             :  */
   22197             : static List *
   22198         678 : getAttributesList(Relation parent_rel)
   22199             : {
   22200             :     AttrNumber  parent_attno;
   22201             :     TupleDesc   modelDesc;
   22202         678 :     List       *colList = NIL;
   22203             : 
   22204         678 :     modelDesc = RelationGetDescr(parent_rel);
   22205             : 
   22206        2424 :     for (parent_attno = 1; parent_attno <= modelDesc->natts;
   22207        1746 :          parent_attno++)
   22208             :     {
   22209        1746 :         Form_pg_attribute attribute = TupleDescAttr(modelDesc,
   22210             :                                                     parent_attno - 1);
   22211             :         ColumnDef  *def;
   22212             : 
   22213             :         /* Ignore dropped columns in the parent. */
   22214        1746 :         if (attribute->attisdropped)
   22215           0 :             continue;
   22216             : 
   22217        1746 :         def = makeColumnDef(NameStr(attribute->attname), attribute->atttypid,
   22218             :                             attribute->atttypmod, attribute->attcollation);
   22219             : 
   22220        1746 :         def->is_not_null = attribute->attnotnull;
   22221             : 
   22222             :         /* Copy identity. */
   22223        1746 :         def->identity = attribute->attidentity;
   22224             : 
   22225             :         /* Copy attgenerated. */
   22226        1746 :         def->generated = attribute->attgenerated;
   22227             : 
   22228        1746 :         def->storage = attribute->attstorage;
   22229             : 
   22230             :         /* Likewise, copy compression. */
   22231        1746 :         if (CompressionMethodIsValid(attribute->attcompression))
   22232          18 :             def->compression =
   22233          18 :                 pstrdup(GetCompressionMethodName(attribute->attcompression));
   22234             :         else
   22235        1728 :             def->compression = NULL;
   22236             : 
   22237             :         /* Add to column list. */
   22238        1746 :         colList = lappend(colList, def);
   22239             :     }
   22240             : 
   22241         678 :     return colList;
   22242             : }
   22243             : 
   22244             : /*
   22245             :  * createTableConstraints:
   22246             :  * create check constraints, default values, and generated values for newRel
   22247             :  * based on parent_rel.  tab is pending-work queue for newRel, we may need it in
   22248             :  * MergePartitionsMoveRows.
   22249             :  */
   22250             : static void
   22251         648 : createTableConstraints(List **wqueue, AlteredTableInfo *tab,
   22252             :                        Relation parent_rel, Relation newRel)
   22253             : {
   22254             :     TupleDesc   tupleDesc;
   22255             :     TupleConstr *constr;
   22256             :     AttrMap    *attmap;
   22257             :     AttrNumber  parent_attno;
   22258             :     int         ccnum;
   22259         648 :     List       *constraints = NIL;
   22260         648 :     List       *cookedConstraints = NIL;
   22261             : 
   22262         648 :     tupleDesc = RelationGetDescr(parent_rel);
   22263         648 :     constr = tupleDesc->constr;
   22264             : 
   22265         648 :     if (!constr)
   22266         414 :         return;
   22267             : 
   22268             :     /*
   22269             :      * Construct a map from the parent relation's attnos to the child rel's.
   22270             :      * This re-checks type match, etc, although it shouldn't be possible to
   22271             :      * have a failure since both tables are locked.
   22272             :      */
   22273         234 :     attmap = build_attrmap_by_name(RelationGetDescr(newRel),
   22274             :                                    tupleDesc,
   22275             :                                    false);
   22276             : 
   22277             :     /* Cycle for default values. */
   22278         888 :     for (parent_attno = 1; parent_attno <= tupleDesc->natts; parent_attno++)
   22279             :     {
   22280         654 :         Form_pg_attribute attribute = TupleDescAttr(tupleDesc,
   22281             :                                                     parent_attno - 1);
   22282             : 
   22283             :         /* Ignore dropped columns in the parent. */
   22284         654 :         if (attribute->attisdropped)
   22285           0 :             continue;
   22286             : 
   22287             :         /* Copy the default, if present, and it should be copied. */
   22288         654 :         if (attribute->atthasdef)
   22289             :         {
   22290         150 :             Node       *this_default = NULL;
   22291             :             bool        found_whole_row;
   22292             :             AttrNumber  num;
   22293             :             Node       *def;
   22294             :             NewColumnValue *newval;
   22295             : 
   22296         150 :             if (attribute->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
   22297           6 :                 this_default = build_generation_expression(parent_rel, attribute->attnum);
   22298             :             else
   22299             :             {
   22300         144 :                 this_default = TupleDescGetDefault(tupleDesc, attribute->attnum);
   22301         144 :                 if (this_default == NULL)
   22302           0 :                     elog(ERROR, "default expression not found for attribute %d of relation \"%s\"",
   22303             :                          attribute->attnum, RelationGetRelationName(parent_rel));
   22304             :             }
   22305             : 
   22306         150 :             num = attmap->attnums[parent_attno - 1];
   22307         150 :             def = map_variable_attnos(this_default, 1, 0, attmap, InvalidOid, &found_whole_row);
   22308             : 
   22309         150 :             if (found_whole_row && attribute->attgenerated != '\0')
   22310           0 :                 elog(ERROR, "cannot convert whole-row table reference");
   22311             : 
   22312             :             /* Add a pre-cooked default expression. */
   22313         150 :             StoreAttrDefault(newRel, num, def, true);
   22314             : 
   22315             :             /*
   22316             :              * Stored generated column expressions in parent_rel might
   22317             :              * reference the tableoid.  newRel, parent_rel tableoid clear is
   22318             :              * not the same. If so, these stored generated columns require
   22319             :              * recomputation for newRel within MergePartitionsMoveRows.
   22320             :              */
   22321         150 :             if (attribute->attgenerated == ATTRIBUTE_GENERATED_STORED)
   22322             :             {
   22323          66 :                 newval = palloc0_object(NewColumnValue);
   22324          66 :                 newval->attnum = num;
   22325          66 :                 newval->expr = expression_planner((Expr *) def);
   22326          66 :                 newval->is_generated = (attribute->attgenerated != '\0');
   22327          66 :                 tab->newvals = lappend(tab->newvals, newval);
   22328             :             }
   22329             :         }
   22330             :     }
   22331             : 
   22332             :     /* Cycle for CHECK constraints. */
   22333         336 :     for (ccnum = 0; ccnum < constr->num_check; ccnum++)
   22334             :     {
   22335         102 :         char       *ccname = constr->check[ccnum].ccname;
   22336         102 :         char       *ccbin = constr->check[ccnum].ccbin;
   22337         102 :         bool        ccenforced = constr->check[ccnum].ccenforced;
   22338         102 :         bool        ccnoinherit = constr->check[ccnum].ccnoinherit;
   22339         102 :         bool        ccvalid = constr->check[ccnum].ccvalid;
   22340             :         Node       *ccbin_node;
   22341             :         bool        found_whole_row;
   22342             :         Constraint *constr;
   22343             : 
   22344             :         /*
   22345             :          * The partitioned table can not have a NO INHERIT check constraint
   22346             :          * (see StoreRelCheck function for details).
   22347             :          */
   22348             :         Assert(!ccnoinherit);
   22349             : 
   22350         102 :         ccbin_node = map_variable_attnos(stringToNode(ccbin),
   22351             :                                          1, 0,
   22352             :                                          attmap,
   22353             :                                          InvalidOid, &found_whole_row);
   22354             : 
   22355             :         /*
   22356             :          * For the moment we have to reject whole-row variables (as for CREATE
   22357             :          * TABLE LIKE and inheritances).
   22358             :          */
   22359         102 :         if (found_whole_row)
   22360           0 :             elog(ERROR, "Constraint \"%s\" contains a whole-row reference to table \"%s\".",
   22361             :                  ccname,
   22362             :                  RelationGetRelationName(parent_rel));
   22363             : 
   22364         102 :         constr = makeNode(Constraint);
   22365         102 :         constr->contype = CONSTR_CHECK;
   22366         102 :         constr->conname = pstrdup(ccname);
   22367         102 :         constr->deferrable = false;
   22368         102 :         constr->initdeferred = false;
   22369         102 :         constr->is_enforced = ccenforced;
   22370         102 :         constr->skip_validation = !ccvalid;
   22371         102 :         constr->initially_valid = ccvalid;
   22372         102 :         constr->is_no_inherit = ccnoinherit;
   22373         102 :         constr->raw_expr = NULL;
   22374         102 :         constr->cooked_expr = nodeToString(ccbin_node);
   22375         102 :         constr->location = -1;
   22376         102 :         constraints = lappend(constraints, constr);
   22377             :     }
   22378             : 
   22379             :     /* Install all CHECK constraints. */
   22380         234 :     cookedConstraints = AddRelationNewConstraints(newRel, NIL, constraints,
   22381             :                                                   false, true, true, NULL);
   22382             : 
   22383             :     /* Make the additional catalog changes visible. */
   22384         234 :     CommandCounterIncrement();
   22385             : 
   22386             :     /*
   22387             :      * parent_rel check constraint expression may reference tableoid, so later
   22388             :      * in MergePartitionsMoveRows, we need to evaluate the check constraint
   22389             :      * again for the newRel. We can check whether the check constraint
   22390             :      * contains a tableoid reference via pull_varattnos.
   22391             :      */
   22392         570 :     foreach_ptr(CookedConstraint, ccon, cookedConstraints)
   22393             :     {
   22394         102 :         if (!ccon->skip_validation)
   22395             :         {
   22396             :             Node       *qual;
   22397          66 :             Bitmapset  *attnums = NULL;
   22398             : 
   22399             :             Assert(ccon->contype == CONSTR_CHECK);
   22400          66 :             qual = expand_generated_columns_in_expr(ccon->expr, newRel, 1);
   22401          66 :             pull_varattnos(qual, 1, &attnums);
   22402             : 
   22403             :             /*
   22404             :              * Add a check only if it contains a tableoid
   22405             :              * (TableOidAttributeNumber).
   22406             :              */
   22407          66 :             if (bms_is_member(TableOidAttributeNumber - FirstLowInvalidHeapAttributeNumber,
   22408             :                               attnums))
   22409             :             {
   22410             :                 NewConstraint *newcon;
   22411             : 
   22412          24 :                 newcon = palloc0_object(NewConstraint);
   22413          24 :                 newcon->name = ccon->name;
   22414          24 :                 newcon->contype = CONSTR_CHECK;
   22415          24 :                 newcon->qual = qual;
   22416             : 
   22417          24 :                 tab->constraints = lappend(tab->constraints, newcon);
   22418             :             }
   22419             :         }
   22420             :     }
   22421             : 
   22422             :     /* Don't need the cookedConstraints anymore. */
   22423         234 :     list_free_deep(cookedConstraints);
   22424             : 
   22425             :     /* Reproduce not-null constraints. */
   22426         234 :     if (constr->has_not_null)
   22427             :     {
   22428             :         List       *nnconstraints;
   22429             : 
   22430             :         /*
   22431             :          * The "include_noinh" argument is false because a partitioned table
   22432             :          * can't have NO INHERIT constraint.
   22433             :          */
   22434         162 :         nnconstraints = RelationGetNotNullConstraints(RelationGetRelid(parent_rel),
   22435             :                                                       false, false);
   22436             : 
   22437             :         Assert(list_length(nnconstraints) > 0);
   22438             : 
   22439             :         /*
   22440             :          * We already set pg_attribute.attnotnull in createPartitionTable. No
   22441             :          * need call set_attnotnull again.
   22442             :          */
   22443         162 :         AddRelationNewConstraints(newRel, NIL, nnconstraints, false, true, true, NULL);
   22444             :     }
   22445             : }
   22446             : 
   22447             : /*
   22448             :  * createPartitionTable:
   22449             :  *
   22450             :  * Create a new partition (newPartName) for the partitioned table (parent_rel).
   22451             :  * ownerId is determined by the partition on which the operation is performed,
   22452             :  * so it is passed separately.  The new partition will inherit the access method
   22453             :  * and persistence type from the parent table.
   22454             :  *
   22455             :  * Returns the created relation (locked in AccessExclusiveLock mode).
   22456             :  */
   22457             : static Relation
   22458         678 : createPartitionTable(List **wqueue, RangeVar *newPartName,
   22459             :                      Relation parent_rel, Oid ownerId)
   22460             : {
   22461             :     Relation    newRel;
   22462             :     Oid         newRelId;
   22463             :     Oid         existingRelid;
   22464             :     TupleDesc   descriptor;
   22465         678 :     List       *colList = NIL;
   22466             :     Oid         relamId;
   22467             :     Oid         namespaceId;
   22468             :     AlteredTableInfo *new_partrel_tab;
   22469         678 :     Form_pg_class parent_relform = parent_rel->rd_rel;
   22470             : 
   22471             :     /* If the existing rel is temp, it must belong to this session. */
   22472         678 :     if (RELATION_IS_OTHER_TEMP(parent_rel))
   22473           0 :         ereport(ERROR,
   22474             :                 errcode(ERRCODE_WRONG_OBJECT_TYPE),
   22475             :                 errmsg("cannot create as partition of temporary relation of another session"));
   22476             : 
   22477             :     /* Look up inheritance ancestors and generate the relation schema. */
   22478         678 :     colList = getAttributesList(parent_rel);
   22479             : 
   22480             :     /* Create a tuple descriptor from the relation schema. */
   22481         678 :     descriptor = BuildDescForRelation(colList);
   22482             : 
   22483             :     /* Look up the access method for the new relation. */
   22484         678 :     relamId = (parent_relform->relam != InvalidOid) ? parent_relform->relam : HEAP_TABLE_AM_OID;
   22485             : 
   22486             :     /* Look up the namespace in which we are supposed to create the relation. */
   22487             :     namespaceId =
   22488         678 :         RangeVarGetAndCheckCreationNamespace(newPartName, NoLock, &existingRelid);
   22489         678 :     if (OidIsValid(existingRelid))
   22490           0 :         ereport(ERROR,
   22491             :                 errcode(ERRCODE_DUPLICATE_TABLE),
   22492             :                 errmsg("relation \"%s\" already exists", newPartName->relname));
   22493             : 
   22494             :     /*
   22495             :      * We intended to create the partition with the same persistence as the
   22496             :      * parent table, but we still need to recheck because that might be
   22497             :      * affected by the search_path.  If the parent is permanent, so must be
   22498             :      * all of its partitions.
   22499             :      */
   22500         678 :     if (parent_relform->relpersistence != RELPERSISTENCE_TEMP &&
   22501         624 :         newPartName->relpersistence == RELPERSISTENCE_TEMP)
   22502          12 :         ereport(ERROR,
   22503             :                 errcode(ERRCODE_WRONG_OBJECT_TYPE),
   22504             :                 errmsg("cannot create a temporary relation as partition of permanent relation \"%s\"",
   22505             :                        RelationGetRelationName(parent_rel)));
   22506             : 
   22507             :     /* Permanent rels cannot be partitions belonging to a temporary parent. */
   22508         666 :     if (newPartName->relpersistence != RELPERSISTENCE_TEMP &&
   22509         630 :         parent_relform->relpersistence == RELPERSISTENCE_TEMP)
   22510          18 :         ereport(ERROR,
   22511             :                 errcode(ERRCODE_WRONG_OBJECT_TYPE),
   22512             :                 errmsg("cannot create a permanent relation as partition of temporary relation \"%s\"",
   22513             :                        RelationGetRelationName(parent_rel)));
   22514             : 
   22515             :     /* Create the relation. */
   22516         648 :     newRelId = heap_create_with_catalog(newPartName->relname,
   22517             :                                         namespaceId,
   22518             :                                         parent_relform->reltablespace,
   22519             :                                         InvalidOid,
   22520             :                                         InvalidOid,
   22521             :                                         InvalidOid,
   22522             :                                         ownerId,
   22523             :                                         relamId,
   22524             :                                         descriptor,
   22525             :                                         NIL,
   22526             :                                         RELKIND_RELATION,
   22527         648 :                                         newPartName->relpersistence,
   22528             :                                         false,
   22529             :                                         false,
   22530             :                                         ONCOMMIT_NOOP,
   22531             :                                         (Datum) 0,
   22532             :                                         true,
   22533             :                                         allowSystemTableMods,
   22534             :                                         true,
   22535             :                                         InvalidOid,
   22536             :                                         NULL);
   22537             : 
   22538             :     /*
   22539             :      * We must bump the command counter to make the newly-created relation
   22540             :      * tuple visible for opening.
   22541             :      */
   22542         648 :     CommandCounterIncrement();
   22543             : 
   22544             :     /*
   22545             :      * Open the new partition with no lock, because we already have an
   22546             :      * AccessExclusiveLock placed there after creation.
   22547             :      */
   22548         648 :     newRel = table_open(newRelId, NoLock);
   22549             : 
   22550             :     /* Find or create a work queue entry for the newly created table. */
   22551         648 :     new_partrel_tab = ATGetQueueEntry(wqueue, newRel);
   22552             : 
   22553             :     /* Create constraints, default values, and generated values. */
   22554         648 :     createTableConstraints(wqueue, new_partrel_tab, parent_rel, newRel);
   22555             : 
   22556             :     /*
   22557             :      * Need to call CommandCounterIncrement, so a fresh relcache entry has
   22558             :      * newly installed constraint info.
   22559             :      */
   22560         648 :     CommandCounterIncrement();
   22561             : 
   22562         648 :     return newRel;
   22563             : }
   22564             : 
   22565             : /*
   22566             :  * MergePartitionsMoveRows: scan partitions to be merged (mergingPartitions)
   22567             :  * of the partitioned table and move rows into the new partition
   22568             :  * (newPartRel). We also verify check constraints against these rows.
   22569             :  */
   22570             : static void
   22571         138 : MergePartitionsMoveRows(List **wqueue, List *mergingPartitions, Relation newPartRel)
   22572             : {
   22573             :     CommandId   mycid;
   22574             :     EState     *estate;
   22575             :     AlteredTableInfo *tab;
   22576             :     ListCell   *ltab;
   22577             : 
   22578             :     /* The FSM is empty, so don't bother using it. */
   22579         138 :     int         ti_options = TABLE_INSERT_SKIP_FSM;
   22580             :     BulkInsertState bistate;    /* state of bulk inserts for partition */
   22581             :     TupleTableSlot *dstslot;
   22582             : 
   22583             :     /* Find the work queue entry for the new partition table: newPartRel. */
   22584         138 :     tab = ATGetQueueEntry(wqueue, newPartRel);
   22585             : 
   22586             :     /* Generate the constraint and default execution states. */
   22587         138 :     estate = CreateExecutorState();
   22588             : 
   22589         138 :     buildExpressionExecutionStates(tab, newPartRel, estate);
   22590             : 
   22591         138 :     mycid = GetCurrentCommandId(true);
   22592             : 
   22593             :     /* Prepare a BulkInsertState for table_tuple_insert. */
   22594         138 :     bistate = GetBulkInsertState();
   22595             : 
   22596             :     /* Create the necessary tuple slot. */
   22597         138 :     dstslot = table_slot_create(newPartRel, NULL);
   22598             : 
   22599         594 :     foreach_oid(merging_oid, mergingPartitions)
   22600             :     {
   22601             :         ExprContext *econtext;
   22602             :         TupleTableSlot *srcslot;
   22603             :         TupleConversionMap *tuple_map;
   22604             :         TableScanDesc scan;
   22605             :         MemoryContext oldCxt;
   22606             :         Snapshot    snapshot;
   22607             :         Relation    mergingPartition;
   22608             : 
   22609         318 :         econtext = GetPerTupleExprContext(estate);
   22610             : 
   22611             :         /*
   22612             :          * Partition is already locked in the transformPartitionCmdForMerge
   22613             :          * function.
   22614             :          */
   22615         318 :         mergingPartition = table_open(merging_oid, NoLock);
   22616             : 
   22617             :         /* Create a source tuple slot for the partition being merged. */
   22618         318 :         srcslot = table_slot_create(mergingPartition, NULL);
   22619             : 
   22620             :         /*
   22621             :          * Map computing for moving attributes of the merged partition to the
   22622             :          * new partition.
   22623             :          */
   22624         318 :         tuple_map = convert_tuples_by_name(RelationGetDescr(mergingPartition),
   22625             :                                            RelationGetDescr(newPartRel));
   22626             : 
   22627             :         /* Scan through the rows. */
   22628         318 :         snapshot = RegisterSnapshot(GetLatestSnapshot());
   22629         318 :         scan = table_beginscan(mergingPartition, snapshot, 0, NULL);
   22630             : 
   22631             :         /*
   22632             :          * Switch to per-tuple memory context and reset it for each tuple
   22633             :          * produced, so we don't leak memory.
   22634             :          */
   22635         318 :         oldCxt = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
   22636             : 
   22637         696 :         while (table_scan_getnextslot(scan, ForwardScanDirection, srcslot))
   22638             :         {
   22639             :             TupleTableSlot *insertslot;
   22640             : 
   22641         378 :             CHECK_FOR_INTERRUPTS();
   22642             : 
   22643         378 :             if (tuple_map)
   22644             :             {
   22645             :                 /* Need to use a map to copy attributes. */
   22646          42 :                 insertslot = execute_attr_map_slot(tuple_map->attrMap, srcslot, dstslot);
   22647             :             }
   22648             :             else
   22649             :             {
   22650         336 :                 slot_getallattrs(srcslot);
   22651             : 
   22652             :                 /* Copy attributes directly. */
   22653         336 :                 insertslot = dstslot;
   22654             : 
   22655         336 :                 ExecClearTuple(insertslot);
   22656             : 
   22657         336 :                 memcpy(insertslot->tts_values, srcslot->tts_values,
   22658         336 :                        sizeof(Datum) * srcslot->tts_nvalid);
   22659         336 :                 memcpy(insertslot->tts_isnull, srcslot->tts_isnull,
   22660         336 :                        sizeof(bool) * srcslot->tts_nvalid);
   22661             : 
   22662         336 :                 ExecStoreVirtualTuple(insertslot);
   22663             :             }
   22664             : 
   22665             :             /*
   22666             :              * Constraints and GENERATED expressions might reference the
   22667             :              * tableoid column, so fill tts_tableOid with the desired value.
   22668             :              * (We must do this each time, because it gets overwritten with
   22669             :              * newrel's OID during storing.)
   22670             :              */
   22671         378 :             insertslot->tts_tableOid = RelationGetRelid(newPartRel);
   22672             : 
   22673             :             /*
   22674             :              * Now, evaluate any generated expressions whose inputs come from
   22675             :              * the new tuple.  We assume these columns won't reference each
   22676             :              * other, so that there's no ordering dependency.
   22677             :              */
   22678         378 :             evaluateGeneratedExpressionsAndCheckConstraints(tab, newPartRel,
   22679             :                                                             insertslot, econtext);
   22680             : 
   22681             :             /* Write the tuple out to the new relation. */
   22682         378 :             table_tuple_insert(newPartRel, insertslot, mycid,
   22683             :                                ti_options, bistate);
   22684             : 
   22685         378 :             ResetExprContext(econtext);
   22686             :         }
   22687             : 
   22688         318 :         MemoryContextSwitchTo(oldCxt);
   22689         318 :         table_endscan(scan);
   22690         318 :         UnregisterSnapshot(snapshot);
   22691             : 
   22692         318 :         if (tuple_map)
   22693          30 :             free_conversion_map(tuple_map);
   22694             : 
   22695         318 :         ExecDropSingleTupleTableSlot(srcslot);
   22696         318 :         table_close(mergingPartition, NoLock);
   22697             :     }
   22698             : 
   22699         138 :     FreeExecutorState(estate);
   22700         138 :     ExecDropSingleTupleTableSlot(dstslot);
   22701         138 :     FreeBulkInsertState(bistate);
   22702             : 
   22703         138 :     table_finish_bulk_insert(newPartRel, ti_options);
   22704             : 
   22705             :     /*
   22706             :      * We don't need to process this newPartRel since we already processed it
   22707             :      * here, so delete the ALTER TABLE queue for it.
   22708             :      */
   22709         276 :     foreach(ltab, *wqueue)
   22710             :     {
   22711         276 :         tab = (AlteredTableInfo *) lfirst(ltab);
   22712         276 :         if (tab->relid == RelationGetRelid(newPartRel))
   22713             :         {
   22714         138 :             *wqueue = list_delete_cell(*wqueue, ltab);
   22715         138 :             break;
   22716             :         }
   22717             :     }
   22718         138 : }
   22719             : 
   22720             : /*
   22721             :  * detachPartitionTable: detach partition "child_rel" from partitioned table
   22722             :  * "parent_rel" with default partition identifier "defaultPartOid"
   22723             :  */
   22724             : static void
   22725         570 : detachPartitionTable(Relation parent_rel, Relation child_rel, Oid defaultPartOid)
   22726             : {
   22727             :     /* Remove the pg_inherits row first. */
   22728         570 :     RemoveInheritance(child_rel, parent_rel, false);
   22729             : 
   22730             :     /*
   22731             :      * Detaching the partition might involve TOAST table access, so ensure we
   22732             :      * have a valid snapshot.
   22733             :      */
   22734         570 :     PushActiveSnapshot(GetTransactionSnapshot());
   22735             : 
   22736             :     /* Do the final part of detaching. */
   22737         570 :     DetachPartitionFinalize(parent_rel, child_rel, false, defaultPartOid);
   22738             : 
   22739         570 :     PopActiveSnapshot();
   22740         570 : }
   22741             : 
   22742             : /*
   22743             :  * ALTER TABLE <name> MERGE PARTITIONS <partition-list> INTO <partition-name>
   22744             :  */
   22745             : static void
   22746         180 : ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel,
   22747             :                       PartitionCmd *cmd, AlterTableUtilityContext *context)
   22748             : {
   22749             :     Relation    newPartRel;
   22750         180 :     List       *mergingPartitions = NIL;
   22751             :     Oid         defaultPartOid;
   22752             :     Oid         existingRelid;
   22753         180 :     Oid         ownerId = InvalidOid;
   22754             :     Oid         save_userid;
   22755             :     int         save_sec_context;
   22756             :     int         save_nestlevel;
   22757             : 
   22758             :     /*
   22759             :      * Check ownership of merged partitions - partitions with different owners
   22760             :      * cannot be merged. Also, collect the OIDs of these partitions during the
   22761             :      * check.
   22762             :      */
   22763         756 :     foreach_node(RangeVar, name, cmd->partlist)
   22764             :     {
   22765             :         Relation    mergingPartition;
   22766             : 
   22767             :         /*
   22768             :          * We are going to detach and remove this partition.  We already took
   22769             :          * AccessExclusiveLock lock on transformPartitionCmdForMerge, so here,
   22770             :          * NoLock is fine.
   22771             :          */
   22772         408 :         mergingPartition = table_openrv_extended(name, NoLock, false);
   22773             :         Assert(CheckRelationLockedByMe(mergingPartition, AccessExclusiveLock, false));
   22774             : 
   22775         408 :         if (OidIsValid(ownerId))
   22776             :         {
   22777             :             /* Do the partitions being merged have different owners? */
   22778         228 :             if (ownerId != mergingPartition->rd_rel->relowner)
   22779           6 :                 ereport(ERROR,
   22780             :                         errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   22781             :                         errmsg("partitions being merged have different owners"));
   22782             :         }
   22783             :         else
   22784         180 :             ownerId = mergingPartition->rd_rel->relowner;
   22785             : 
   22786             :         /* Store the next merging partition into the list. */
   22787         402 :         mergingPartitions = lappend_oid(mergingPartitions,
   22788             :                                         RelationGetRelid(mergingPartition));
   22789             : 
   22790         402 :         table_close(mergingPartition, NoLock);
   22791             :     }
   22792             : 
   22793             :     /* Look up the existing relation by the new partition name. */
   22794         174 :     RangeVarGetAndCheckCreationNamespace(cmd->name, NoLock, &existingRelid);
   22795             : 
   22796             :     /*
   22797             :      * Check if this name is already taken.  This helps us to detect the
   22798             :      * situation when one of the merging partitions has the same name as the
   22799             :      * new partition.  Otherwise, this would fail later on anyway, but
   22800             :      * catching this here allows us to emit a nicer error message.
   22801             :      */
   22802         174 :     if (OidIsValid(existingRelid))
   22803             :     {
   22804          26 :         if (list_member_oid(mergingPartitions, existingRelid))
   22805             :         {
   22806             :             /*
   22807             :              * The new partition has the same name as one of the merging
   22808             :              * partitions.
   22809             :              */
   22810             :             char        tmpRelName[NAMEDATALEN];
   22811             : 
   22812             :             /* Generate a temporary name. */
   22813          20 :             sprintf(tmpRelName, "merge-%u-%X-tmp", RelationGetRelid(rel), MyProcPid);
   22814             : 
   22815             :             /*
   22816             :              * Rename the existing partition with a temporary name, leaving it
   22817             :              * free for the new partition.  We don't need to care about this
   22818             :              * in the future because we're going to eventually drop the
   22819             :              * existing partition anyway.
   22820             :              */
   22821          20 :             RenameRelationInternal(existingRelid, tmpRelName, true, false);
   22822             : 
   22823             :             /*
   22824             :              * We must bump the command counter to make the new partition
   22825             :              * tuple visible for rename.
   22826             :              */
   22827          20 :             CommandCounterIncrement();
   22828             :         }
   22829             :         else
   22830             :         {
   22831           6 :             ereport(ERROR,
   22832             :                     errcode(ERRCODE_DUPLICATE_TABLE),
   22833             :                     errmsg("relation \"%s\" already exists", cmd->name->relname));
   22834             :         }
   22835             :     }
   22836             : 
   22837             :     defaultPartOid =
   22838         168 :         get_default_oid_from_partdesc(RelationGetPartitionDesc(rel, true));
   22839             : 
   22840             :     /* Detach all merging partitions. */
   22841         714 :     foreach_oid(mergingPartitionOid, mergingPartitions)
   22842             :     {
   22843             :         Relation    child_rel;
   22844             : 
   22845         378 :         child_rel = table_open(mergingPartitionOid, NoLock);
   22846             : 
   22847         378 :         detachPartitionTable(rel, child_rel, defaultPartOid);
   22848             : 
   22849         378 :         table_close(child_rel, NoLock);
   22850             :     }
   22851             : 
   22852             :     /*
   22853             :      * Perform a preliminary check to determine whether it's safe to drop all
   22854             :      * merging partitions before we actually do so later. After merging rows
   22855             :      * into the new partitions via MergePartitionsMoveRows, all old partitions
   22856             :      * need to be dropped. However, since the drop behavior is DROP_RESTRICT
   22857             :      * and the merge process (MergePartitionsMoveRows) can be time-consuming,
   22858             :      * performing an early check on the drop eligibility of old partitions is
   22859             :      * preferable.
   22860             :      */
   22861         696 :     foreach_oid(mergingPartitionOid, mergingPartitions)
   22862             :     {
   22863             :         ObjectAddress object;
   22864             : 
   22865             :         /* Get oid of the later to be dropped relation. */
   22866         372 :         object.objectId = mergingPartitionOid;
   22867         372 :         object.classId = RelationRelationId;
   22868         372 :         object.objectSubId = 0;
   22869             : 
   22870         372 :         performDeletionCheck(&object, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
   22871             :     }
   22872             : 
   22873             :     /*
   22874             :      * Create a table for the new partition, using the partitioned table as a
   22875             :      * model.
   22876             :      */
   22877             :     Assert(OidIsValid(ownerId));
   22878         162 :     newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId);
   22879             : 
   22880             :     /*
   22881             :      * Switch to the table owner's userid, so that any index functions are run
   22882             :      * as that user.  Also, lockdown security-restricted operations and
   22883             :      * arrange to make GUC variable changes local to this command.
   22884             :      *
   22885             :      * Need to do it after determining the namespace in the
   22886             :      * createPartitionTable() call.
   22887             :      */
   22888         138 :     GetUserIdAndSecContext(&save_userid, &save_sec_context);
   22889         138 :     SetUserIdAndSecContext(ownerId,
   22890             :                            save_sec_context | SECURITY_RESTRICTED_OPERATION);
   22891         138 :     save_nestlevel = NewGUCNestLevel();
   22892         138 :     RestrictSearchPath();
   22893             : 
   22894             :     /* Copy data from merged partitions to the new partition. */
   22895         138 :     MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel);
   22896             : 
   22897             :     /* Drop the current partitions before attaching the new one. */
   22898         594 :     foreach_oid(mergingPartitionOid, mergingPartitions)
   22899             :     {
   22900             :         ObjectAddress object;
   22901             : 
   22902         318 :         object.objectId = mergingPartitionOid;
   22903         318 :         object.classId = RelationRelationId;
   22904         318 :         object.objectSubId = 0;
   22905             : 
   22906         318 :         performDeletion(&object, DROP_RESTRICT, 0);
   22907             :     }
   22908             : 
   22909         138 :     list_free(mergingPartitions);
   22910             : 
   22911             :     /*
   22912             :      * Attach a new partition to the partitioned table. wqueue = NULL:
   22913             :      * verification for each cloned constraint is not needed.
   22914             :      */
   22915         138 :     attachPartitionTable(NULL, rel, newPartRel, cmd->bound);
   22916             : 
   22917             :     /* Keep the lock until commit. */
   22918         138 :     table_close(newPartRel, NoLock);
   22919             : 
   22920             :     /* Roll back any GUC changes executed by index functions. */
   22921         138 :     AtEOXact_GUC(false, save_nestlevel);
   22922             : 
   22923             :     /* Restore the userid and security context. */
   22924         138 :     SetUserIdAndSecContext(save_userid, save_sec_context);
   22925         138 : }
   22926             : 
   22927             : /*
   22928             :  * Struct with the context of the new partition for inserting rows from the
   22929             :  * split partition.
   22930             :  */
   22931             : typedef struct SplitPartitionContext
   22932             : {
   22933             :     ExprState  *partqualstate;  /* expression for checking a slot for a
   22934             :                                  * partition (NULL for DEFAULT partition) */
   22935             :     BulkInsertState bistate;    /* state of bulk inserts for partition */
   22936             :     TupleTableSlot *dstslot;    /* slot for inserting row into partition */
   22937             :     AlteredTableInfo *tab;      /* structure with generated column expressions
   22938             :                                  * and check constraint expressions. */
   22939             :     Relation    partRel;        /* relation for partition */
   22940             : } SplitPartitionContext;
   22941             : 
   22942             : /*
   22943             :  * createSplitPartitionContext: create context for partition and fill it
   22944             :  */
   22945             : static SplitPartitionContext *
   22946         510 : createSplitPartitionContext(Relation partRel)
   22947             : {
   22948             :     SplitPartitionContext *pc;
   22949             : 
   22950         510 :     pc = palloc0_object(SplitPartitionContext);
   22951         510 :     pc->partRel = partRel;
   22952             : 
   22953             :     /*
   22954             :      * Prepare a BulkInsertState for table_tuple_insert. The FSM is empty, so
   22955             :      * don't bother using it.
   22956             :      */
   22957         510 :     pc->bistate = GetBulkInsertState();
   22958             : 
   22959             :     /* Create a destination tuple slot for the new partition. */
   22960         510 :     pc->dstslot = table_slot_create(pc->partRel, NULL);
   22961             : 
   22962         510 :     return pc;
   22963             : }
   22964             : 
   22965             : /*
   22966             :  * deleteSplitPartitionContext: delete context for partition
   22967             :  */
   22968             : static void
   22969         510 : deleteSplitPartitionContext(SplitPartitionContext *pc, List **wqueue, int ti_options)
   22970             : {
   22971             :     ListCell   *ltab;
   22972             : 
   22973         510 :     ExecDropSingleTupleTableSlot(pc->dstslot);
   22974         510 :     FreeBulkInsertState(pc->bistate);
   22975             : 
   22976         510 :     table_finish_bulk_insert(pc->partRel, ti_options);
   22977             : 
   22978             :     /*
   22979             :      * We don't need to process this pc->partRel so delete the ALTER TABLE
   22980             :      * queue of it.
   22981             :      */
   22982        1020 :     foreach(ltab, *wqueue)
   22983             :     {
   22984        1020 :         AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
   22985             : 
   22986        1020 :         if (tab->relid == RelationGetRelid(pc->partRel))
   22987             :         {
   22988         510 :             *wqueue = list_delete_cell(*wqueue, ltab);
   22989         510 :             break;
   22990             :         }
   22991             :     }
   22992             : 
   22993         510 :     pfree(pc);
   22994         510 : }
   22995             : 
   22996             : /*
   22997             :  * SplitPartitionMoveRows: scan split partition (splitRel) of partitioned table
   22998             :  * (rel) and move rows into new partitions.
   22999             :  *
   23000             :  * New partitions description:
   23001             :  * partlist: list of pointers to SinglePartitionSpec structures.  It contains
   23002             :  * the partition specification details for all new partitions.
   23003             :  * newPartRels: list of Relations, new partitions created in
   23004             :  * ATExecSplitPartition.
   23005             :  */
   23006             : static void
   23007         186 : SplitPartitionMoveRows(List **wqueue, Relation rel, Relation splitRel,
   23008             :                        List *partlist, List *newPartRels)
   23009             : {
   23010             :     /* The FSM is empty, so don't bother using it. */
   23011         186 :     int         ti_options = TABLE_INSERT_SKIP_FSM;
   23012             :     CommandId   mycid;
   23013             :     EState     *estate;
   23014             :     ListCell   *listptr,
   23015             :                *listptr2;
   23016             :     TupleTableSlot *srcslot;
   23017             :     ExprContext *econtext;
   23018             :     TableScanDesc scan;
   23019             :     Snapshot    snapshot;
   23020             :     MemoryContext oldCxt;
   23021         186 :     List       *partContexts = NIL;
   23022             :     TupleConversionMap *tuple_map;
   23023         186 :     SplitPartitionContext *defaultPartCtx = NULL,
   23024             :                *pc;
   23025             : 
   23026         186 :     mycid = GetCurrentCommandId(true);
   23027             : 
   23028         186 :     estate = CreateExecutorState();
   23029             : 
   23030         696 :     forboth(listptr, partlist, listptr2, newPartRels)
   23031             :     {
   23032         510 :         SinglePartitionSpec *sps = (SinglePartitionSpec *) lfirst(listptr);
   23033             : 
   23034         510 :         pc = createSplitPartitionContext((Relation) lfirst(listptr2));
   23035             : 
   23036             :         /* Find the work queue entry for the new partition table: newPartRel. */
   23037         510 :         pc->tab = ATGetQueueEntry(wqueue, pc->partRel);
   23038             : 
   23039         510 :         buildExpressionExecutionStates(pc->tab, pc->partRel, estate);
   23040             : 
   23041         510 :         if (sps->bound->is_default)
   23042             :         {
   23043             :             /*
   23044             :              * We should not create a structure to check the partition
   23045             :              * constraint for the new DEFAULT partition.
   23046             :              */
   23047          42 :             defaultPartCtx = pc;
   23048             :         }
   23049             :         else
   23050             :         {
   23051             :             List       *partConstraint;
   23052             : 
   23053             :             /* Build expression execution states for partition check quals. */
   23054         468 :             partConstraint = get_qual_from_partbound(rel, sps->bound);
   23055             :             partConstraint =
   23056         468 :                 (List *) eval_const_expressions(NULL,
   23057             :                                                 (Node *) partConstraint);
   23058             :             /* Make a boolean expression for ExecCheck(). */
   23059         468 :             partConstraint = list_make1(make_ands_explicit(partConstraint));
   23060             : 
   23061             :             /*
   23062             :              * Map the vars in the constraint expression from rel's attnos to
   23063             :              * splitRel's.
   23064             :              */
   23065         468 :             partConstraint = map_partition_varattnos(partConstraint,
   23066             :                                                      1, splitRel, rel);
   23067             : 
   23068         468 :             pc->partqualstate =
   23069         468 :                 ExecPrepareExpr((Expr *) linitial(partConstraint), estate);
   23070             :             Assert(pc->partqualstate != NULL);
   23071             :         }
   23072             : 
   23073             :         /* Store partition context into a list. */
   23074         510 :         partContexts = lappend(partContexts, pc);
   23075             :     }
   23076             : 
   23077         186 :     econtext = GetPerTupleExprContext(estate);
   23078             : 
   23079             :     /* Create the necessary tuple slot. */
   23080         186 :     srcslot = table_slot_create(splitRel, NULL);
   23081             : 
   23082             :     /*
   23083             :      * Map computing for moving attributes of the split partition to the new
   23084             :      * partition (for the first new partition, but other new partitions can
   23085             :      * use the same map).
   23086             :      */
   23087         186 :     pc = (SplitPartitionContext *) lfirst(list_head(partContexts));
   23088         186 :     tuple_map = convert_tuples_by_name(RelationGetDescr(splitRel),
   23089         186 :                                        RelationGetDescr(pc->partRel));
   23090             : 
   23091             :     /* Scan through the rows. */
   23092         186 :     snapshot = RegisterSnapshot(GetLatestSnapshot());
   23093         186 :     scan = table_beginscan(splitRel, snapshot, 0, NULL);
   23094             : 
   23095             :     /*
   23096             :      * Switch to per-tuple memory context and reset it for each tuple
   23097             :      * produced, so we don't leak memory.
   23098             :      */
   23099         186 :     oldCxt = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
   23100             : 
   23101         826 :     while (table_scan_getnextslot(scan, ForwardScanDirection, srcslot))
   23102             :     {
   23103         640 :         bool        found = false;
   23104             :         TupleTableSlot *insertslot;
   23105             : 
   23106         640 :         CHECK_FOR_INTERRUPTS();
   23107             : 
   23108         640 :         econtext->ecxt_scantuple = srcslot;
   23109             : 
   23110             :         /* Search partition for the current slot, srcslot. */
   23111        1718 :         foreach(listptr, partContexts)
   23112             :         {
   23113        1604 :             pc = (SplitPartitionContext *) lfirst(listptr);
   23114             : 
   23115             :             /* skip DEFAULT partition */
   23116        1604 :             if (pc->partqualstate && ExecCheck(pc->partqualstate, econtext))
   23117             :             {
   23118         526 :                 found = true;
   23119         526 :                 break;
   23120             :             }
   23121             :         }
   23122         640 :         if (!found)
   23123             :         {
   23124             :             /* Use the DEFAULT partition if it exists. */
   23125         114 :             if (defaultPartCtx)
   23126         114 :                 pc = defaultPartCtx;
   23127             :             else
   23128           0 :                 ereport(ERROR,
   23129             :                         errcode(ERRCODE_CHECK_VIOLATION),
   23130             :                         errmsg("can not find partition for split partition row"),
   23131             :                         errtable(splitRel));
   23132             :         }
   23133             : 
   23134         640 :         if (tuple_map)
   23135             :         {
   23136             :             /* Need to use a map to copy attributes. */
   23137          24 :             insertslot = execute_attr_map_slot(tuple_map->attrMap, srcslot, pc->dstslot);
   23138             :         }
   23139             :         else
   23140             :         {
   23141             :             /* Extract data from the old tuple. */
   23142         616 :             slot_getallattrs(srcslot);
   23143             : 
   23144             :             /* Copy attributes directly. */
   23145         616 :             insertslot = pc->dstslot;
   23146             : 
   23147         616 :             ExecClearTuple(insertslot);
   23148             : 
   23149         616 :             memcpy(insertslot->tts_values, srcslot->tts_values,
   23150         616 :                    sizeof(Datum) * srcslot->tts_nvalid);
   23151         616 :             memcpy(insertslot->tts_isnull, srcslot->tts_isnull,
   23152         616 :                    sizeof(bool) * srcslot->tts_nvalid);
   23153             : 
   23154         616 :             ExecStoreVirtualTuple(insertslot);
   23155             :         }
   23156             : 
   23157             :         /*
   23158             :          * Constraints and GENERATED expressions might reference the tableoid
   23159             :          * column, so fill tts_tableOid with the desired value. (We must do
   23160             :          * this each time, because it gets overwritten with newrel's OID
   23161             :          * during storing.)
   23162             :          */
   23163         640 :         insertslot->tts_tableOid = RelationGetRelid(pc->partRel);
   23164             : 
   23165             :         /*
   23166             :          * Now, evaluate any generated expressions whose inputs come from the
   23167             :          * new tuple.  We assume these columns won't reference each other, so
   23168             :          * that there's no ordering dependency.
   23169             :          */
   23170         640 :         evaluateGeneratedExpressionsAndCheckConstraints(pc->tab, pc->partRel,
   23171             :                                                         insertslot, econtext);
   23172             : 
   23173             :         /* Write the tuple out to the new relation. */
   23174         640 :         table_tuple_insert(pc->partRel, insertslot, mycid,
   23175         640 :                            ti_options, pc->bistate);
   23176             : 
   23177         640 :         ResetExprContext(econtext);
   23178             :     }
   23179             : 
   23180         186 :     MemoryContextSwitchTo(oldCxt);
   23181             : 
   23182         186 :     table_endscan(scan);
   23183         186 :     UnregisterSnapshot(snapshot);
   23184             : 
   23185         186 :     if (tuple_map)
   23186           6 :         free_conversion_map(tuple_map);
   23187             : 
   23188         186 :     ExecDropSingleTupleTableSlot(srcslot);
   23189             : 
   23190         186 :     FreeExecutorState(estate);
   23191             : 
   23192         882 :     foreach_ptr(SplitPartitionContext, spc, partContexts)
   23193         510 :         deleteSplitPartitionContext(spc, wqueue, ti_options);
   23194         186 : }
   23195             : 
   23196             : /*
   23197             :  * ALTER TABLE <name> SPLIT PARTITION <partition-name> INTO <partition-list>
   23198             :  */
   23199             : static void
   23200         198 : ATExecSplitPartition(List **wqueue, AlteredTableInfo *tab, Relation rel,
   23201             :                      PartitionCmd *cmd, AlterTableUtilityContext *context)
   23202             : {
   23203             :     Relation    splitRel;
   23204             :     Oid         splitRelOid;
   23205             :     ListCell   *listptr,
   23206             :                *listptr2;
   23207         198 :     bool        isSameName = false;
   23208             :     char        tmpRelName[NAMEDATALEN];
   23209         198 :     List       *newPartRels = NIL;
   23210             :     ObjectAddress object;
   23211             :     Oid         defaultPartOid;
   23212             :     Oid         save_userid;
   23213             :     int         save_sec_context;
   23214             :     int         save_nestlevel;
   23215             : 
   23216         198 :     defaultPartOid = get_default_oid_from_partdesc(RelationGetPartitionDesc(rel, true));
   23217             : 
   23218             :     /*
   23219             :      * Partition is already locked in the transformPartitionCmdForSplit
   23220             :      * function.
   23221             :      */
   23222         198 :     splitRel = table_openrv(cmd->name, NoLock);
   23223             : 
   23224         198 :     splitRelOid = RelationGetRelid(splitRel);
   23225             : 
   23226             :     /* Check descriptions of new partitions. */
   23227         912 :     foreach_node(SinglePartitionSpec, sps, cmd->partlist)
   23228             :     {
   23229             :         Oid         existingRelid;
   23230             : 
   23231             :         /* Look up the existing relation by the new partition name. */
   23232         528 :         RangeVarGetAndCheckCreationNamespace(sps->name, NoLock, &existingRelid);
   23233             : 
   23234             :         /*
   23235             :          * This would fail later on anyway if the relation already exists. But
   23236             :          * by catching it here, we can emit a nicer error message.
   23237             :          */
   23238         528 :         if (existingRelid == splitRelOid && !isSameName)
   23239             :             /* One new partition can have the same name as a split partition. */
   23240          44 :             isSameName = true;
   23241         484 :         else if (OidIsValid(existingRelid))
   23242           6 :             ereport(ERROR,
   23243             :                     errcode(ERRCODE_DUPLICATE_TABLE),
   23244             :                     errmsg("relation \"%s\" already exists", sps->name->relname));
   23245             :     }
   23246             : 
   23247             :     /* Detach the split partition. */
   23248         192 :     detachPartitionTable(rel, splitRel, defaultPartOid);
   23249             : 
   23250             :     /*
   23251             :      * Perform a preliminary check to determine whether it's safe to drop the
   23252             :      * split partition before we actually do so later. After merging rows into
   23253             :      * the new partitions via SplitPartitionMoveRows, all old partitions need
   23254             :      * to be dropped. However, since the drop behavior is DROP_RESTRICT and
   23255             :      * the merge process (SplitPartitionMoveRows) can be time-consuming,
   23256             :      * performing an early check on the drop eligibility of old partitions is
   23257             :      * preferable.
   23258             :      */
   23259         192 :     object.objectId = splitRelOid;
   23260         192 :     object.classId = RelationRelationId;
   23261         192 :     object.objectSubId = 0;
   23262         192 :     performDeletionCheck(&object, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
   23263             : 
   23264             :     /*
   23265             :      * If a new partition has the same name as the split partition, then we
   23266             :      * should rename the split partition to reuse its name.
   23267             :      */
   23268         192 :     if (isSameName)
   23269             :     {
   23270             :         /*
   23271             :          * We must bump the command counter to make the split partition tuple
   23272             :          * visible for renaming.
   23273             :          */
   23274          44 :         CommandCounterIncrement();
   23275             :         /* Rename partition. */
   23276          44 :         sprintf(tmpRelName, "split-%u-%X-tmp", RelationGetRelid(rel), MyProcPid);
   23277          44 :         RenameRelationInternal(splitRelOid, tmpRelName, true, false);
   23278             : 
   23279             :         /*
   23280             :          * We must bump the command counter to make the split partition tuple
   23281             :          * visible after renaming.
   23282             :          */
   23283          44 :         CommandCounterIncrement();
   23284             :     }
   23285             : 
   23286             :     /* Create new partitions (like a split partition), without indexes. */
   23287         888 :     foreach_node(SinglePartitionSpec, sps, cmd->partlist)
   23288             :     {
   23289             :         Relation    newPartRel;
   23290             : 
   23291         516 :         newPartRel = createPartitionTable(wqueue, sps->name, rel,
   23292         516 :                                           splitRel->rd_rel->relowner);
   23293         510 :         newPartRels = lappend(newPartRels, newPartRel);
   23294             :     }
   23295             : 
   23296             :     /*
   23297             :      * Switch to the table owner's userid, so that any index functions are run
   23298             :      * as that user.  Also, lockdown security-restricted operations and
   23299             :      * arrange to make GUC variable changes local to this command.
   23300             :      *
   23301             :      * Need to do it after determining the namespace in the
   23302             :      * createPartitionTable() call.
   23303             :      */
   23304         186 :     GetUserIdAndSecContext(&save_userid, &save_sec_context);
   23305         186 :     SetUserIdAndSecContext(splitRel->rd_rel->relowner,
   23306             :                            save_sec_context | SECURITY_RESTRICTED_OPERATION);
   23307         186 :     save_nestlevel = NewGUCNestLevel();
   23308         186 :     RestrictSearchPath();
   23309             : 
   23310             :     /* Copy data from the split partition to the new partitions. */
   23311         186 :     SplitPartitionMoveRows(wqueue, rel, splitRel, cmd->partlist, newPartRels);
   23312             :     /* Keep the lock until commit. */
   23313         186 :     table_close(splitRel, NoLock);
   23314             : 
   23315             :     /* Attach new partitions to the partitioned table. */
   23316         696 :     forboth(listptr, cmd->partlist, listptr2, newPartRels)
   23317             :     {
   23318         510 :         SinglePartitionSpec *sps = (SinglePartitionSpec *) lfirst(listptr);
   23319         510 :         Relation    newPartRel = (Relation) lfirst(listptr2);
   23320             : 
   23321             :         /*
   23322             :          * wqueue = NULL: verification for each cloned constraint is not
   23323             :          * needed.
   23324             :          */
   23325         510 :         attachPartitionTable(NULL, rel, newPartRel, sps->bound);
   23326             :         /* Keep the lock until commit. */
   23327         510 :         table_close(newPartRel, NoLock);
   23328             :     }
   23329             : 
   23330             :     /* Drop the split partition. */
   23331         186 :     object.classId = RelationRelationId;
   23332         186 :     object.objectId = splitRelOid;
   23333         186 :     object.objectSubId = 0;
   23334             :     /* Probably DROP_CASCADE is not needed. */
   23335         186 :     performDeletion(&object, DROP_RESTRICT, 0);
   23336             : 
   23337             :     /* Roll back any GUC changes executed by index functions. */
   23338         186 :     AtEOXact_GUC(false, save_nestlevel);
   23339             : 
   23340             :     /* Restore the userid and security context. */
   23341         186 :     SetUserIdAndSecContext(save_userid, save_sec_context);
   23342         186 : }

Generated by: LCOV version 1.16