LCOV - code coverage report
Current view: top level - src/backend/commands - tablecmds.c (source / functions) Hit Total Coverage
Test: PostgreSQL Lines: 3917 4339 90.3 %
Date: 2017-09-29 15:12:54 Functions: 136 138 98.6 %
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-2017, 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/genam.h"
      18             : #include "access/heapam.h"
      19             : #include "access/multixact.h"
      20             : #include "access/reloptions.h"
      21             : #include "access/relscan.h"
      22             : #include "access/sysattr.h"
      23             : #include "access/tupconvert.h"
      24             : #include "access/xact.h"
      25             : #include "access/xlog.h"
      26             : #include "catalog/catalog.h"
      27             : #include "catalog/dependency.h"
      28             : #include "catalog/heap.h"
      29             : #include "catalog/index.h"
      30             : #include "catalog/indexing.h"
      31             : #include "catalog/namespace.h"
      32             : #include "catalog/objectaccess.h"
      33             : #include "catalog/partition.h"
      34             : #include "catalog/pg_am.h"
      35             : #include "catalog/pg_collation.h"
      36             : #include "catalog/pg_constraint.h"
      37             : #include "catalog/pg_constraint_fn.h"
      38             : #include "catalog/pg_depend.h"
      39             : #include "catalog/pg_foreign_table.h"
      40             : #include "catalog/pg_inherits.h"
      41             : #include "catalog/pg_inherits_fn.h"
      42             : #include "catalog/pg_namespace.h"
      43             : #include "catalog/pg_opclass.h"
      44             : #include "catalog/pg_tablespace.h"
      45             : #include "catalog/pg_trigger.h"
      46             : #include "catalog/pg_type.h"
      47             : #include "catalog/pg_type_fn.h"
      48             : #include "catalog/storage.h"
      49             : #include "catalog/storage_xlog.h"
      50             : #include "catalog/toasting.h"
      51             : #include "commands/cluster.h"
      52             : #include "commands/comment.h"
      53             : #include "commands/defrem.h"
      54             : #include "commands/event_trigger.h"
      55             : #include "commands/policy.h"
      56             : #include "commands/sequence.h"
      57             : #include "commands/tablecmds.h"
      58             : #include "commands/tablespace.h"
      59             : #include "commands/trigger.h"
      60             : #include "commands/typecmds.h"
      61             : #include "commands/user.h"
      62             : #include "executor/executor.h"
      63             : #include "foreign/foreign.h"
      64             : #include "miscadmin.h"
      65             : #include "nodes/makefuncs.h"
      66             : #include "nodes/nodeFuncs.h"
      67             : #include "nodes/parsenodes.h"
      68             : #include "optimizer/clauses.h"
      69             : #include "optimizer/planner.h"
      70             : #include "optimizer/predtest.h"
      71             : #include "optimizer/prep.h"
      72             : #include "optimizer/var.h"
      73             : #include "parser/parse_clause.h"
      74             : #include "parser/parse_coerce.h"
      75             : #include "parser/parse_collate.h"
      76             : #include "parser/parse_expr.h"
      77             : #include "parser/parse_oper.h"
      78             : #include "parser/parse_relation.h"
      79             : #include "parser/parse_type.h"
      80             : #include "parser/parse_utilcmd.h"
      81             : #include "parser/parser.h"
      82             : #include "pgstat.h"
      83             : #include "rewrite/rewriteDefine.h"
      84             : #include "rewrite/rewriteHandler.h"
      85             : #include "rewrite/rewriteManip.h"
      86             : #include "storage/bufmgr.h"
      87             : #include "storage/lmgr.h"
      88             : #include "storage/lock.h"
      89             : #include "storage/predicate.h"
      90             : #include "storage/smgr.h"
      91             : #include "utils/acl.h"
      92             : #include "utils/builtins.h"
      93             : #include "utils/fmgroids.h"
      94             : #include "utils/inval.h"
      95             : #include "utils/lsyscache.h"
      96             : #include "utils/memutils.h"
      97             : #include "utils/relcache.h"
      98             : #include "utils/ruleutils.h"
      99             : #include "utils/snapmgr.h"
     100             : #include "utils/syscache.h"
     101             : #include "utils/tqual.h"
     102             : #include "utils/typcache.h"
     103             : 
     104             : 
     105             : /*
     106             :  * ON COMMIT action list
     107             :  */
     108             : typedef struct OnCommitItem
     109             : {
     110             :     Oid         relid;          /* relid of relation */
     111             :     OnCommitAction oncommit;    /* what to do at end of xact */
     112             : 
     113             :     /*
     114             :      * If this entry was created during the current transaction,
     115             :      * creating_subid is the ID of the creating subxact; if created in a prior
     116             :      * transaction, creating_subid is zero.  If deleted during the current
     117             :      * transaction, deleting_subid is the ID of the deleting subxact; if no
     118             :      * deletion request is pending, deleting_subid is zero.
     119             :      */
     120             :     SubTransactionId creating_subid;
     121             :     SubTransactionId deleting_subid;
     122             : } OnCommitItem;
     123             : 
     124             : static List *on_commits = NIL;
     125             : 
     126             : 
     127             : /*
     128             :  * State information for ALTER TABLE
     129             :  *
     130             :  * The pending-work queue for an ALTER TABLE is a List of AlteredTableInfo
     131             :  * structs, one for each table modified by the operation (the named table
     132             :  * plus any child tables that are affected).  We save lists of subcommands
     133             :  * to apply to this table (possibly modified by parse transformation steps);
     134             :  * these lists will be executed in Phase 2.  If a Phase 3 step is needed,
     135             :  * necessary information is stored in the constraints and newvals lists.
     136             :  *
     137             :  * Phase 2 is divided into multiple passes; subcommands are executed in
     138             :  * a pass determined by subcommand type.
     139             :  */
     140             : 
     141             : #define AT_PASS_UNSET           -1  /* UNSET will cause ERROR */
     142             : #define AT_PASS_DROP            0   /* DROP (all flavors) */
     143             : #define AT_PASS_ALTER_TYPE      1   /* ALTER COLUMN TYPE */
     144             : #define AT_PASS_OLD_INDEX       2   /* re-add existing indexes */
     145             : #define AT_PASS_OLD_CONSTR      3   /* re-add existing constraints */
     146             : #define AT_PASS_COL_ATTRS       4   /* set other column attributes */
     147             : /* We could support a RENAME COLUMN pass here, but not currently used */
     148             : #define AT_PASS_ADD_COL         5   /* ADD COLUMN */
     149             : #define AT_PASS_ADD_INDEX       6   /* ADD indexes */
     150             : #define AT_PASS_ADD_CONSTR      7   /* ADD constraints, defaults */
     151             : #define AT_PASS_MISC            8   /* other stuff */
     152             : #define AT_NUM_PASSES           9
     153             : 
     154             : typedef struct AlteredTableInfo
     155             : {
     156             :     /* Information saved before any work commences: */
     157             :     Oid         relid;          /* Relation to work on */
     158             :     char        relkind;        /* Its relkind */
     159             :     TupleDesc   oldDesc;        /* Pre-modification tuple descriptor */
     160             :     /* Information saved by Phase 1 for Phase 2: */
     161             :     List       *subcmds[AT_NUM_PASSES]; /* Lists of AlterTableCmd */
     162             :     /* Information saved by Phases 1/2 for Phase 3: */
     163             :     List       *constraints;    /* List of NewConstraint */
     164             :     List       *newvals;        /* List of NewColumnValue */
     165             :     bool        new_notnull;    /* T if we added new NOT NULL constraints */
     166             :     int         rewrite;        /* Reason for forced rewrite, if any */
     167             :     Oid         newTableSpace;  /* new tablespace; 0 means no change */
     168             :     bool        chgPersistence; /* T if SET LOGGED/UNLOGGED is used */
     169             :     char        newrelpersistence;  /* if above is true */
     170             :     Expr       *partition_constraint;   /* for attach partition validation */
     171             :     /* Objects to rebuild after completing ALTER TYPE operations */
     172             :     List       *changedConstraintOids;  /* OIDs of constraints to rebuild */
     173             :     List       *changedConstraintDefs;  /* string definitions of same */
     174             :     List       *changedIndexOids;   /* OIDs of indexes to rebuild */
     175             :     List       *changedIndexDefs;   /* string definitions of same */
     176             : } AlteredTableInfo;
     177             : 
     178             : /* Struct describing one new constraint to check in Phase 3 scan */
     179             : /* Note: new NOT NULL constraints are handled elsewhere */
     180             : typedef struct NewConstraint
     181             : {
     182             :     char       *name;           /* Constraint name, or NULL if none */
     183             :     ConstrType  contype;        /* CHECK or FOREIGN */
     184             :     Oid         refrelid;       /* PK rel, if FOREIGN */
     185             :     Oid         refindid;       /* OID of PK's index, if FOREIGN */
     186             :     Oid         conid;          /* OID of pg_constraint entry, if FOREIGN */
     187             :     Node       *qual;           /* Check expr or CONSTR_FOREIGN Constraint */
     188             :     ExprState  *qualstate;      /* Execution state for CHECK expr */
     189             : } NewConstraint;
     190             : 
     191             : /*
     192             :  * Struct describing one new column value that needs to be computed during
     193             :  * Phase 3 copy (this could be either a new column with a non-null default, or
     194             :  * a column that we're changing the type of).  Columns without such an entry
     195             :  * are just copied from the old table during ATRewriteTable.  Note that the
     196             :  * expr is an expression over *old* table values.
     197             :  */
     198             : typedef struct NewColumnValue
     199             : {
     200             :     AttrNumber  attnum;         /* which column */
     201             :     Expr       *expr;           /* expression to compute */
     202             :     ExprState  *exprstate;      /* execution state */
     203             : } NewColumnValue;
     204             : 
     205             : /*
     206             :  * Error-reporting support for RemoveRelations
     207             :  */
     208             : struct dropmsgstrings
     209             : {
     210             :     char        kind;
     211             :     int         nonexistent_code;
     212             :     const char *nonexistent_msg;
     213             :     const char *skipping_msg;
     214             :     const char *nota_msg;
     215             :     const char *drophint_msg;
     216             : };
     217             : 
     218             : static const struct dropmsgstrings dropmsgstringarray[] = {
     219             :     {RELKIND_RELATION,
     220             :         ERRCODE_UNDEFINED_TABLE,
     221             :         gettext_noop("table \"%s\" does not exist"),
     222             :         gettext_noop("table \"%s\" does not exist, skipping"),
     223             :         gettext_noop("\"%s\" is not a table"),
     224             :     gettext_noop("Use DROP TABLE to remove a table.")},
     225             :     {RELKIND_SEQUENCE,
     226             :         ERRCODE_UNDEFINED_TABLE,
     227             :         gettext_noop("sequence \"%s\" does not exist"),
     228             :         gettext_noop("sequence \"%s\" does not exist, skipping"),
     229             :         gettext_noop("\"%s\" is not a sequence"),
     230             :     gettext_noop("Use DROP SEQUENCE to remove a sequence.")},
     231             :     {RELKIND_VIEW,
     232             :         ERRCODE_UNDEFINED_TABLE,
     233             :         gettext_noop("view \"%s\" does not exist"),
     234             :         gettext_noop("view \"%s\" does not exist, skipping"),
     235             :         gettext_noop("\"%s\" is not a view"),
     236             :     gettext_noop("Use DROP VIEW to remove a view.")},
     237             :     {RELKIND_MATVIEW,
     238             :         ERRCODE_UNDEFINED_TABLE,
     239             :         gettext_noop("materialized view \"%s\" does not exist"),
     240             :         gettext_noop("materialized view \"%s\" does not exist, skipping"),
     241             :         gettext_noop("\"%s\" is not a materialized view"),
     242             :     gettext_noop("Use DROP MATERIALIZED VIEW to remove a materialized view.")},
     243             :     {RELKIND_INDEX,
     244             :         ERRCODE_UNDEFINED_OBJECT,
     245             :         gettext_noop("index \"%s\" does not exist"),
     246             :         gettext_noop("index \"%s\" does not exist, skipping"),
     247             :         gettext_noop("\"%s\" is not an index"),
     248             :     gettext_noop("Use DROP INDEX to remove an index.")},
     249             :     {RELKIND_COMPOSITE_TYPE,
     250             :         ERRCODE_UNDEFINED_OBJECT,
     251             :         gettext_noop("type \"%s\" does not exist"),
     252             :         gettext_noop("type \"%s\" does not exist, skipping"),
     253             :         gettext_noop("\"%s\" is not a type"),
     254             :     gettext_noop("Use DROP TYPE to remove a type.")},
     255             :     {RELKIND_FOREIGN_TABLE,
     256             :         ERRCODE_UNDEFINED_OBJECT,
     257             :         gettext_noop("foreign table \"%s\" does not exist"),
     258             :         gettext_noop("foreign table \"%s\" does not exist, skipping"),
     259             :         gettext_noop("\"%s\" is not a foreign table"),
     260             :     gettext_noop("Use DROP FOREIGN TABLE to remove a foreign table.")},
     261             :     {RELKIND_PARTITIONED_TABLE,
     262             :         ERRCODE_UNDEFINED_TABLE,
     263             :         gettext_noop("table \"%s\" does not exist"),
     264             :         gettext_noop("table \"%s\" does not exist, skipping"),
     265             :         gettext_noop("\"%s\" is not a table"),
     266             :     gettext_noop("Use DROP TABLE to remove a table.")},
     267             :     {'\0', 0, NULL, NULL, NULL, NULL}
     268             : };
     269             : 
     270             : struct DropRelationCallbackState
     271             : {
     272             :     char        relkind;
     273             :     Oid         heapOid;
     274             :     Oid         partParentOid;
     275             :     bool        concurrent;
     276             : };
     277             : 
     278             : /* Alter table target-type flags for ATSimplePermissions */
     279             : #define     ATT_TABLE               0x0001
     280             : #define     ATT_VIEW                0x0002
     281             : #define     ATT_MATVIEW             0x0004
     282             : #define     ATT_INDEX               0x0008
     283             : #define     ATT_COMPOSITE_TYPE      0x0010
     284             : #define     ATT_FOREIGN_TABLE       0x0020
     285             : 
     286             : /*
     287             :  * Partition tables are expected to be dropped when the parent partitioned
     288             :  * table gets dropped. Hence for partitioning we use AUTO dependency.
     289             :  * Otherwise, for regular inheritance use NORMAL dependency.
     290             :  */
     291             : #define child_dependency_type(child_is_partition)   \
     292             :     ((child_is_partition) ? DEPENDENCY_AUTO : DEPENDENCY_NORMAL)
     293             : 
     294             : static void truncate_check_rel(Relation rel);
     295             : static List *MergeAttributes(List *schema, List *supers, char relpersistence,
     296             :                 bool is_partition, List **supOids, List **supconstr,
     297             :                 int *supOidCount);
     298             : static bool MergeCheckConstraint(List *constraints, char *name, Node *expr);
     299             : static void MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel);
     300             : static void MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel);
     301             : static void StoreCatalogInheritance(Oid relationId, List *supers,
     302             :                         bool child_is_partition);
     303             : static void StoreCatalogInheritance1(Oid relationId, Oid parentOid,
     304             :                          int16 seqNumber, Relation inhRelation,
     305             :                          bool child_is_partition);
     306             : static int  findAttrByName(const char *attributeName, List *schema);
     307             : static void AlterIndexNamespaces(Relation classRel, Relation rel,
     308             :                      Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved);
     309             : static void AlterSeqNamespaces(Relation classRel, Relation rel,
     310             :                    Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved,
     311             :                    LOCKMODE lockmode);
     312             : static ObjectAddress ATExecAlterConstraint(Relation rel, AlterTableCmd *cmd,
     313             :                       bool recurse, bool recursing, LOCKMODE lockmode);
     314             : static ObjectAddress ATExecValidateConstraint(Relation rel, char *constrName,
     315             :                          bool recurse, bool recursing, LOCKMODE lockmode);
     316             : static int transformColumnNameList(Oid relId, List *colList,
     317             :                         int16 *attnums, Oid *atttypids);
     318             : static int transformFkeyGetPrimaryKey(Relation pkrel, Oid *indexOid,
     319             :                            List **attnamelist,
     320             :                            int16 *attnums, Oid *atttypids,
     321             :                            Oid *opclasses);
     322             : static Oid transformFkeyCheckAttrs(Relation pkrel,
     323             :                         int numattrs, int16 *attnums,
     324             :                         Oid *opclasses);
     325             : static void checkFkeyPermissions(Relation rel, int16 *attnums, int natts);
     326             : static CoercionPathType findFkeyCast(Oid targetTypeId, Oid sourceTypeId,
     327             :              Oid *funcid);
     328             : static void validateCheckConstraint(Relation rel, HeapTuple constrtup);
     329             : static void validateForeignKeyConstraint(char *conname,
     330             :                              Relation rel, Relation pkrel,
     331             :                              Oid pkindOid, Oid constraintOid);
     332             : static void createForeignKeyTriggers(Relation rel, Oid refRelOid,
     333             :                          Constraint *fkconstraint,
     334             :                          Oid constraintOid, Oid indexOid);
     335             : static void ATController(AlterTableStmt *parsetree,
     336             :              Relation rel, List *cmds, bool recurse, LOCKMODE lockmode);
     337             : static void ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
     338             :           bool recurse, bool recursing, LOCKMODE lockmode);
     339             : static void ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode);
     340             : static void ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
     341             :           AlterTableCmd *cmd, LOCKMODE lockmode);
     342             : static void ATRewriteTables(AlterTableStmt *parsetree,
     343             :                 List **wqueue, LOCKMODE lockmode);
     344             : static void ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode);
     345             : static AlteredTableInfo *ATGetQueueEntry(List **wqueue, Relation rel);
     346             : static void ATSimplePermissions(Relation rel, int allowed_targets);
     347             : static void ATWrongRelkindError(Relation rel, int allowed_targets);
     348             : static void ATSimpleRecursion(List **wqueue, Relation rel,
     349             :                   AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode);
     350             : static void ATTypedTableRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd,
     351             :                       LOCKMODE lockmode);
     352             : static List *find_typed_table_dependencies(Oid typeOid, const char *typeName,
     353             :                               DropBehavior behavior);
     354             : static void ATPrepAddColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
     355             :                 bool is_view, AlterTableCmd *cmd, LOCKMODE lockmode);
     356             : static ObjectAddress ATExecAddColumn(List **wqueue, AlteredTableInfo *tab,
     357             :                 Relation rel, ColumnDef *colDef, bool isOid,
     358             :                 bool recurse, bool recursing,
     359             :                 bool if_not_exists, LOCKMODE lockmode);
     360             : static bool check_for_column_name_collision(Relation rel, const char *colname,
     361             :                                 bool if_not_exists);
     362             : static void add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid);
     363             : static void add_column_collation_dependency(Oid relid, int32 attnum, Oid collid);
     364             : static void ATPrepAddOids(List **wqueue, Relation rel, bool recurse,
     365             :               AlterTableCmd *cmd, LOCKMODE lockmode);
     366             : static void ATPrepDropNotNull(Relation rel, bool recurse, bool recursing);
     367             : static ObjectAddress ATExecDropNotNull(Relation rel, const char *colName, LOCKMODE lockmode);
     368             : static void ATPrepSetNotNull(Relation rel, bool recurse, bool recursing);
     369             : static ObjectAddress ATExecSetNotNull(AlteredTableInfo *tab, Relation rel,
     370             :                  const char *colName, LOCKMODE lockmode);
     371             : static ObjectAddress ATExecColumnDefault(Relation rel, const char *colName,
     372             :                     Node *newDefault, LOCKMODE lockmode);
     373             : static ObjectAddress ATExecAddIdentity(Relation rel, const char *colName,
     374             :                   Node *def, LOCKMODE lockmode);
     375             : static ObjectAddress ATExecSetIdentity(Relation rel, const char *colName,
     376             :                   Node *def, LOCKMODE lockmode);
     377             : static ObjectAddress ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode);
     378             : static void ATPrepSetStatistics(Relation rel, const char *colName,
     379             :                     Node *newValue, LOCKMODE lockmode);
     380             : static ObjectAddress ATExecSetStatistics(Relation rel, const char *colName,
     381             :                     Node *newValue, LOCKMODE lockmode);
     382             : static ObjectAddress ATExecSetOptions(Relation rel, const char *colName,
     383             :                  Node *options, bool isReset, LOCKMODE lockmode);
     384             : static ObjectAddress ATExecSetStorage(Relation rel, const char *colName,
     385             :                  Node *newValue, LOCKMODE lockmode);
     386             : static void ATPrepDropColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
     387             :                  AlterTableCmd *cmd, LOCKMODE lockmode);
     388             : static ObjectAddress ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
     389             :                  DropBehavior behavior,
     390             :                  bool recurse, bool recursing,
     391             :                  bool missing_ok, LOCKMODE lockmode);
     392             : static ObjectAddress ATExecAddIndex(AlteredTableInfo *tab, Relation rel,
     393             :                IndexStmt *stmt, bool is_rebuild, LOCKMODE lockmode);
     394             : static ObjectAddress ATExecAddConstraint(List **wqueue,
     395             :                     AlteredTableInfo *tab, Relation rel,
     396             :                     Constraint *newConstraint, bool recurse, bool is_readd,
     397             :                     LOCKMODE lockmode);
     398             : static ObjectAddress ATExecAddIndexConstraint(AlteredTableInfo *tab, Relation rel,
     399             :                          IndexStmt *stmt, LOCKMODE lockmode);
     400             : static ObjectAddress ATAddCheckConstraint(List **wqueue,
     401             :                      AlteredTableInfo *tab, Relation rel,
     402             :                      Constraint *constr,
     403             :                      bool recurse, bool recursing, bool is_readd,
     404             :                      LOCKMODE lockmode);
     405             : static ObjectAddress ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
     406             :                           Constraint *fkconstraint, LOCKMODE lockmode);
     407             : static void ATExecDropConstraint(Relation rel, const char *constrName,
     408             :                      DropBehavior behavior,
     409             :                      bool recurse, bool recursing,
     410             :                      bool missing_ok, LOCKMODE lockmode);
     411             : static void ATPrepAlterColumnType(List **wqueue,
     412             :                       AlteredTableInfo *tab, Relation rel,
     413             :                       bool recurse, bool recursing,
     414             :                       AlterTableCmd *cmd, LOCKMODE lockmode);
     415             : static bool ATColumnChangeRequiresRewrite(Node *expr, AttrNumber varattno);
     416             : static ObjectAddress ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
     417             :                       AlterTableCmd *cmd, LOCKMODE lockmode);
     418             : static ObjectAddress ATExecAlterColumnGenericOptions(Relation rel, const char *colName,
     419             :                                 List *options, LOCKMODE lockmode);
     420             : static void ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab,
     421             :                        LOCKMODE lockmode);
     422             : static void ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId,
     423             :                      char *cmd, List **wqueue, LOCKMODE lockmode,
     424             :                      bool rewrite);
     425             : static void RebuildConstraintComment(AlteredTableInfo *tab, int pass,
     426             :                          Oid objid, Relation rel, char *conname);
     427             : static void TryReuseIndex(Oid oldId, IndexStmt *stmt);
     428             : static void TryReuseForeignKey(Oid oldId, Constraint *con);
     429             : static void change_owner_fix_column_acls(Oid relationOid,
     430             :                              Oid oldOwnerId, Oid newOwnerId);
     431             : static void change_owner_recurse_to_sequences(Oid relationOid,
     432             :                                   Oid newOwnerId, LOCKMODE lockmode);
     433             : static ObjectAddress ATExecClusterOn(Relation rel, const char *indexName,
     434             :                 LOCKMODE lockmode);
     435             : static void ATExecDropCluster(Relation rel, LOCKMODE lockmode);
     436             : static bool ATPrepChangePersistence(Relation rel, bool toLogged);
     437             : static void ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel,
     438             :                     char *tablespacename, LOCKMODE lockmode);
     439             : static void ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode);
     440             : static void ATExecSetRelOptions(Relation rel, List *defList,
     441             :                     AlterTableType operation,
     442             :                     LOCKMODE lockmode);
     443             : static void ATExecEnableDisableTrigger(Relation rel, char *trigname,
     444             :                            char fires_when, bool skip_system, LOCKMODE lockmode);
     445             : static void ATExecEnableDisableRule(Relation rel, char *rulename,
     446             :                         char fires_when, LOCKMODE lockmode);
     447             : static void ATPrepAddInherit(Relation child_rel);
     448             : static ObjectAddress ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode);
     449             : static ObjectAddress ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode);
     450             : static void drop_parent_dependency(Oid relid, Oid refclassid, Oid refobjid,
     451             :                        DependencyType deptype);
     452             : static ObjectAddress ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode);
     453             : static void ATExecDropOf(Relation rel, LOCKMODE lockmode);
     454             : static void ATExecReplicaIdentity(Relation rel, ReplicaIdentityStmt *stmt, LOCKMODE lockmode);
     455             : static void ATExecGenericOptions(Relation rel, List *options);
     456             : static void ATExecEnableRowSecurity(Relation rel);
     457             : static void ATExecDisableRowSecurity(Relation rel);
     458             : static void ATExecForceNoForceRowSecurity(Relation rel, bool force_rls);
     459             : 
     460             : static void copy_relation_data(SMgrRelation rel, SMgrRelation dst,
     461             :                    ForkNumber forkNum, char relpersistence);
     462             : static const char *storage_name(char c);
     463             : 
     464             : static void RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid,
     465             :                                 Oid oldRelOid, void *arg);
     466             : static void RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid,
     467             :                                  Oid oldrelid, void *arg);
     468             : static bool is_partition_attr(Relation rel, AttrNumber attnum, bool *used_in_expr);
     469             : static PartitionSpec *transformPartitionSpec(Relation rel, PartitionSpec *partspec, char *strategy);
     470             : static void ComputePartitionAttrs(Relation rel, List *partParams, AttrNumber *partattrs,
     471             :                       List **partexprs, Oid *partopclass, Oid *partcollation);
     472             : static void CreateInheritance(Relation child_rel, Relation parent_rel);
     473             : static void RemoveInheritance(Relation child_rel, Relation parent_rel);
     474             : static ObjectAddress ATExecAttachPartition(List **wqueue, Relation rel,
     475             :                       PartitionCmd *cmd);
     476             : static bool PartConstraintImpliedByRelConstraint(Relation scanrel,
     477             :                                      List *partConstraint);
     478             : static void ValidatePartitionConstraints(List **wqueue, Relation scanrel,
     479             :                              List *scanrel_children,
     480             :                              List *partConstraint);
     481             : static ObjectAddress ATExecDetachPartition(Relation rel, RangeVar *name);
     482             : 
     483             : 
     484             : /* ----------------------------------------------------------------
     485             :  *      DefineRelation
     486             :  *              Creates a new relation.
     487             :  *
     488             :  * stmt carries parsetree information from an ordinary CREATE TABLE statement.
     489             :  * The other arguments are used to extend the behavior for other cases:
     490             :  * relkind: relkind to assign to the new relation
     491             :  * ownerId: if not InvalidOid, use this as the new relation's owner.
     492             :  * typaddress: if not null, it's set to the pg_type entry's address.
     493             :  *
     494             :  * Note that permissions checks are done against current user regardless of
     495             :  * ownerId.  A nonzero ownerId is used when someone is creating a relation
     496             :  * "on behalf of" someone else, so we still want to see that the current user
     497             :  * has permissions to do it.
     498             :  *
     499             :  * If successful, returns the address of the new relation.
     500             :  * ----------------------------------------------------------------
     501             :  */
     502             : ObjectAddress
     503        2178 : DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
     504             :                ObjectAddress *typaddress, const char *queryString)
     505             : {
     506             :     char        relname[NAMEDATALEN];
     507             :     Oid         namespaceId;
     508             :     Oid         relationId;
     509             :     Oid         tablespaceId;
     510             :     Relation    rel;
     511             :     TupleDesc   descriptor;
     512             :     List       *inheritOids;
     513             :     List       *old_constraints;
     514             :     bool        localHasOids;
     515             :     int         parentOidCount;
     516             :     List       *rawDefaults;
     517             :     List       *cookedDefaults;
     518             :     Datum       reloptions;
     519             :     ListCell   *listptr;
     520             :     AttrNumber  attnum;
     521             :     static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
     522             :     Oid         ofTypeId;
     523             :     ObjectAddress address;
     524             : 
     525             :     /*
     526             :      * Truncate relname to appropriate length (probably a waste of time, as
     527             :      * parser should have done this already).
     528             :      */
     529        2178 :     StrNCpy(relname, stmt->relation->relname, NAMEDATALEN);
     530             : 
     531             :     /*
     532             :      * Check consistency of arguments
     533             :      */
     534        2178 :     if (stmt->oncommit != ONCOMMIT_NOOP
     535          10 :         && stmt->relation->relpersistence != RELPERSISTENCE_TEMP)
     536           2 :         ereport(ERROR,
     537             :                 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
     538             :                  errmsg("ON COMMIT can only be used on temporary tables")));
     539             : 
     540        2176 :     if (stmt->partspec != NULL)
     541             :     {
     542          93 :         if (relkind != RELKIND_RELATION)
     543           0 :             elog(ERROR, "unexpected relkind: %d", (int) relkind);
     544             : 
     545          93 :         relkind = RELKIND_PARTITIONED_TABLE;
     546             :     }
     547             : 
     548             :     /*
     549             :      * Look up the namespace in which we are supposed to create the relation,
     550             :      * check we have permission to create there, lock it against concurrent
     551             :      * drop, and mark stmt->relation as RELPERSISTENCE_TEMP if a temporary
     552             :      * namespace is selected.
     553             :      */
     554        2176 :     namespaceId =
     555        2176 :         RangeVarGetAndCheckCreationNamespace(stmt->relation, NoLock, NULL);
     556             : 
     557             :     /*
     558             :      * Security check: disallow creating temp tables from security-restricted
     559             :      * code.  This is needed because calling code might not expect untrusted
     560             :      * tables to appear in pg_temp at the front of its search path.
     561             :      */
     562        2176 :     if (stmt->relation->relpersistence == RELPERSISTENCE_TEMP
     563         311 :         && InSecurityRestrictedOperation())
     564           0 :         ereport(ERROR,
     565             :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
     566             :                  errmsg("cannot create temporary table within security-restricted operation")));
     567             : 
     568             :     /*
     569             :      * Select tablespace to use.  If not specified, use default tablespace
     570             :      * (which may in turn default to database's default).
     571             :      */
     572        2176 :     if (stmt->tablespacename)
     573             :     {
     574           7 :         tablespaceId = get_tablespace_oid(stmt->tablespacename, false);
     575             :     }
     576             :     else
     577             :     {
     578        2169 :         tablespaceId = GetDefaultTablespace(stmt->relation->relpersistence);
     579             :         /* note InvalidOid is OK in this case */
     580             :     }
     581             : 
     582             :     /* Check permissions except when using database's default */
     583        2175 :     if (OidIsValid(tablespaceId) && tablespaceId != MyDatabaseTableSpace)
     584             :     {
     585             :         AclResult   aclresult;
     586             : 
     587           6 :         aclresult = pg_tablespace_aclcheck(tablespaceId, GetUserId(),
     588             :                                            ACL_CREATE);
     589           6 :         if (aclresult != ACLCHECK_OK)
     590           1 :             aclcheck_error(aclresult, ACL_KIND_TABLESPACE,
     591           1 :                            get_tablespace_name(tablespaceId));
     592             :     }
     593             : 
     594             :     /* In all cases disallow placing user relations in pg_global */
     595        2174 :     if (tablespaceId == GLOBALTABLESPACE_OID)
     596           0 :         ereport(ERROR,
     597             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     598             :                  errmsg("only shared relations can be placed in pg_global tablespace")));
     599             : 
     600             :     /* Identify user ID that will own the table */
     601        2174 :     if (!OidIsValid(ownerId))
     602        2167 :         ownerId = GetUserId();
     603             : 
     604             :     /*
     605             :      * Parse and validate reloptions, if any.
     606             :      */
     607        2174 :     reloptions = transformRelOptions((Datum) 0, stmt->options, NULL, validnsps,
     608             :                                      true, false);
     609             : 
     610        2173 :     if (relkind == RELKIND_VIEW)
     611         385 :         (void) view_reloptions(reloptions, true);
     612             :     else
     613        1788 :         (void) heap_reloptions(relkind, reloptions, true);
     614             : 
     615        2156 :     if (stmt->ofTypename)
     616             :     {
     617             :         AclResult   aclresult;
     618             : 
     619          14 :         ofTypeId = typenameTypeId(NULL, stmt->ofTypename);
     620             : 
     621          14 :         aclresult = pg_type_aclcheck(ofTypeId, GetUserId(), ACL_USAGE);
     622          14 :         if (aclresult != ACLCHECK_OK)
     623           1 :             aclcheck_error_type(aclresult, ofTypeId);
     624             :     }
     625             :     else
     626        2142 :         ofTypeId = InvalidOid;
     627             : 
     628             :     /*
     629             :      * Look up inheritance ancestors and generate relation schema, including
     630             :      * inherited attributes.  (Note that stmt->tableElts is destructively
     631             :      * modified by MergeAttributes.)
     632             :      */
     633        2142 :     stmt->tableElts =
     634        4310 :         MergeAttributes(stmt->tableElts, stmt->inhRelations,
     635        2155 :                         stmt->relation->relpersistence,
     636        2155 :                         stmt->partbound != NULL,
     637             :                         &inheritOids, &old_constraints, &parentOidCount);
     638             : 
     639             :     /*
     640             :      * Create a tuple descriptor from the relation schema.  Note that this
     641             :      * deals with column names, types, and NOT NULL constraints, but not
     642             :      * default values or CHECK constraints; we handle those below.
     643             :      */
     644        2142 :     descriptor = BuildDescForRelation(stmt->tableElts);
     645             : 
     646             :     /*
     647             :      * Notice that we allow OIDs here only for plain tables and partitioned
     648             :      * tables, even though some other relkinds can support them.  This is
     649             :      * necessary because the default_with_oids GUC must apply only to plain
     650             :      * tables and not any other relkind; doing otherwise would break existing
     651             :      * pg_dump files.  We could allow explicit "WITH OIDS" while not allowing
     652             :      * default_with_oids to affect other relkinds, but it would complicate
     653             :      * interpretOidsOption().
     654             :      */
     655        2138 :     localHasOids = interpretOidsOption(stmt->options,
     656        2138 :                                        (relkind == RELKIND_RELATION ||
     657             :                                         relkind == RELKIND_PARTITIONED_TABLE));
     658        2138 :     descriptor->tdhasoid = (localHasOids || parentOidCount > 0);
     659             : 
     660             :     /*
     661             :      * If a partitioned table doesn't have the system OID column, then none of
     662             :      * its partitions should have it.
     663             :      */
     664        2138 :     if (stmt->partbound && parentOidCount == 0 && localHasOids)
     665           1 :         ereport(ERROR,
     666             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
     667             :                  errmsg("cannot create table with OIDs as partition of table without OIDs")));
     668             : 
     669             :     /*
     670             :      * Find columns with default values and prepare for insertion of the
     671             :      * defaults.  Pre-cooked (that is, inherited) defaults go into a list of
     672             :      * CookedConstraint structs that we'll pass to heap_create_with_catalog,
     673             :      * while raw defaults go into a list of RawColumnDefault structs that will
     674             :      * be processed by AddRelationNewConstraints.  (We can't deal with raw
     675             :      * expressions until we can do transformExpr.)
     676             :      *
     677             :      * We can set the atthasdef flags now in the tuple descriptor; this just
     678             :      * saves StoreAttrDefault from having to do an immediate update of the
     679             :      * pg_attribute rows.
     680             :      */
     681        2137 :     rawDefaults = NIL;
     682        2137 :     cookedDefaults = NIL;
     683        2137 :     attnum = 0;
     684             : 
     685        7943 :     foreach(listptr, stmt->tableElts)
     686             :     {
     687        5806 :         ColumnDef  *colDef = lfirst(listptr);
     688             :         Form_pg_attribute attr;
     689             : 
     690        5806 :         attnum++;
     691        5806 :         attr = TupleDescAttr(descriptor, attnum - 1);
     692             : 
     693        5806 :         if (colDef->raw_default != NULL)
     694             :         {
     695             :             RawColumnDefault *rawEnt;
     696             : 
     697         131 :             Assert(colDef->cooked_default == NULL);
     698             : 
     699         131 :             rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
     700         131 :             rawEnt->attnum = attnum;
     701         131 :             rawEnt->raw_default = colDef->raw_default;
     702         131 :             rawDefaults = lappend(rawDefaults, rawEnt);
     703         131 :             attr->atthasdef = true;
     704             :         }
     705        5675 :         else if (colDef->cooked_default != NULL)
     706             :         {
     707             :             CookedConstraint *cooked;
     708             : 
     709          17 :             cooked = (CookedConstraint *) palloc(sizeof(CookedConstraint));
     710          17 :             cooked->contype = CONSTR_DEFAULT;
     711          17 :             cooked->conoid = InvalidOid; /* until created */
     712          17 :             cooked->name = NULL;
     713          17 :             cooked->attnum = attnum;
     714          17 :             cooked->expr = colDef->cooked_default;
     715          17 :             cooked->skip_validation = false;
     716          17 :             cooked->is_local = true; /* not used for defaults */
     717          17 :             cooked->inhcount = 0;    /* ditto */
     718          17 :             cooked->is_no_inherit = false;
     719          17 :             cookedDefaults = lappend(cookedDefaults, cooked);
     720          17 :             attr->atthasdef = true;
     721             :         }
     722             : 
     723        5806 :         if (colDef->identity)
     724          12 :             attr->attidentity = colDef->identity;
     725             :     }
     726             : 
     727             :     /*
     728             :      * Create the relation.  Inherited defaults and constraints are passed in
     729             :      * for immediate handling --- since they don't need parsing, they can be
     730             :      * stored immediately.
     731             :      */
     732        4274 :     relationId = heap_create_with_catalog(relname,
     733             :                                           namespaceId,
     734             :                                           tablespaceId,
     735             :                                           InvalidOid,
     736             :                                           InvalidOid,
     737             :                                           ofTypeId,
     738             :                                           ownerId,
     739             :                                           descriptor,
     740             :                                           list_concat(cookedDefaults,
     741             :                                                       old_constraints),
     742             :                                           relkind,
     743        2137 :                                           stmt->relation->relpersistence,
     744             :                                           false,
     745             :                                           false,
     746             :                                           localHasOids,
     747             :                                           parentOidCount,
     748             :                                           stmt->oncommit,
     749             :                                           reloptions,
     750             :                                           true,
     751             :                                           allowSystemTableMods,
     752             :                                           false,
     753             :                                           typaddress);
     754             : 
     755             :     /* Store inheritance information for new rel. */
     756        2132 :     StoreCatalogInheritance(relationId, inheritOids, stmt->partbound != NULL);
     757             : 
     758             :     /*
     759             :      * We must bump the command counter to make the newly-created relation
     760             :      * tuple visible for opening.
     761             :      */
     762        2132 :     CommandCounterIncrement();
     763             : 
     764             :     /*
     765             :      * Open the new relation and acquire exclusive lock on it.  This isn't
     766             :      * really necessary for locking out other backends (since they can't see
     767             :      * the new rel anyway until we commit), but it keeps the lock manager from
     768             :      * complaining about deadlock risks.
     769             :      */
     770        2132 :     rel = relation_open(relationId, AccessExclusiveLock);
     771             : 
     772             :     /* Process and store partition bound, if any. */
     773        2132 :     if (stmt->partbound)
     774             :     {
     775             :         PartitionBoundSpec *bound;
     776             :         ParseState *pstate;
     777         136 :         Oid         parentId = linitial_oid(inheritOids);
     778             :         Relation    parent;
     779             : 
     780             :         /* Already have strong enough lock on the parent */
     781         136 :         parent = heap_open(parentId, NoLock);
     782             : 
     783             :         /*
     784             :          * We are going to try to validate the partition bound specification
     785             :          * against the partition key of parentRel, so it better have one.
     786             :          */
     787         136 :         if (parent->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
     788           1 :             ereport(ERROR,
     789             :                     (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
     790             :                      errmsg("\"%s\" is not partitioned",
     791             :                             RelationGetRelationName(parent))));
     792             : 
     793             :         /* Tranform the bound values */
     794         135 :         pstate = make_parsestate(NULL);
     795         135 :         pstate->p_sourcetext = queryString;
     796             : 
     797         135 :         bound = transformPartitionBound(pstate, parent, stmt->partbound);
     798             : 
     799             :         /*
     800             :          * Check first that the new partition's bound is valid and does not
     801             :          * overlap with any of existing partitions of the parent - note that
     802             :          * it does not return on error.
     803             :          */
     804         128 :         check_new_partition_bound(relname, parent, bound);
     805             : 
     806             :         /* Update the pg_class entry. */
     807         116 :         StorePartitionBound(rel, parent, bound);
     808             : 
     809         116 :         heap_close(parent, NoLock);
     810             : 
     811             :         /*
     812             :          * The code that follows may also update the pg_class tuple to update
     813             :          * relnumchecks, so bump up the command counter to avoid the "already
     814             :          * updated by self" error.
     815             :          */
     816         116 :         CommandCounterIncrement();
     817             :     }
     818             : 
     819             :     /*
     820             :      * Process the partitioning specification (if any) and store the partition
     821             :      * key information into the catalog.
     822             :      */
     823        2112 :     if (stmt->partspec)
     824             :     {
     825             :         char        strategy;
     826             :         int         partnatts;
     827             :         AttrNumber  partattrs[PARTITION_MAX_KEYS];
     828             :         Oid         partopclass[PARTITION_MAX_KEYS];
     829             :         Oid         partcollation[PARTITION_MAX_KEYS];
     830          93 :         List       *partexprs = NIL;
     831             : 
     832          93 :         partnatts = list_length(stmt->partspec->partParams);
     833             : 
     834             :         /* Protect fixed-size arrays here and in executor */
     835          93 :         if (partnatts > PARTITION_MAX_KEYS)
     836           0 :             ereport(ERROR,
     837             :                     (errcode(ERRCODE_TOO_MANY_COLUMNS),
     838             :                      errmsg("cannot partition using more than %d columns",
     839             :                             PARTITION_MAX_KEYS)));
     840             : 
     841             :         /*
     842             :          * We need to transform the raw parsetrees corresponding to partition
     843             :          * expressions into executable expression trees.  Like column defaults
     844             :          * and CHECK constraints, we could not have done the transformation
     845             :          * earlier.
     846             :          */
     847          93 :         stmt->partspec = transformPartitionSpec(rel, stmt->partspec,
     848             :                                                 &strategy);
     849             : 
     850          86 :         ComputePartitionAttrs(rel, stmt->partspec->partParams,
     851             :                               partattrs, &partexprs, partopclass,
     852             :                               partcollation);
     853             : 
     854          75 :         StorePartitionKey(rel, strategy, partnatts, partattrs, partexprs,
     855             :                           partopclass, partcollation);
     856             :     }
     857             : 
     858             :     /*
     859             :      * Now add any newly specified column default values and CHECK constraints
     860             :      * to the new relation.  These are passed to us in the form of raw
     861             :      * parsetrees; we need to transform them to executable expression trees
     862             :      * before they can be added. The most convenient way to do that is to
     863             :      * apply the parser's transformExpr routine, but transformExpr doesn't
     864             :      * work unless we have a pre-existing relation. So, the transformation has
     865             :      * to be postponed to this final step of CREATE TABLE.
     866             :      */
     867        2094 :     if (rawDefaults || stmt->constraints)
     868         144 :         AddRelationNewConstraints(rel, rawDefaults, stmt->constraints,
     869             :                                   true, true, false);
     870             : 
     871        2091 :     ObjectAddressSet(address, RelationRelationId, relationId);
     872             : 
     873             :     /*
     874             :      * Clean up.  We keep lock on new relation (although it shouldn't be
     875             :      * visible to anyone else anyway, until commit).
     876             :      */
     877        2091 :     relation_close(rel, NoLock);
     878             : 
     879        2091 :     return address;
     880             : }
     881             : 
     882             : /*
     883             :  * Emit the right error or warning message for a "DROP" command issued on a
     884             :  * non-existent relation
     885             :  */
     886             : static void
     887          50 : DropErrorMsgNonExistent(RangeVar *rel, char rightkind, bool missing_ok)
     888             : {
     889             :     const struct dropmsgstrings *rentry;
     890             : 
     891          69 :     if (rel->schemaname != NULL &&
     892          19 :         !OidIsValid(LookupNamespaceNoError(rel->schemaname)))
     893             :     {
     894           7 :         if (!missing_ok)
     895             :         {
     896           0 :             ereport(ERROR,
     897             :                     (errcode(ERRCODE_UNDEFINED_SCHEMA),
     898             :                      errmsg("schema \"%s\" does not exist", rel->schemaname)));
     899             :         }
     900             :         else
     901             :         {
     902           7 :             ereport(NOTICE,
     903             :                     (errmsg("schema \"%s\" does not exist, skipping",
     904             :                             rel->schemaname)));
     905             :         }
     906          35 :         return;
     907             :     }
     908             : 
     909          92 :     for (rentry = dropmsgstringarray; rentry->kind != '\0'; rentry++)
     910             :     {
     911          92 :         if (rentry->kind == rightkind)
     912             :         {
     913          43 :             if (!missing_ok)
     914             :             {
     915          22 :                 ereport(ERROR,
     916             :                         (errcode(rentry->nonexistent_code),
     917             :                          errmsg(rentry->nonexistent_msg, rel->relname)));
     918             :             }
     919             :             else
     920             :             {
     921          21 :                 ereport(NOTICE, (errmsg(rentry->skipping_msg, rel->relname)));
     922          21 :                 break;
     923             :             }
     924             :         }
     925             :     }
     926             : 
     927          21 :     Assert(rentry->kind != '\0');    /* Should be impossible */
     928             : }
     929             : 
     930             : /*
     931             :  * Emit the right error message for a "DROP" command issued on a
     932             :  * relation of the wrong type
     933             :  */
     934             : static void
     935           0 : DropErrorMsgWrongType(const char *relname, char wrongkind, char rightkind)
     936             : {
     937             :     const struct dropmsgstrings *rentry;
     938             :     const struct dropmsgstrings *wentry;
     939             : 
     940           0 :     for (rentry = dropmsgstringarray; rentry->kind != '\0'; rentry++)
     941           0 :         if (rentry->kind == rightkind)
     942           0 :             break;
     943           0 :     Assert(rentry->kind != '\0');
     944             : 
     945           0 :     for (wentry = dropmsgstringarray; wentry->kind != '\0'; wentry++)
     946           0 :         if (wentry->kind == wrongkind)
     947           0 :             break;
     948             :     /* wrongkind could be something we don't have in our table... */
     949             : 
     950           0 :     ereport(ERROR,
     951             :             (errcode(ERRCODE_WRONG_OBJECT_TYPE),
     952             :              errmsg(rentry->nota_msg, relname),
     953             :              (wentry->kind != '\0') ? errhint("%s", _(wentry->drophint_msg)) : 0));
     954             : }
     955             : 
     956             : /*
     957             :  * RemoveRelations
     958             :  *      Implements DROP TABLE, DROP INDEX, DROP SEQUENCE, DROP VIEW,
     959             :  *      DROP MATERIALIZED VIEW, DROP FOREIGN TABLE
     960             :  */
     961             : void
     962         823 : RemoveRelations(DropStmt *drop)
     963             : {
     964             :     ObjectAddresses *objects;
     965             :     char        relkind;
     966             :     ListCell   *cell;
     967         823 :     int         flags = 0;
     968         823 :     LOCKMODE    lockmode = AccessExclusiveLock;
     969             : 
     970             :     /* DROP CONCURRENTLY uses a weaker lock, and has some restrictions */
     971         823 :     if (drop->concurrent)
     972             :     {
     973           8 :         flags |= PERFORM_DELETION_CONCURRENTLY;
     974           8 :         lockmode = ShareUpdateExclusiveLock;
     975           8 :         Assert(drop->removeType == OBJECT_INDEX);
     976           8 :         if (list_length(drop->objects) != 1)
     977           1 :             ereport(ERROR,
     978             :                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     979             :                      errmsg("DROP INDEX CONCURRENTLY does not support dropping multiple objects")));
     980           7 :         if (drop->behavior == DROP_CASCADE)
     981           0 :             ereport(ERROR,
     982             :                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     983             :                      errmsg("DROP INDEX CONCURRENTLY does not support CASCADE")));
     984             :     }
     985             : 
     986             :     /*
     987             :      * First we identify all the relations, then we delete them in a single
     988             :      * performMultipleDeletions() call.  This is to avoid unwanted DROP
     989             :      * RESTRICT errors if one of the relations depends on another.
     990             :      */
     991             : 
     992             :     /* Determine required relkind */
     993         822 :     switch (drop->removeType)
     994             :     {
     995             :         case OBJECT_TABLE:
     996         639 :             relkind = RELKIND_RELATION;
     997         639 :             break;
     998             : 
     999             :         case OBJECT_INDEX:
    1000          62 :             relkind = RELKIND_INDEX;
    1001          62 :             break;
    1002             : 
    1003             :         case OBJECT_SEQUENCE:
    1004          23 :             relkind = RELKIND_SEQUENCE;
    1005          23 :             break;
    1006             : 
    1007             :         case OBJECT_VIEW:
    1008          83 :             relkind = RELKIND_VIEW;
    1009          83 :             break;
    1010             : 
    1011             :         case OBJECT_MATVIEW:
    1012           5 :             relkind = RELKIND_MATVIEW;
    1013           5 :             break;
    1014             : 
    1015             :         case OBJECT_FOREIGN_TABLE:
    1016          10 :             relkind = RELKIND_FOREIGN_TABLE;
    1017          10 :             break;
    1018             : 
    1019             :         default:
    1020           0 :             elog(ERROR, "unrecognized drop object type: %d",
    1021             :                  (int) drop->removeType);
    1022             :             relkind = 0;        /* keep compiler quiet */
    1023             :             break;
    1024             :     }
    1025             : 
    1026             :     /* Lock and validate each relation; build a list of object addresses */
    1027         822 :     objects = new_object_addresses();
    1028             : 
    1029        1725 :     foreach(cell, drop->objects)
    1030             :     {
    1031         925 :         RangeVar   *rel = makeRangeVarFromNameList((List *) lfirst(cell));
    1032             :         Oid         relOid;
    1033             :         ObjectAddress obj;
    1034             :         struct DropRelationCallbackState state;
    1035             : 
    1036             :         /*
    1037             :          * These next few steps are a great deal like relation_openrv, but we
    1038             :          * don't bother building a relcache entry since we don't need it.
    1039             :          *
    1040             :          * Check for shared-cache-inval messages before trying to access the
    1041             :          * relation.  This is needed to cover the case where the name
    1042             :          * identifies a rel that has been dropped and recreated since the
    1043             :          * start of our transaction: if we don't flush the old syscache entry,
    1044             :          * then we'll latch onto that entry and suffer an error later.
    1045             :          */
    1046         925 :         AcceptInvalidationMessages();
    1047             : 
    1048             :         /* Look up the appropriate relation using namespace search. */
    1049         925 :         state.relkind = relkind;
    1050         925 :         state.heapOid = InvalidOid;
    1051         925 :         state.partParentOid = InvalidOid;
    1052         925 :         state.concurrent = drop->concurrent;
    1053         925 :         relOid = RangeVarGetRelidExtended(rel, lockmode, true,
    1054             :                                           false,
    1055             :                                           RangeVarCallbackForDropRelation,
    1056             :                                           (void *) &state);
    1057             : 
    1058             :         /* Not there? */
    1059         925 :         if (!OidIsValid(relOid))
    1060             :         {
    1061          50 :             DropErrorMsgNonExistent(rel, relkind, drop->missing_ok);
    1062          28 :             continue;
    1063             :         }
    1064             : 
    1065             :         /* OK, we're ready to delete this one */
    1066         875 :         obj.classId = RelationRelationId;
    1067         875 :         obj.objectId = relOid;
    1068         875 :         obj.objectSubId = 0;
    1069             : 
    1070         875 :         add_exact_object_address(&obj, objects);
    1071             :     }
    1072             : 
    1073         800 :     performMultipleDeletions(objects, drop->behavior, flags);
    1074             : 
    1075         791 :     free_object_addresses(objects);
    1076         791 : }
    1077             : 
    1078             : /*
    1079             :  * Before acquiring a table lock, check whether we have sufficient rights.
    1080             :  * In the case of DROP INDEX, also try to lock the table before the index.
    1081             :  * Also, if the table to be dropped is a partition, we try to lock the parent
    1082             :  * first.
    1083             :  */
    1084             : static void
    1085         945 : RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid, Oid oldRelOid,
    1086             :                                 void *arg)
    1087             : {
    1088             :     HeapTuple   tuple;
    1089             :     struct DropRelationCallbackState *state;
    1090             :     char        relkind;
    1091             :     char        expected_relkind;
    1092             :     bool        is_partition;
    1093             :     Form_pg_class classform;
    1094             :     LOCKMODE    heap_lockmode;
    1095             : 
    1096         945 :     state = (struct DropRelationCallbackState *) arg;
    1097         945 :     relkind = state->relkind;
    1098        1890 :     heap_lockmode = state->concurrent ?
    1099         945 :         ShareUpdateExclusiveLock : AccessExclusiveLock;
    1100             : 
    1101             :     /*
    1102             :      * If we previously locked some other index's heap, and the name we're
    1103             :      * looking up no longer refers to that relation, release the now-useless
    1104             :      * lock.
    1105             :      */
    1106         945 :     if (relOid != oldRelOid && OidIsValid(state->heapOid))
    1107             :     {
    1108           0 :         UnlockRelationOid(state->heapOid, heap_lockmode);
    1109           0 :         state->heapOid = InvalidOid;
    1110             :     }
    1111             : 
    1112             :     /*
    1113             :      * Similarly, if we previously locked some other partition's heap, and the
    1114             :      * name we're looking up no longer refers to that relation, release the
    1115             :      * now-useless lock.
    1116             :      */
    1117         945 :     if (relOid != oldRelOid && OidIsValid(state->partParentOid))
    1118             :     {
    1119           0 :         UnlockRelationOid(state->partParentOid, AccessExclusiveLock);
    1120           0 :         state->partParentOid = InvalidOid;
    1121             :     }
    1122             : 
    1123             :     /* Didn't find a relation, so no need for locking or permission checks. */
    1124         945 :     if (!OidIsValid(relOid))
    1125          51 :         return;
    1126             : 
    1127         894 :     tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid));
    1128         894 :     if (!HeapTupleIsValid(tuple))
    1129           0 :         return;                 /* concurrently dropped, so nothing to do */
    1130         894 :     classform = (Form_pg_class) GETSTRUCT(tuple);
    1131         894 :     is_partition = classform->relispartition;
    1132             : 
    1133             :     /*
    1134             :      * Both RELKIND_RELATION and RELKIND_PARTITIONED_TABLE are OBJECT_TABLE,
    1135             :      * but RemoveRelations() can only pass one relkind for a given relation.
    1136             :      * It chooses RELKIND_RELATION for both regular and partitioned tables.
    1137             :      * That means we must be careful before giving the wrong type error when
    1138             :      * the relation is RELKIND_PARTITIONED_TABLE.
    1139             :      */
    1140         894 :     if (classform->relkind == RELKIND_PARTITIONED_TABLE)
    1141          57 :         expected_relkind = RELKIND_RELATION;
    1142             :     else
    1143         837 :         expected_relkind = classform->relkind;
    1144             : 
    1145         894 :     if (relkind != expected_relkind)
    1146           0 :         DropErrorMsgWrongType(rel->relname, classform->relkind, relkind);
    1147             : 
    1148             :     /* Allow DROP to either table owner or schema owner */
    1149         894 :     if (!pg_class_ownercheck(relOid, GetUserId()) &&
    1150           0 :         !pg_namespace_ownercheck(classform->relnamespace, GetUserId()))
    1151           0 :         aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
    1152           0 :                        rel->relname);
    1153             : 
    1154         894 :     if (!allowSystemTableMods && IsSystemClass(relOid, classform))
    1155           0 :         ereport(ERROR,
    1156             :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
    1157             :                  errmsg("permission denied: \"%s\" is a system catalog",
    1158             :                         rel->relname)));
    1159             : 
    1160         894 :     ReleaseSysCache(tuple);
    1161             : 
    1162             :     /*
    1163             :      * In DROP INDEX, attempt to acquire lock on the parent table before
    1164             :      * locking the index.  index_drop() will need this anyway, and since
    1165             :      * regular queries lock tables before their indexes, we risk deadlock if
    1166             :      * we do it the other way around.  No error if we don't find a pg_index
    1167             :      * entry, though --- the relation may have been dropped.
    1168             :      */
    1169         894 :     if (relkind == RELKIND_INDEX && relOid != oldRelOid)
    1170             :     {
    1171          57 :         state->heapOid = IndexGetRelation(relOid, true);
    1172          57 :         if (OidIsValid(state->heapOid))
    1173          57 :             LockRelationOid(state->heapOid, heap_lockmode);
    1174             :     }
    1175             : 
    1176             :     /*
    1177             :      * Similarly, if the relation is a partition, we must acquire lock on its
    1178             :      * parent before locking the partition.  That's because queries lock the
    1179             :      * parent before its partitions, so we risk deadlock it we do it the other
    1180             :      * way around.
    1181             :      */
    1182         894 :     if (is_partition && relOid != oldRelOid)
    1183             :     {
    1184          14 :         state->partParentOid = get_partition_parent(relOid);
    1185          14 :         if (OidIsValid(state->partParentOid))
    1186          14 :             LockRelationOid(state->partParentOid, AccessExclusiveLock);
    1187             :     }
    1188             : }
    1189             : 
    1190             : /*
    1191             :  * ExecuteTruncate
    1192             :  *      Executes a TRUNCATE command.
    1193             :  *
    1194             :  * This is a multi-relation truncate.  We first open and grab exclusive
    1195             :  * lock on all relations involved, checking permissions and otherwise
    1196             :  * verifying that the relation is OK for truncation.  In CASCADE mode,
    1197             :  * relations having FK references to the targeted relations are automatically
    1198             :  * added to the group; in RESTRICT mode, we check that all FK references are
    1199             :  * internal to the group that's being truncated.  Finally all the relations
    1200             :  * are truncated and reindexed.
    1201             :  */
    1202             : void
    1203          69 : ExecuteTruncate(TruncateStmt *stmt)
    1204             : {
    1205          69 :     List       *rels = NIL;
    1206          69 :     List       *relids = NIL;
    1207          69 :     List       *seq_relids = NIL;
    1208             :     EState     *estate;
    1209             :     ResultRelInfo *resultRelInfos;
    1210             :     ResultRelInfo *resultRelInfo;
    1211             :     SubTransactionId mySubid;
    1212             :     ListCell   *cell;
    1213             : 
    1214             :     /*
    1215             :      * Open, exclusive-lock, and check all the explicitly-specified relations
    1216             :      */
    1217         155 :     foreach(cell, stmt->relations)
    1218             :     {
    1219          95 :         RangeVar   *rv = lfirst(cell);
    1220             :         Relation    rel;
    1221          95 :         bool        recurse = rv->inh;
    1222             :         Oid         myrelid;
    1223             : 
    1224          95 :         rel = heap_openrv(rv, AccessExclusiveLock);
    1225          95 :         myrelid = RelationGetRelid(rel);
    1226             :         /* don't throw error for "TRUNCATE foo, foo" */
    1227          95 :         if (list_member_oid(relids, myrelid))
    1228             :         {
    1229           0 :             heap_close(rel, AccessExclusiveLock);
    1230           0 :             continue;
    1231             :         }
    1232          95 :         truncate_check_rel(rel);
    1233          90 :         rels = lappend(rels, rel);
    1234          90 :         relids = lappend_oid(relids, myrelid);
    1235             : 
    1236          90 :         if (recurse)
    1237             :         {
    1238             :             ListCell   *child;
    1239             :             List       *children;
    1240             : 
    1241          84 :             children = find_all_inheritors(myrelid, AccessExclusiveLock, NULL);
    1242             : 
    1243         192 :             foreach(child, children)
    1244             :             {
    1245         110 :                 Oid         childrelid = lfirst_oid(child);
    1246             : 
    1247         110 :                 if (list_member_oid(relids, childrelid))
    1248          84 :                     continue;
    1249             : 
    1250             :                 /* find_all_inheritors already got lock */
    1251          26 :                 rel = heap_open(childrelid, NoLock);
    1252          26 :                 truncate_check_rel(rel);
    1253          24 :                 rels = lappend(rels, rel);
    1254          24 :                 relids = lappend_oid(relids, childrelid);
    1255             :             }
    1256             :         }
    1257           6 :         else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
    1258           2 :             ereport(ERROR,
    1259             :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    1260             :                      errmsg("cannot truncate only a partitioned table"),
    1261             :                      errhint("Do not specify the ONLY keyword, or use truncate only on the partitions directly.")));
    1262             :     }
    1263             : 
    1264             :     /*
    1265             :      * In CASCADE mode, suck in all referencing relations as well.  This
    1266             :      * requires multiple iterations to find indirectly-dependent relations. At
    1267             :      * each phase, we need to exclusive-lock new rels before looking for their
    1268             :      * dependencies, else we might miss something.  Also, we check each rel as
    1269             :      * soon as we open it, to avoid a faux pas such as holding lock for a long
    1270             :      * time on a rel we have no permissions for.
    1271             :      */
    1272          60 :     if (stmt->behavior == DROP_CASCADE)
    1273             :     {
    1274             :         for (;;)
    1275             :         {
    1276             :             List       *newrelids;
    1277             : 
    1278           5 :             newrelids = heap_truncate_find_FKs(relids);
    1279           5 :             if (newrelids == NIL)
    1280           2 :                 break;          /* nothing else to add */
    1281             : 
    1282           9 :             foreach(cell, newrelids)
    1283             :             {
    1284           6 :                 Oid         relid = lfirst_oid(cell);
    1285             :                 Relation    rel;
    1286             : 
    1287           6 :                 rel = heap_open(relid, AccessExclusiveLock);
    1288           6 :                 ereport(NOTICE,
    1289             :                         (errmsg("truncate cascades to table \"%s\"",
    1290             :                                 RelationGetRelationName(rel))));
    1291           6 :                 truncate_check_rel(rel);
    1292           6 :                 rels = lappend(rels, rel);
    1293           6 :                 relids = lappend_oid(relids, relid);
    1294             :             }
    1295           3 :         }
    1296             :     }
    1297             : 
    1298             :     /*
    1299             :      * Check foreign key references.  In CASCADE mode, this should be
    1300             :      * unnecessary since we just pulled in all the references; but as a
    1301             :      * cross-check, do it anyway if in an Assert-enabled build.
    1302             :      */
    1303             : #ifdef USE_ASSERT_CHECKING
    1304          60 :     heap_truncate_check_FKs(rels, false);
    1305             : #else
    1306             :     if (stmt->behavior == DROP_RESTRICT)
    1307             :         heap_truncate_check_FKs(rels, false);
    1308             : #endif
    1309             : 
    1310             :     /*
    1311             :      * If we are asked to restart sequences, find all the sequences, lock them
    1312             :      * (we need AccessExclusiveLock for ResetSequence), and check permissions.
    1313             :      * We want to do this early since it's pointless to do all the truncation
    1314             :      * work only to fail on sequence permissions.
    1315             :      */
    1316          49 :     if (stmt->restart_seqs)
    1317             :     {
    1318           6 :         foreach(cell, rels)
    1319             :         {
    1320           3 :             Relation    rel = (Relation) lfirst(cell);
    1321           3 :             List       *seqlist = getOwnedSequences(RelationGetRelid(rel), 0);
    1322             :             ListCell   *seqcell;
    1323             : 
    1324           8 :             foreach(seqcell, seqlist)
    1325             :             {
    1326           5 :                 Oid         seq_relid = lfirst_oid(seqcell);
    1327             :                 Relation    seq_rel;
    1328             : 
    1329           5 :                 seq_rel = relation_open(seq_relid, AccessExclusiveLock);
    1330             : 
    1331             :                 /* This check must match AlterSequence! */
    1332           5 :                 if (!pg_class_ownercheck(seq_relid, GetUserId()))
    1333           0 :                     aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
    1334           0 :                                    RelationGetRelationName(seq_rel));
    1335             : 
    1336           5 :                 seq_relids = lappend_oid(seq_relids, seq_relid);
    1337             : 
    1338           5 :                 relation_close(seq_rel, NoLock);
    1339             :             }
    1340             :         }
    1341             :     }
    1342             : 
    1343             :     /* Prepare to catch AFTER triggers. */
    1344          49 :     AfterTriggerBeginQuery();
    1345             : 
    1346             :     /*
    1347             :      * To fire triggers, we'll need an EState as well as a ResultRelInfo for
    1348             :      * each relation.  We don't need to call ExecOpenIndices, though.
    1349             :      */
    1350          49 :     estate = CreateExecutorState();
    1351          49 :     resultRelInfos = (ResultRelInfo *)
    1352          49 :         palloc(list_length(rels) * sizeof(ResultRelInfo));
    1353          49 :     resultRelInfo = resultRelInfos;
    1354         142 :     foreach(cell, rels)
    1355             :     {
    1356          93 :         Relation    rel = (Relation) lfirst(cell);
    1357             : 
    1358          93 :         InitResultRelInfo(resultRelInfo,
    1359             :                           rel,
    1360             :                           0,    /* dummy rangetable index */
    1361             :                           NULL,
    1362             :                           0);
    1363          93 :         resultRelInfo++;
    1364             :     }
    1365          49 :     estate->es_result_relations = resultRelInfos;
    1366          49 :     estate->es_num_result_relations = list_length(rels);
    1367             : 
    1368             :     /*
    1369             :      * Process all BEFORE STATEMENT TRUNCATE triggers before we begin
    1370             :      * truncating (this is because one of them might throw an error). Also, if
    1371             :      * we were to allow them to prevent statement execution, that would need
    1372             :      * to be handled here.
    1373             :      */
    1374          49 :     resultRelInfo = resultRelInfos;
    1375         142 :     foreach(cell, rels)
    1376             :     {
    1377          93 :         estate->es_result_relation_info = resultRelInfo;
    1378          93 :         ExecBSTruncateTriggers(estate, resultRelInfo);
    1379          93 :         resultRelInfo++;
    1380             :     }
    1381             : 
    1382             :     /*
    1383             :      * OK, truncate each table.
    1384             :      */
    1385          49 :     mySubid = GetCurrentSubTransactionId();
    1386             : 
    1387         142 :     foreach(cell, rels)
    1388             :     {
    1389          93 :         Relation    rel = (Relation) lfirst(cell);
    1390             : 
    1391             :         /* Skip partitioned tables as there is nothing to do */
    1392          93 :         if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
    1393           9 :             continue;
    1394             : 
    1395             :         /*
    1396             :          * Normally, we need a transaction-safe truncation here.  However, if
    1397             :          * the table was either created in the current (sub)transaction or has
    1398             :          * a new relfilenode in the current (sub)transaction, then we can just
    1399             :          * truncate it in-place, because a rollback would cause the whole
    1400             :          * table or the current physical file to be thrown away anyway.
    1401             :          */
    1402         168 :         if (rel->rd_createSubid == mySubid ||
    1403          84 :             rel->rd_newRelfilenodeSubid == mySubid)
    1404             :         {
    1405             :             /* Immediate, non-rollbackable truncation is OK */
    1406           0 :             heap_truncate_one_rel(rel);
    1407             :         }
    1408             :         else
    1409             :         {
    1410             :             Oid         heap_relid;
    1411             :             Oid         toast_relid;
    1412             :             MultiXactId minmulti;
    1413             : 
    1414             :             /*
    1415             :              * This effectively deletes all rows in the table, and may be done
    1416             :              * in a serializable transaction.  In that case we must record a
    1417             :              * rw-conflict in to this transaction from each transaction
    1418             :              * holding a predicate lock on the table.
    1419             :              */
    1420          84 :             CheckTableForSerializableConflictIn(rel);
    1421             : 
    1422          84 :             minmulti = GetOldestMultiXactId();
    1423             : 
    1424             :             /*
    1425             :              * Need the full transaction-safe pushups.
    1426             :              *
    1427             :              * Create a new empty storage file for the relation, and assign it
    1428             :              * as the relfilenode value. The old storage file is scheduled for
    1429             :              * deletion at commit.
    1430             :              */
    1431          84 :             RelationSetNewRelfilenode(rel, rel->rd_rel->relpersistence,
    1432             :                                       RecentXmin, minmulti);
    1433          84 :             if (rel->rd_rel->relpersistence == RELPERSISTENCE_UNLOGGED)
    1434           0 :                 heap_create_init_fork(rel);
    1435             : 
    1436          84 :             heap_relid = RelationGetRelid(rel);
    1437          84 :             toast_relid = rel->rd_rel->reltoastrelid;
    1438             : 
    1439             :             /*
    1440             :              * The same for the toast table, if any.
    1441             :              */
    1442          84 :             if (OidIsValid(toast_relid))
    1443             :             {
    1444          33 :                 rel = relation_open(toast_relid, AccessExclusiveLock);
    1445          33 :                 RelationSetNewRelfilenode(rel, rel->rd_rel->relpersistence,
    1446             :                                           RecentXmin, minmulti);
    1447          33 :                 if (rel->rd_rel->relpersistence == RELPERSISTENCE_UNLOGGED)
    1448           0 :                     heap_create_init_fork(rel);
    1449          33 :                 heap_close(rel, NoLock);
    1450             :             }
    1451             : 
    1452             :             /*
    1453             :              * Reconstruct the indexes to match, and we're done.
    1454             :              */
    1455          84 :             reindex_relation(heap_relid, REINDEX_REL_PROCESS_TOAST, 0);
    1456             :         }
    1457             : 
    1458          84 :         pgstat_count_truncate(rel);
    1459             :     }
    1460             : 
    1461             :     /*
    1462             :      * Restart owned sequences if we were asked to.
    1463             :      */
    1464          54 :     foreach(cell, seq_relids)
    1465             :     {
    1466           5 :         Oid         seq_relid = lfirst_oid(cell);
    1467             : 
    1468           5 :         ResetSequence(seq_relid);
    1469             :     }
    1470             : 
    1471             :     /*
    1472             :      * Process all AFTER STATEMENT TRUNCATE triggers.
    1473             :      */
    1474          49 :     resultRelInfo = resultRelInfos;
    1475         142 :     foreach(cell, rels)
    1476             :     {
    1477          93 :         estate->es_result_relation_info = resultRelInfo;
    1478          93 :         ExecASTruncateTriggers(estate, resultRelInfo);
    1479          93 :         resultRelInfo++;
    1480             :     }
    1481             : 
    1482             :     /* Handle queued AFTER triggers */
    1483          49 :     AfterTriggerEndQuery(estate);
    1484             : 
    1485             :     /* We can clean up the EState now */
    1486          49 :     FreeExecutorState(estate);
    1487             : 
    1488             :     /* And close the rels (can't do this while EState still holds refs) */
    1489         142 :     foreach(cell, rels)
    1490             :     {
    1491          93 :         Relation    rel = (Relation) lfirst(cell);
    1492             : 
    1493          93 :         heap_close(rel, NoLock);
    1494             :     }
    1495          49 : }
    1496             : 
    1497             : /*
    1498             :  * Check that a given rel is safe to truncate.  Subroutine for ExecuteTruncate
    1499             :  */
    1500             : static void
    1501         127 : truncate_check_rel(Relation rel)
    1502             : {
    1503             :     AclResult   aclresult;
    1504             : 
    1505             :     /*
    1506             :      * Only allow truncate on regular tables and partitioned tables (although,
    1507             :      * the latter are only being included here for the following checks; no
    1508             :      * physical truncation will occur in their case.)
    1509             :      */
    1510         143 :     if (rel->rd_rel->relkind != RELKIND_RELATION &&
    1511          16 :         rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
    1512           4 :         ereport(ERROR,
    1513             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    1514             :                  errmsg("\"%s\" is not a table",
    1515             :                         RelationGetRelationName(rel))));
    1516             : 
    1517             :     /* Permissions checks */
    1518         123 :     aclresult = pg_class_aclcheck(RelationGetRelid(rel), GetUserId(),
    1519             :                                   ACL_TRUNCATE);
    1520         123 :     if (aclresult != ACLCHECK_OK)
    1521           3 :         aclcheck_error(aclresult, ACL_KIND_CLASS,
    1522           3 :                        RelationGetRelationName(rel));
    1523             : 
    1524         120 :     if (!allowSystemTableMods && IsSystemRelation(rel))
    1525           0 :         ereport(ERROR,
    1526             :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
    1527             :                  errmsg("permission denied: \"%s\" is a system catalog",
    1528             :                         RelationGetRelationName(rel))));
    1529             : 
    1530             :     /*
    1531             :      * Don't allow truncate on temp tables of other backends ... their local
    1532             :      * buffer manager is not going to cope.
    1533             :      */
    1534         120 :     if (RELATION_IS_OTHER_TEMP(rel))
    1535           0 :         ereport(ERROR,
    1536             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    1537             :                  errmsg("cannot truncate temporary tables of other sessions")));
    1538             : 
    1539             :     /*
    1540             :      * Also check for active uses of the relation in the current transaction,
    1541             :      * including open scans and pending AFTER trigger events.
    1542             :      */
    1543         120 :     CheckTableNotInUse(rel, "TRUNCATE");
    1544         120 : }
    1545             : 
    1546             : /*
    1547             :  * storage_name
    1548             :  *    returns the name corresponding to a typstorage/attstorage enum value
    1549             :  */
    1550             : static const char *
    1551           4 : storage_name(char c)
    1552             : {
    1553           4 :     switch (c)
    1554             :     {
    1555             :         case 'p':
    1556           0 :             return "PLAIN";
    1557             :         case 'm':
    1558           2 :             return "MAIN";
    1559             :         case 'x':
    1560           2 :             return "EXTENDED";
    1561             :         case 'e':
    1562           0 :             return "EXTERNAL";
    1563             :         default:
    1564           0 :             return "???";
    1565             :     }
    1566             : }
    1567             : 
    1568             : /*----------
    1569             :  * MergeAttributes
    1570             :  *      Returns new schema given initial schema and superclasses.
    1571             :  *
    1572             :  * Input arguments:
    1573             :  * 'schema' is the column/attribute definition for the table. (It's a list
    1574             :  *      of ColumnDef's.) It is destructively changed.
    1575             :  * 'supers' is a list of names (as RangeVar nodes) of parent relations.
    1576             :  * 'relpersistence' is a persistence type of the table.
    1577             :  * 'is_partition' tells if the table is a partition
    1578             :  *
    1579             :  * Output arguments:
    1580             :  * 'supOids' receives a list of the OIDs of the parent relations.
    1581             :  * 'supconstr' receives a list of constraints belonging to the parents,
    1582             :  *      updated as necessary to be valid for the child.
    1583             :  * 'supOidCount' is set to the number of parents that have OID columns.
    1584             :  *
    1585             :  * Return value:
    1586             :  * Completed schema list.
    1587             :  *
    1588             :  * Notes:
    1589             :  *    The order in which the attributes are inherited is very important.
    1590             :  *    Intuitively, the inherited attributes should come first. If a table
    1591             :  *    inherits from multiple parents, the order of those attributes are
    1592             :  *    according to the order of the parents specified in CREATE TABLE.
    1593             :  *
    1594             :  *    Here's an example:
    1595             :  *
    1596             :  *      create table person (name text, age int4, location point);
    1597             :  *      create table emp (salary int4, manager text) inherits(person);
    1598             :  *      create table student (gpa float8) inherits (person);
    1599             :  *      create table stud_emp (percent int4) inherits (emp, student);
    1600             :  *
    1601             :  *    The order of the attributes of stud_emp is:
    1602             :  *
    1603             :  *                          person {1:name, 2:age, 3:location}
    1604             :  *                          /    \
    1605             :  *             {6:gpa}  student   emp {4:salary, 5:manager}
    1606             :  *                          \    /
    1607             :  *                         stud_emp {7:percent}
    1608             :  *
    1609             :  *     If the same attribute name appears multiple times, then it appears
    1610             :  *     in the result table in the proper location for its first appearance.
    1611             :  *
    1612             :  *     Constraints (including NOT NULL constraints) for the child table
    1613             :  *     are the union of all relevant constraints, from both the child schema
    1614             :  *     and parent tables.
    1615             :  *
    1616             :  *     The default value for a child column is defined as:
    1617             :  *      (1) If the child schema specifies a default, that value is used.
    1618             :  *      (2) If neither the child nor any parent specifies a default, then
    1619             :  *          the column will not have a default.
    1620             :  *      (3) If conflicting defaults are inherited from different parents
    1621             :  *          (and not overridden by the child), an error is raised.
    1622             :  *      (4) Otherwise the inherited default is used.
    1623             :  *      Rule (3) is new in Postgres 7.1; in earlier releases you got a
    1624             :  *      rather arbitrary choice of which parent default to use.
    1625             :  *----------
    1626             :  */
    1627             : static List *
    1628        2155 : MergeAttributes(List *schema, List *supers, char relpersistence,
    1629             :                 bool is_partition, List **supOids, List **supconstr,
    1630             :                 int *supOidCount)
    1631             : {
    1632             :     ListCell   *entry;
    1633        2155 :     List       *inhSchema = NIL;
    1634        2155 :     List       *parentOids = NIL;
    1635        2155 :     List       *constraints = NIL;
    1636        2155 :     int         parentsWithOids = 0;
    1637        2155 :     bool        have_bogus_defaults = false;
    1638             :     int         child_attno;
    1639             :     static Node bogus_marker = {0}; /* marks conflicting defaults */
    1640        2155 :     List       *saved_schema = NIL;
    1641             : 
    1642             :     /*
    1643             :      * Check for and reject tables with too many columns. We perform this
    1644             :      * check relatively early for two reasons: (a) we don't run the risk of
    1645             :      * overflowing an AttrNumber in subsequent code (b) an O(n^2) algorithm is
    1646             :      * okay if we're processing <= 1600 columns, but could take minutes to
    1647             :      * execute if the user attempts to create a table with hundreds of
    1648             :      * thousands of columns.
    1649             :      *
    1650             :      * Note that we also need to check that we do not exceed this figure after
    1651             :      * including columns from inherited relations.
    1652             :      */
    1653        2155 :     if (list_length(schema) > MaxHeapAttributeNumber)
    1654           0 :         ereport(ERROR,
    1655             :                 (errcode(ERRCODE_TOO_MANY_COLUMNS),
    1656             :                  errmsg("tables can have at most %d columns",
    1657             :                         MaxHeapAttributeNumber)));
    1658             : 
    1659             :     /*
    1660             :      * In case of a partition, there are no new column definitions, only dummy
    1661             :      * ColumnDefs created for column constraints.  We merge them with the
    1662             :      * constraints inherited from the parent.
    1663             :      */
    1664        2155 :     if (is_partition)
    1665             :     {
    1666         139 :         saved_schema = schema;
    1667         139 :         schema = NIL;
    1668             :     }
    1669             : 
    1670             :     /*
    1671             :      * Check for duplicate names in the explicit list of attributes.
    1672             :      *
    1673             :      * Although we might consider merging such entries in the same way that we
    1674             :      * handle name conflicts for inherited attributes, it seems to make more
    1675             :      * sense to assume such conflicts are errors.
    1676             :      */
    1677        7468 :     foreach(entry, schema)
    1678             :     {
    1679        5316 :         ColumnDef  *coldef = lfirst(entry);
    1680        5316 :         ListCell   *rest = lnext(entry);
    1681        5316 :         ListCell   *prev = entry;
    1682             : 
    1683        5316 :         if (coldef->typeName == NULL)
    1684             : 
    1685             :             /*
    1686             :              * Typed table column option that does not belong to a column from
    1687             :              * the type.  This works because the columns from the type come
    1688             :              * first in the list.
    1689             :              */
    1690           1 :             ereport(ERROR,
    1691             :                     (errcode(ERRCODE_UNDEFINED_COLUMN),
    1692             :                      errmsg("column \"%s\" does not exist",
    1693             :                             coldef->colname)));
    1694             : 
    1695       28460 :         while (rest != NULL)
    1696             :         {
    1697       17832 :             ColumnDef  *restdef = lfirst(rest);
    1698       17832 :             ListCell   *next = lnext(rest); /* need to save it in case we
    1699             :                                              * delete it */
    1700             : 
    1701       17832 :             if (strcmp(coldef->colname, restdef->colname) == 0)
    1702             :             {
    1703           7 :                 if (coldef->is_from_type)
    1704             :                 {
    1705             :                     /*
    1706             :                      * merge the column options into the column from the type
    1707             :                      */
    1708           5 :                     coldef->is_not_null = restdef->is_not_null;
    1709           5 :                     coldef->raw_default = restdef->raw_default;
    1710           5 :                     coldef->cooked_default = restdef->cooked_default;
    1711           5 :                     coldef->constraints = restdef->constraints;
    1712           5 :                     coldef->is_from_type = false;
    1713           5 :                     list_delete_cell(schema, rest, prev);
    1714             :                 }
    1715             :                 else
    1716           2 :                     ereport(ERROR,
    1717             :                             (errcode(ERRCODE_DUPLICATE_COLUMN),
    1718             :                              errmsg("column \"%s\" specified more than once",
    1719             :                                     coldef->colname)));
    1720             :             }
    1721       17830 :             prev = rest;
    1722       17830 :             rest = next;
    1723             :         }
    1724             :     }
    1725             : 
    1726             :     /*
    1727             :      * Scan the parents left-to-right, and merge their attributes to form a
    1728             :      * list of inherited attributes (inhSchema).  Also check to see if we need
    1729             :      * to inherit an OID column.
    1730             :      */
    1731        2152 :     child_attno = 0;
    1732        2455 :     foreach(entry, supers)
    1733             :     {
    1734         307 :         RangeVar   *parent = (RangeVar *) lfirst(entry);
    1735             :         Relation    relation;
    1736             :         TupleDesc   tupleDesc;
    1737             :         TupleConstr *constr;
    1738             :         AttrNumber *newattno;
    1739             :         AttrNumber  parent_attno;
    1740             : 
    1741             :         /*
    1742             :          * A self-exclusive lock is needed here.  If two backends attempt to
    1743             :          * add children to the same parent simultaneously, and that parent has
    1744             :          * no pre-existing children, then both will attempt to update the
    1745             :          * parent's relhassubclass field, leading to a "tuple concurrently
    1746             :          * updated" error.  Also, this interlocks against a concurrent ANALYZE
    1747             :          * on the parent table, which might otherwise be attempting to clear
    1748             :          * the parent's relhassubclass field, if its previous children were
    1749             :          * recently dropped.
    1750             :          *
    1751             :          * If the child table is a partition, then we instead grab an
    1752             :          * exclusive lock on the parent because its partition descriptor will
    1753             :          * be changed by addition of the new partition.
    1754             :          */
    1755         307 :         if (!is_partition)
    1756         168 :             relation = heap_openrv(parent, ShareUpdateExclusiveLock);
    1757             :         else
    1758         139 :             relation = heap_openrv(parent, AccessExclusiveLock);
    1759             : 
    1760             :         /*
    1761             :          * We do not allow partitioned tables and partitions to participate in
    1762             :          * regular inheritance.
    1763             :          */
    1764         307 :         if (relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&
    1765             :             !is_partition)
    1766           1 :             ereport(ERROR,
    1767             :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    1768             :                      errmsg("cannot inherit from partitioned table \"%s\"",
    1769             :                             parent->relname)));
    1770         306 :         if (relation->rd_rel->relispartition && !is_partition)
    1771           1 :             ereport(ERROR,
    1772             :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    1773             :                      errmsg("cannot inherit from partition \"%s\"",
    1774             :                             parent->relname)));
    1775             : 
    1776         445 :         if (relation->rd_rel->relkind != RELKIND_RELATION &&
    1777         278 :             relation->rd_rel->relkind != RELKIND_FOREIGN_TABLE &&
    1778         138 :             relation->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
    1779           0 :             ereport(ERROR,
    1780             :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    1781             :                      errmsg("inherited relation \"%s\" is not a table or foreign table",
    1782             :                             parent->relname)));
    1783             :         /* Permanent rels cannot inherit from temporary ones */
    1784         595 :         if (relpersistence != RELPERSISTENCE_TEMP &&
    1785         290 :             relation->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
    1786           1 :             ereport(ERROR,
    1787             :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    1788             :                      errmsg(!is_partition
    1789             :                             ? "cannot inherit from temporary relation \"%s\""
    1790             :                             : "cannot create a permanent relation as partition of temporary relation \"%s\"",
    1791             :                             parent->relname)));
    1792             : 
    1793             :         /* If existing rel is temp, it must belong to this session */
    1794         318 :         if (relation->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
    1795          14 :             !relation->rd_islocaltemp)
    1796           0 :             ereport(ERROR,
    1797             :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    1798             :                      errmsg(!is_partition
    1799             :                             ? "cannot inherit from temporary relation of another session"
    1800             :                             : "cannot create as partition of temporary relation of another session")));
    1801             : 
    1802             :         /*
    1803             :          * We should have an UNDER permission flag for this, but for now,
    1804             :          * demand that creator of a child table own the parent.
    1805             :          */
    1806         304 :         if (!pg_class_ownercheck(RelationGetRelid(relation), GetUserId()))
    1807           0 :             aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
    1808           0 :                            RelationGetRelationName(relation));
    1809             : 
    1810             :         /*
    1811             :          * Reject duplications in the list of parents.
    1812             :          */
    1813         304 :         if (list_member_oid(parentOids, RelationGetRelid(relation)))
    1814           0 :             ereport(ERROR,
    1815             :                     (errcode(ERRCODE_DUPLICATE_TABLE),
    1816             :                      errmsg("relation \"%s\" would be inherited from more than once",
    1817             :                             parent->relname)));
    1818             : 
    1819         304 :         parentOids = lappend_oid(parentOids, RelationGetRelid(relation));
    1820             : 
    1821         304 :         if (relation->rd_rel->relhasoids)
    1822           7 :             parentsWithOids++;
    1823             : 
    1824         304 :         tupleDesc = RelationGetDescr(relation);
    1825         304 :         constr = tupleDesc->constr;
    1826             : 
    1827             :         /*
    1828             :          * newattno[] will contain the child-table attribute numbers for the
    1829             :          * attributes of this parent table.  (They are not the same for
    1830             :          * parents after the first one, nor if we have dropped columns.)
    1831             :          */
    1832         304 :         newattno = (AttrNumber *)
    1833         304 :             palloc0(tupleDesc->natts * sizeof(AttrNumber));
    1834             : 
    1835        1179 :         for (parent_attno = 1; parent_attno <= tupleDesc->natts;
    1836         571 :              parent_attno++)
    1837             :         {
    1838         572 :             Form_pg_attribute attribute = TupleDescAttr(tupleDesc,
    1839             :                                                         parent_attno - 1);
    1840         572 :             char       *attributeName = NameStr(attribute->attname);
    1841             :             int         exist_attno;
    1842             :             ColumnDef  *def;
    1843             : 
    1844             :             /*
    1845             :              * Ignore dropped columns in the parent.
    1846             :              */
    1847         572 :             if (attribute->attisdropped)
    1848           2 :                 continue;       /* leave newattno entry as zero */
    1849             : 
    1850             :             /*
    1851             :              * Does it conflict with some previously inherited column?
    1852             :              */
    1853         570 :             exist_attno = findAttrByName(attributeName, inhSchema);
    1854         570 :             if (exist_attno > 0)
    1855             :             {
    1856             :                 Oid         defTypeId;
    1857             :                 int32       deftypmod;
    1858             :                 Oid         defCollId;
    1859             : 
    1860             :                 /*
    1861             :                  * Yes, try to merge the two column definitions. They must
    1862             :                  * have the same type, typmod, and collation.
    1863             :                  */
    1864          25 :                 ereport(NOTICE,
    1865             :                         (errmsg("merging multiple inherited definitions of column \"%s\"",
    1866             :                                 attributeName)));
    1867          25 :                 def = (ColumnDef *) list_nth(inhSchema, exist_attno - 1);
    1868          25 :                 typenameTypeIdAndMod(NULL, def->typeName, &defTypeId, &deftypmod);
    1869          50 :                 if (defTypeId != attribute->atttypid ||
    1870          25 :                     deftypmod != attribute->atttypmod)
    1871           0 :                     ereport(ERROR,
    1872             :                             (errcode(ERRCODE_DATATYPE_MISMATCH),
    1873             :                              errmsg("inherited column \"%s\" has a type conflict",
    1874             :                                     attributeName),
    1875             :                              errdetail("%s versus %s",
    1876             :                                        format_type_with_typemod(defTypeId,
    1877             :                                                                 deftypmod),
    1878             :                                        format_type_with_typemod(attribute->atttypid,
    1879             :                                                                 attribute->atttypmod))));
    1880          25 :                 defCollId = GetColumnDefCollation(NULL, def, defTypeId);
    1881          25 :                 if (defCollId != attribute->attcollation)
    1882           0 :                     ereport(ERROR,
    1883             :                             (errcode(ERRCODE_COLLATION_MISMATCH),
    1884             :                              errmsg("inherited column \"%s\" has a collation conflict",
    1885             :                                     attributeName),
    1886             :                              errdetail("\"%s\" versus \"%s\"",
    1887             :                                        get_collation_name(defCollId),
    1888             :                                        get_collation_name(attribute->attcollation))));
    1889             : 
    1890             :                 /* Copy storage parameter */
    1891          25 :                 if (def->storage == 0)
    1892           0 :                     def->storage = attribute->attstorage;
    1893          25 :                 else if (def->storage != attribute->attstorage)
    1894           1 :                     ereport(ERROR,
    1895             :                             (errcode(ERRCODE_DATATYPE_MISMATCH),
    1896             :                              errmsg("inherited column \"%s\" has a storage parameter conflict",
    1897             :                                     attributeName),
    1898             :                              errdetail("%s versus %s",
    1899             :                                        storage_name(def->storage),
    1900             :                                        storage_name(attribute->attstorage))));
    1901             : 
    1902          24 :                 def->inhcount++;
    1903             :                 /* Merge of NOT NULL constraints = OR 'em together */
    1904          24 :                 def->is_not_null |= attribute->attnotnull;
    1905             :                 /* Default and other constraints are handled below */
    1906          24 :                 newattno[parent_attno - 1] = exist_attno;
    1907             :             }
    1908             :             else
    1909             :             {
    1910             :                 /*
    1911             :                  * No, create a new inherited column
    1912             :                  */
    1913         545 :                 def = makeNode(ColumnDef);
    1914         545 :                 def->colname = pstrdup(attributeName);
    1915         545 :                 def->typeName = makeTypeNameFromOid(attribute->atttypid,
    1916             :                                                     attribute->atttypmod);
    1917         545 :                 def->inhcount = 1;
    1918         545 :                 def->is_local = false;
    1919         545 :                 def->is_not_null = attribute->attnotnull;
    1920         545 :                 def->is_from_type = false;
    1921         545 :                 def->is_from_parent = true;
    1922         545 :                 def->storage = attribute->attstorage;
    1923         545 :                 def->raw_default = NULL;
    1924         545 :                 def->cooked_default = NULL;
    1925         545 :                 def->collClause = NULL;
    1926         545 :                 def->collOid = attribute->attcollation;
    1927         545 :                 def->constraints = NIL;
    1928         545 :                 def->location = -1;
    1929         545 :                 inhSchema = lappend(inhSchema, def);
    1930         545 :                 newattno[parent_attno - 1] = ++child_attno;
    1931             :             }
    1932             : 
    1933             :             /*
    1934             :              * Copy default if any
    1935             :              */
    1936         569 :             if (attribute->atthasdef)
    1937             :             {
    1938          24 :                 Node       *this_default = NULL;
    1939             :                 AttrDefault *attrdef;
    1940             :                 int         i;
    1941             : 
    1942             :                 /* Find default in constraint structure */
    1943          24 :                 Assert(constr != NULL);
    1944          24 :                 attrdef = constr->defval;
    1945          28 :                 for (i = 0; i < constr->num_defval; i++)
    1946             :                 {
    1947          28 :                     if (attrdef[i].adnum == parent_attno)
    1948             :                     {
    1949          24 :                         this_default = stringToNode(attrdef[i].adbin);
    1950          24 :                         break;
    1951             :                     }
    1952             :                 }
    1953          24 :                 Assert(this_default != NULL);
    1954             : 
    1955             :                 /*
    1956             :                  * If default expr could contain any vars, we'd need to fix
    1957             :                  * 'em, but it can't; so default is ready to apply to child.
    1958             :                  *
    1959             :                  * If we already had a default from some prior parent, check
    1960             :                  * to see if they are the same.  If so, no problem; if not,
    1961             :                  * mark the column as having a bogus default. Below, we will
    1962             :                  * complain if the bogus default isn't overridden by the child
    1963             :                  * schema.
    1964             :                  */
    1965          24 :                 Assert(def->raw_default == NULL);
    1966          24 :                 if (def->cooked_default == NULL)
    1967          21 :                     def->cooked_default = this_default;
    1968           3 :                 else if (!equal(def->cooked_default, this_default))
    1969             :                 {
    1970           2 :                     def->cooked_default = &bogus_marker;
    1971           2 :                     have_bogus_defaults = true;
    1972             :                 }
    1973             :             }
    1974             :         }
    1975             : 
    1976             :         /*
    1977             :          * Now copy the CHECK constraints of this parent, adjusting attnos
    1978             :          * using the completed newattno[] map.  Identically named constraints
    1979             :          * are merged if possible, else we throw error.
    1980             :          */
    1981         303 :         if (constr && constr->num_check > 0)
    1982             :         {
    1983          34 :             ConstrCheck *check = constr->check;
    1984             :             int         i;
    1985             : 
    1986          72 :             for (i = 0; i < constr->num_check; i++)
    1987             :             {
    1988          38 :                 char       *name = check[i].ccname;
    1989             :                 Node       *expr;
    1990             :                 bool        found_whole_row;
    1991             : 
    1992             :                 /* ignore if the constraint is non-inheritable */
    1993          38 :                 if (check[i].ccnoinherit)
    1994           7 :                     continue;
    1995             : 
    1996             :                 /* Adjust Vars to match new table's column numbering */
    1997          31 :                 expr = map_variable_attnos(stringToNode(check[i].ccbin),
    1998             :                                            1, 0,
    1999             :                                            newattno, tupleDesc->natts,
    2000             :                                            InvalidOid, &found_whole_row);
    2001             : 
    2002             :                 /*
    2003             :                  * For the moment we have to reject whole-row variables. We
    2004             :                  * could convert them, if we knew the new table's rowtype OID,
    2005             :                  * but that hasn't been assigned yet.
    2006             :                  */
    2007          31 :                 if (found_whole_row)
    2008           0 :                     ereport(ERROR,
    2009             :                             (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    2010             :                              errmsg("cannot convert whole-row table reference"),
    2011             :                              errdetail("Constraint \"%s\" contains a whole-row reference to table \"%s\".",
    2012             :                                        name,
    2013             :                                        RelationGetRelationName(relation))));
    2014             : 
    2015             :                 /* check for duplicate */
    2016          31 :                 if (!MergeCheckConstraint(constraints, name, expr))
    2017             :                 {
    2018             :                     /* nope, this is a new one */
    2019             :                     CookedConstraint *cooked;
    2020             : 
    2021          29 :                     cooked = (CookedConstraint *) palloc(sizeof(CookedConstraint));
    2022          29 :                     cooked->contype = CONSTR_CHECK;
    2023          29 :                     cooked->conoid = InvalidOid; /* until created */
    2024          29 :                     cooked->name = pstrdup(name);
    2025          29 :                     cooked->attnum = 0; /* not used for constraints */
    2026          29 :                     cooked->expr = expr;
    2027          29 :                     cooked->skip_validation = false;
    2028          29 :                     cooked->is_local = false;
    2029          29 :                     cooked->inhcount = 1;
    2030          29 :                     cooked->is_no_inherit = false;
    2031          29 :                     constraints = lappend(constraints, cooked);
    2032             :                 }
    2033             :             }
    2034             :         }
    2035             : 
    2036         303 :         pfree(newattno);
    2037             : 
    2038             :         /*
    2039             :          * Close the parent rel, but keep our lock on it until xact commit.
    2040             :          * That will prevent someone else from deleting or ALTERing the parent
    2041             :          * before the child is committed.
    2042             :          */
    2043         303 :         heap_close(relation, NoLock);
    2044             :     }
    2045             : 
    2046             :     /*
    2047             :      * If we had no inherited attributes, the result schema is just the
    2048             :      * explicitly declared columns.  Otherwise, we need to merge the declared
    2049             :      * columns into the inherited schema list.  Although, we never have any
    2050             :      * explicitly declared columns if the table is a partition.
    2051             :      */
    2052        2148 :     if (inhSchema != NIL)
    2053             :     {
    2054         279 :         int         schema_attno = 0;
    2055             : 
    2056         376 :         foreach(entry, schema)
    2057             :         {
    2058         101 :             ColumnDef  *newdef = lfirst(entry);
    2059         101 :             char       *attributeName = newdef->colname;
    2060             :             int         exist_attno;
    2061             : 
    2062         101 :             schema_attno++;
    2063             : 
    2064             :             /*
    2065             :              * Does it conflict with some previously inherited column?
    2066             :              */
    2067         101 :             exist_attno = findAttrByName(attributeName, inhSchema);
    2068         101 :             if (exist_attno > 0)
    2069             :             {
    2070             :                 ColumnDef  *def;
    2071             :                 Oid         defTypeId,
    2072             :                             newTypeId;
    2073             :                 int32       deftypmod,
    2074             :                             newtypmod;
    2075             :                 Oid         defcollid,
    2076             :                             newcollid;
    2077             : 
    2078             :                 /*
    2079             :                  * Partitions have only one parent and have no column
    2080             :                  * definitions of their own, so conflict should never occur.
    2081             :                  */
    2082          24 :                 Assert(!is_partition);
    2083             : 
    2084             :                 /*
    2085             :                  * Yes, try to merge the two column definitions. They must
    2086             :                  * have the same type, typmod, and collation.
    2087             :                  */
    2088          24 :                 if (exist_attno == schema_attno)
    2089          22 :                     ereport(NOTICE,
    2090             :                             (errmsg("merging column \"%s\" with inherited definition",
    2091             :                                     attributeName)));
    2092             :                 else
    2093           2 :                     ereport(NOTICE,
    2094             :                             (errmsg("moving and merging column \"%s\" with inherited definition", attributeName),
    2095             :                              errdetail("User-specified column moved to the position of the inherited column.")));
    2096          24 :                 def = (ColumnDef *) list_nth(inhSchema, exist_attno - 1);
    2097          24 :                 typenameTypeIdAndMod(NULL, def->typeName, &defTypeId, &deftypmod);
    2098          24 :                 typenameTypeIdAndMod(NULL, newdef->typeName, &newTypeId, &newtypmod);
    2099          24 :                 if (defTypeId != newTypeId || deftypmod != newtypmod)
    2100           2 :                     ereport(ERROR,
    2101             :                             (errcode(ERRCODE_DATATYPE_MISMATCH),
    2102             :                              errmsg("column \"%s\" has a type conflict",
    2103             :                                     attributeName),
    2104             :                              errdetail("%s versus %s",
    2105             :                                        format_type_with_typemod(defTypeId,
    2106             :                                                                 deftypmod),
    2107             :                                        format_type_with_typemod(newTypeId,
    2108             :                                                                 newtypmod))));
    2109          22 :                 defcollid = GetColumnDefCollation(NULL, def, defTypeId);
    2110          22 :                 newcollid = GetColumnDefCollation(NULL, newdef, newTypeId);
    2111          22 :                 if (defcollid != newcollid)
    2112           1 :                     ereport(ERROR,
    2113             :                             (errcode(ERRCODE_COLLATION_MISMATCH),
    2114             :                              errmsg("column \"%s\" has a collation conflict",
    2115             :                                     attributeName),
    2116             :                              errdetail("\"%s\" versus \"%s\"",
    2117             :                                        get_collation_name(defcollid),
    2118             :                                        get_collation_name(newcollid))));
    2119             : 
    2120             :                 /*
    2121             :                  * Identity is never inherited.  The new column can have an
    2122             :                  * identity definition, so we always just take that one.
    2123             :                  */
    2124          21 :                 def->identity = newdef->identity;
    2125             : 
    2126             :                 /* Copy storage parameter */
    2127          21 :                 if (def->storage == 0)
    2128           0 :                     def->storage = newdef->storage;
    2129          21 :                 else if (newdef->storage != 0 && def->storage != newdef->storage)
    2130           1 :                     ereport(ERROR,
    2131             :                             (errcode(ERRCODE_DATATYPE_MISMATCH),
    2132             :                              errmsg("column \"%s\" has a storage parameter conflict",
    2133             :                                     attributeName),
    2134             :                              errdetail("%s versus %s",
    2135             :                                        storage_name(def->storage),
    2136             :                                        storage_name(newdef->storage))));
    2137             : 
    2138             :                 /* Mark the column as locally defined */
    2139          20 :                 def->is_local = true;
    2140             :                 /* Merge of NOT NULL constraints = OR 'em together */
    2141          20 :                 def->is_not_null |= newdef->is_not_null;
    2142             :                 /* If new def has a default, override previous default */
    2143          20 :                 if (newdef->raw_default != NULL)
    2144             :                 {
    2145           1 :                     def->raw_default = newdef->raw_default;
    2146           1 :                     def->cooked_default = newdef->cooked_default;
    2147             :                 }
    2148             :             }
    2149             :             else
    2150             :             {
    2151             :                 /*
    2152             :                  * No, attach new column to result schema
    2153             :                  */
    2154          77 :                 inhSchema = lappend(inhSchema, newdef);
    2155             :             }
    2156             :         }
    2157             : 
    2158         275 :         schema = inhSchema;
    2159             : 
    2160             :         /*
    2161             :          * Check that we haven't exceeded the legal # of columns after merging
    2162             :          * in inherited columns.
    2163             :          */
    2164         275 :         if (list_length(schema) > MaxHeapAttributeNumber)
    2165           0 :             ereport(ERROR,
    2166             :                     (errcode(ERRCODE_TOO_MANY_COLUMNS),
    2167             :                      errmsg("tables can have at most %d columns",
    2168             :                             MaxHeapAttributeNumber)));
    2169             :     }
    2170             : 
    2171             :     /*
    2172             :      * Now that we have the column definition list for a partition, we can
    2173             :      * check whether the columns referenced in the column constraint specs
    2174             :      * actually exist.  Also, we merge the constraints into the corresponding
    2175             :      * column definitions.
    2176             :      */
    2177        2144 :     if (is_partition && list_length(saved_schema) > 0)
    2178             :     {
    2179           3 :         schema = list_concat(schema, saved_schema);
    2180             : 
    2181           8 :         foreach(entry, schema)
    2182             :         {
    2183           6 :             ColumnDef  *coldef = lfirst(entry);
    2184           6 :             ListCell   *rest = lnext(entry);
    2185           6 :             ListCell   *prev = entry;
    2186             : 
    2187             :             /*
    2188             :              * Partition column option that does not belong to a column from
    2189             :              * the parent.  This works because the columns from the parent
    2190             :              * come first in the list (see above).
    2191             :              */
    2192           6 :             if (coldef->typeName == NULL)
    2193           0 :                 ereport(ERROR,
    2194             :                         (errcode(ERRCODE_UNDEFINED_COLUMN),
    2195             :                          errmsg("column \"%s\" does not exist",
    2196             :                                 coldef->colname)));
    2197          23 :             while (rest != NULL)
    2198             :             {
    2199          12 :                 ColumnDef  *restdef = lfirst(rest);
    2200          12 :                 ListCell   *next = lnext(rest); /* need to save it in case we
    2201             :                                                  * delete it */
    2202             : 
    2203          12 :                 if (strcmp(coldef->colname, restdef->colname) == 0)
    2204             :                 {
    2205             :                     /*
    2206             :                      * merge the column options into the column from the
    2207             :                      * parent
    2208             :                      */
    2209           4 :                     if (coldef->is_from_parent)
    2210             :                     {
    2211           3 :                         coldef->is_not_null = restdef->is_not_null;
    2212           3 :                         coldef->raw_default = restdef->raw_default;
    2213           3 :                         coldef->cooked_default = restdef->cooked_default;
    2214           3 :                         coldef->constraints = restdef->constraints;
    2215           3 :                         coldef->is_from_parent = false;
    2216           3 :                         list_delete_cell(schema, rest, prev);
    2217             :                     }
    2218             :                     else
    2219           1 :                         ereport(ERROR,
    2220             :                                 (errcode(ERRCODE_DUPLICATE_COLUMN),
    2221             :                                  errmsg("column \"%s\" specified more than once",
    2222             :                                         coldef->colname)));
    2223             :                 }
    2224          11 :                 prev = rest;
    2225          11 :                 rest = next;
    2226             :             }
    2227             :         }
    2228             :     }
    2229             : 
    2230             :     /*
    2231             :      * If we found any conflicting parent default values, check to make sure
    2232             :      * they were overridden by the child.
    2233             :      */
    2234        2143 :     if (have_bogus_defaults)
    2235             :     {
    2236           3 :         foreach(entry, schema)
    2237             :         {
    2238           2 :             ColumnDef  *def = lfirst(entry);
    2239             : 
    2240           2 :             if (def->cooked_default == &bogus_marker)
    2241           1 :                 ereport(ERROR,
    2242             :                         (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
    2243             :                          errmsg("column \"%s\" inherits conflicting default values",
    2244             :                                 def->colname),
    2245             :                          errhint("To resolve the conflict, specify a default explicitly.")));
    2246             :         }
    2247             :     }
    2248             : 
    2249        2142 :     *supOids = parentOids;
    2250        2142 :     *supconstr = constraints;
    2251        2142 :     *supOidCount = parentsWithOids;
    2252        2142 :     return schema;
    2253             : }
    2254             : 
    2255             : 
    2256             : /*
    2257             :  * MergeCheckConstraint
    2258             :  *      Try to merge an inherited CHECK constraint with previous ones
    2259             :  *
    2260             :  * If we inherit identically-named constraints from multiple parents, we must
    2261             :  * merge them, or throw an error if they don't have identical definitions.
    2262             :  *
    2263             :  * constraints is a list of CookedConstraint structs for previous constraints.
    2264             :  *
    2265             :  * Returns TRUE if merged (constraint is a duplicate), or FALSE if it's
    2266             :  * got a so-far-unique name, or throws error if conflict.
    2267             :  */
    2268             : static bool
    2269          31 : MergeCheckConstraint(List *constraints, char *name, Node *expr)
    2270             : {
    2271             :     ListCell   *lc;
    2272             : 
    2273          34 :     foreach(lc, constraints)
    2274             :     {
    2275           5 :         CookedConstraint *ccon = (CookedConstraint *) lfirst(lc);
    2276             : 
    2277           5 :         Assert(ccon->contype == CONSTR_CHECK);
    2278             : 
    2279             :         /* Non-matching names never conflict */
    2280           5 :         if (strcmp(ccon->name, name) != 0)
    2281           3 :             continue;
    2282             : 
    2283           2 :         if (equal(expr, ccon->expr))
    2284             :         {
    2285             :             /* OK to merge */
    2286           2 :             ccon->inhcount++;
    2287           2 :             return true;
    2288             :         }
    2289             : 
    2290           0 :         ereport(ERROR,
    2291             :                 (errcode(ERRCODE_DUPLICATE_OBJECT),
    2292             :                  errmsg("check constraint name \"%s\" appears multiple times but with different expressions",
    2293             :                         name)));
    2294             :     }
    2295             : 
    2296          29 :     return false;
    2297             : }
    2298             : 
    2299             : 
    2300             : /*
    2301             :  * StoreCatalogInheritance
    2302             :  *      Updates the system catalogs with proper inheritance information.
    2303             :  *
    2304             :  * supers is a list of the OIDs of the new relation's direct ancestors.
    2305             :  */
    2306             : static void
    2307        2132 : StoreCatalogInheritance(Oid relationId, List *supers,
    2308             :                         bool child_is_partition)
    2309             : {
    2310             :     Relation    relation;
    2311             :     int16       seqNumber;
    2312             :     ListCell   *entry;
    2313             : 
    2314             :     /*
    2315             :      * sanity checks
    2316             :      */
    2317        2132 :     AssertArg(OidIsValid(relationId));
    2318             : 
    2319        2132 :     if (supers == NIL)
    2320        3991 :         return;
    2321             : 
    2322             :     /*
    2323             :      * Store INHERITS information in pg_inherits using direct ancestors only.
    2324             :      * Also enter dependencies on the direct ancestors, and make sure they are
    2325             :      * marked with relhassubclass = true.
    2326             :      *
    2327             :      * (Once upon a time, both direct and indirect ancestors were found here
    2328             :      * and then entered into pg_ipl.  Since that catalog doesn't exist
    2329             :      * anymore, there's no need to look for indirect ancestors.)
    2330             :      */
    2331         273 :     relation = heap_open(InheritsRelationId, RowExclusiveLock);
    2332             : 
    2333         273 :     seqNumber = 1;
    2334         567 :     foreach(entry, supers)
    2335             :     {
    2336         294 :         Oid         parentOid = lfirst_oid(entry);
    2337             : 
    2338         294 :         StoreCatalogInheritance1(relationId, parentOid, seqNumber, relation,
    2339             :                                  child_is_partition);
    2340         294 :         seqNumber++;
    2341             :     }
    2342             : 
    2343         273 :     heap_close(relation, RowExclusiveLock);
    2344             : }
    2345             : 
    2346             : /*
    2347             :  * Make catalog entries showing relationId as being an inheritance child
    2348             :  * of parentOid.  inhRelation is the already-opened pg_inherits catalog.
    2349             :  */
    2350             : static void
    2351         339 : StoreCatalogInheritance1(Oid relationId, Oid parentOid,
    2352             :                          int16 seqNumber, Relation inhRelation,
    2353             :                          bool child_is_partition)
    2354             : {
    2355         339 :     TupleDesc   desc = RelationGetDescr(inhRelation);
    2356             :     Datum       values[Natts_pg_inherits];
    2357             :     bool        nulls[Natts_pg_inherits];
    2358             :     ObjectAddress childobject,
    2359             :                 parentobject;
    2360             :     HeapTuple   tuple;
    2361             : 
    2362             :     /*
    2363             :      * Make the pg_inherits entry
    2364             :      */
    2365         339 :     values[Anum_pg_inherits_inhrelid - 1] = ObjectIdGetDatum(relationId);
    2366         339 :     values[Anum_pg_inherits_inhparent - 1] = ObjectIdGetDatum(parentOid);
    2367         339 :     values[Anum_pg_inherits_inhseqno - 1] = Int16GetDatum(seqNumber);
    2368             : 
    2369         339 :     memset(nulls, 0, sizeof(nulls));
    2370             : 
    2371         339 :     tuple = heap_form_tuple(desc, values, nulls);
    2372             : 
    2373         339 :     CatalogTupleInsert(inhRelation, tuple);
    2374             : 
    2375         339 :     heap_freetuple(tuple);
    2376             : 
    2377             :     /*
    2378             :      * Store a dependency too
    2379             :      */
    2380         339 :     parentobject.classId = RelationRelationId;
    2381         339 :     parentobject.objectId = parentOid;
    2382         339 :     parentobject.objectSubId = 0;
    2383         339 :     childobject.classId = RelationRelationId;
    2384         339 :     childobject.objectId = relationId;
    2385         339 :     childobject.objectSubId = 0;
    2386             : 
    2387         339 :     recordDependencyOn(&childobject, &parentobject,
    2388             :                        child_dependency_type(child_is_partition));
    2389             : 
    2390             :     /*
    2391             :      * Post creation hook of this inheritance. Since object_access_hook
    2392             :      * doesn't take multiple object identifiers, we relay oid of parent
    2393             :      * relation using auxiliary_id argument.
    2394             :      */
    2395         339 :     InvokeObjectPostAlterHookArg(InheritsRelationId,
    2396             :                                  relationId, 0,
    2397             :                                  parentOid, false);
    2398             : 
    2399             :     /*
    2400             :      * Mark the parent as having subclasses.
    2401             :      */
    2402         339 :     SetRelationHasSubclass(parentOid, true);
    2403         339 : }
    2404             : 
    2405             : /*
    2406             :  * Look for an existing schema entry with the given name.
    2407             :  *
    2408             :  * Returns the index (starting with 1) if attribute already exists in schema,
    2409             :  * 0 if it doesn't.
    2410             :  */
    2411             : static int
    2412         671 : findAttrByName(const char *attributeName, List *schema)
    2413             : {
    2414             :     ListCell   *s;
    2415         671 :     int         i = 1;
    2416             : 
    2417        1246 :     foreach(s, schema)
    2418             :     {
    2419         624 :         ColumnDef  *def = lfirst(s);
    2420             : 
    2421         624 :         if (strcmp(attributeName, def->colname) == 0)
    2422          49 :             return i;
    2423             : 
    2424         575 :         i++;
    2425             :     }
    2426         622 :     return 0;
    2427             : }
    2428             : 
    2429             : 
    2430             : /*
    2431             :  * SetRelationHasSubclass
    2432             :  *      Set the value of the relation's relhassubclass field in pg_class.
    2433             :  *
    2434             :  * NOTE: caller must be holding an appropriate lock on the relation.
    2435             :  * ShareUpdateExclusiveLock is sufficient.
    2436             :  *
    2437             :  * NOTE: an important side-effect of this operation is that an SI invalidation
    2438             :  * message is sent out to all backends --- including me --- causing plans
    2439             :  * referencing the relation to be rebuilt with the new list of children.
    2440             :  * This must happen even if we find that no change is needed in the pg_class
    2441             :  * row.
    2442             :  */
    2443             : void
    2444         339 : SetRelationHasSubclass(Oid relationId, bool relhassubclass)
    2445             : {
    2446             :     Relation    relationRelation;
    2447             :     HeapTuple   tuple;
    2448             :     Form_pg_class classtuple;
    2449             : 
    2450             :     /*
    2451             :      * Fetch a modifiable copy of the tuple, modify it, update pg_class.
    2452             :      */
    2453         339 :     relationRelation = heap_open(RelationRelationId, RowExclusiveLock);
    2454         339 :     tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relationId));
    2455         339 :     if (!HeapTupleIsValid(tuple))
    2456           0 :         elog(ERROR, "cache lookup failed for relation %u", relationId);
    2457         339 :     classtuple = (Form_pg_class) GETSTRUCT(tuple);
    2458             : 
    2459         339 :     if (classtuple->relhassubclass != relhassubclass)
    2460             :     {
    2461         197 :         classtuple->relhassubclass = relhassubclass;
    2462         197 :         CatalogTupleUpdate(relationRelation, &tuple->t_self, tuple);
    2463             :     }
    2464             :     else
    2465             :     {
    2466             :         /* no need to change tuple, but force relcache rebuild anyway */
    2467         142 :         CacheInvalidateRelcacheByTuple(tuple);
    2468             :     }
    2469             : 
    2470         339 :     heap_freetuple(tuple);
    2471         339 :     heap_close(relationRelation, RowExclusiveLock);
    2472         339 : }
    2473             : 
    2474             : /*
    2475             :  *      renameatt_check         - basic sanity checks before attribute rename
    2476             :  */
    2477             : static void
    2478         142 : renameatt_check(Oid myrelid, Form_pg_class classform, bool recursing)
    2479             : {
    2480         142 :     char        relkind = classform->relkind;
    2481             : 
    2482         142 :     if (classform->reloftype && !recursing)
    2483           1 :         ereport(ERROR,
    2484             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    2485             :                  errmsg("cannot rename column of typed table")));
    2486             : 
    2487             :     /*
    2488             :      * Renaming the columns of sequences or toast tables doesn't actually
    2489             :      * break anything from the system's point of view, since internal
    2490             :      * references are by attnum.  But it doesn't seem right to allow users to
    2491             :      * change names that are hardcoded into the system, hence the following
    2492             :      * restriction.
    2493             :      */
    2494         141 :     if (relkind != RELKIND_RELATION &&
    2495          14 :         relkind != RELKIND_VIEW &&
    2496          14 :         relkind != RELKIND_MATVIEW &&
    2497           6 :         relkind != RELKIND_COMPOSITE_TYPE &&
    2498           6 :         relkind != RELKIND_INDEX &&
    2499           0 :         relkind != RELKIND_FOREIGN_TABLE &&
    2500             :         relkind != RELKIND_PARTITIONED_TABLE)
    2501           0 :         ereport(ERROR,
    2502             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    2503             :                  errmsg("\"%s\" is not a table, view, materialized view, composite type, index, or foreign table",
    2504             :                         NameStr(classform->relname))));
    2505             : 
    2506             :     /*
    2507             :      * permissions checking.  only the owner of a class can change its schema.
    2508             :      */
    2509         141 :     if (!pg_class_ownercheck(myrelid, GetUserId()))
    2510           0 :         aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
    2511           0 :                        NameStr(classform->relname));
    2512         141 :     if (!allowSystemTableMods && IsSystemClass(myrelid, classform))
    2513           0 :         ereport(ERROR,
    2514             :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
    2515             :                  errmsg("permission denied: \"%s\" is a system catalog",
    2516             :                         NameStr(classform->relname))));
    2517         141 : }
    2518             : 
    2519             : /*
    2520             :  *      renameatt_internal      - workhorse for renameatt
    2521             :  *
    2522             :  * Return value is the attribute number in the 'myrelid' relation.
    2523             :  */
    2524             : static AttrNumber
    2525          83 : renameatt_internal(Oid myrelid,
    2526             :                    const char *oldattname,
    2527             :                    const char *newattname,
    2528             :                    bool recurse,
    2529             :                    bool recursing,
    2530             :                    int expected_parents,
    2531             :                    DropBehavior behavior)
    2532             : {
    2533             :     Relation    targetrelation;
    2534             :     Relation    attrelation;
    2535             :     HeapTuple   atttup;
    2536             :     Form_pg_attribute attform;
    2537             :     AttrNumber  attnum;
    2538             : 
    2539             :     /*
    2540             :      * Grab an exclusive lock on the target table, which we will NOT release
    2541             :      * until end of transaction.
    2542             :      */
    2543          83 :     targetrelation = relation_open(myrelid, AccessExclusiveLock);
    2544          83 :     renameatt_check(myrelid, RelationGetForm(targetrelation), recursing);
    2545             : 
    2546             :     /*
    2547             :      * if the 'recurse' flag is set then we are supposed to rename this
    2548             :      * attribute in all classes that inherit from 'relname' (as well as in
    2549             :      * 'relname').
    2550             :      *
    2551             :      * any permissions or problems with duplicate attributes will cause the
    2552             :      * whole transaction to abort, which is what we want -- all or nothing.
    2553             :      */
    2554          83 :     if (recurse)
    2555             :     {
    2556             :         List       *child_oids,
    2557             :                    *child_numparents;
    2558             :         ListCell   *lo,
    2559             :                    *li;
    2560             : 
    2561             :         /*
    2562             :          * we need the number of parents for each child so that the recursive
    2563             :          * calls to renameatt() can determine whether there are any parents
    2564             :          * outside the inheritance hierarchy being processed.
    2565             :          */
    2566          33 :         child_oids = find_all_inheritors(myrelid, AccessExclusiveLock,
    2567             :                                          &child_numparents);
    2568             : 
    2569             :         /*
    2570             :          * find_all_inheritors does the recursive search of the inheritance
    2571             :          * hierarchy, so all we have to do is process all of the relids in the
    2572             :          * list that it returns.
    2573             :          */
    2574         105 :         forboth(lo, child_oids, li, child_numparents)
    2575             :         {
    2576          77 :             Oid         childrelid = lfirst_oid(lo);
    2577          77 :             int         numparents = lfirst_int(li);
    2578             : 
    2579          77 :             if (childrelid == myrelid)
    2580          33 :                 continue;
    2581             :             /* note we need not recurse again */
    2582          44 :             renameatt_internal(childrelid, oldattname, newattname, false, true, numparents, behavior);
    2583             :         }
    2584             :     }
    2585             :     else
    2586             :     {
    2587             :         /*
    2588             :          * If we are told not to recurse, there had better not be any child
    2589             :          * tables; else the rename would put them out of step.
    2590             :          *
    2591             :          * expected_parents will only be 0 if we are not already recursing.
    2592             :          */
    2593          56 :         if (expected_parents == 0 &&
    2594           6 :             find_inheritance_children(myrelid, NoLock) != NIL)
    2595           2 :             ereport(ERROR,
    2596             :                     (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    2597             :                      errmsg("inherited column \"%s\" must be renamed in child tables too",
    2598             :                             oldattname)));
    2599             :     }
    2600             : 
    2601             :     /* rename attributes in typed tables of composite type */
    2602          76 :     if (targetrelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
    2603             :     {
    2604             :         List       *child_oids;
    2605             :         ListCell   *lo;
    2606             : 
    2607           4 :         child_oids = find_typed_table_dependencies(targetrelation->rd_rel->reltype,
    2608           4 :                                                    RelationGetRelationName(targetrelation),
    2609             :                                                    behavior);
    2610             : 
    2611           4 :         foreach(lo, child_oids)
    2612           1 :             renameatt_internal(lfirst_oid(lo), oldattname, newattname, true, true, 0, behavior);
    2613             :     }
    2614             : 
    2615          75 :     attrelation = heap_open(AttributeRelationId, RowExclusiveLock);
    2616             : 
    2617          75 :     atttup = SearchSysCacheCopyAttName(myrelid, oldattname);
    2618          75 :     if (!HeapTupleIsValid(atttup))
    2619           4 :         ereport(ERROR,
    2620             :                 (errcode(ERRCODE_UNDEFINED_COLUMN),
    2621             :                  errmsg("column \"%s\" does not exist",
    2622             :                         oldattname)));
    2623          71 :     attform = (Form_pg_attribute) GETSTRUCT(atttup);
    2624             : 
    2625          71 :     attnum = attform->attnum;
    2626          71 :     if (attnum <= 0)
    2627           0 :         ereport(ERROR,
    2628             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    2629             :                  errmsg("cannot rename system column \"%s\"",
    2630             :                         oldattname)));
    2631             : 
    2632             :     /*
    2633             :      * if the attribute is inherited, forbid the renaming.  if this is a
    2634             :      * top-level call to renameatt(), then expected_parents will be 0, so the
    2635             :      * effect of this code will be to prohibit the renaming if the attribute
    2636             :      * is inherited at all.  if this is a recursive call to renameatt(),
    2637             :      * expected_parents will be the number of parents the current relation has
    2638             :      * within the inheritance hierarchy being processed, so we'll prohibit the
    2639             :      * renaming only if there are additional parents from elsewhere.
    2640             :      */
    2641          71 :     if (attform->attinhcount > expected_parents)
    2642           5 :         ereport(ERROR,
    2643             :                 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    2644             :                  errmsg("cannot rename inherited column \"%s\"",
    2645             :                         oldattname)));
    2646             : 
    2647             :     /* new name should not already exist */
    2648          66 :     (void) check_for_column_name_collision(targetrelation, newattname, false);
    2649             : 
    2650             :     /* apply the update */
    2651          64 :     namestrcpy(&(attform->attname), newattname);
    2652             : 
    2653          64 :     CatalogTupleUpdate(attrelation, &atttup->t_self, atttup);
    2654             : 
    2655          64 :     InvokeObjectPostAlterHook(RelationRelationId, myrelid, attnum);
    2656             : 
    2657          64 :     heap_freetuple(atttup);
    2658             : 
    2659          64 :     heap_close(attrelation, RowExclusiveLock);
    2660             : 
    2661          64 :     relation_close(targetrelation, NoLock); /* close rel but keep lock */
    2662             : 
    2663          64 :     return attnum;
    2664             : }
    2665             : 
    2666             : /*
    2667             :  * Perform permissions and integrity checks before acquiring a relation lock.
    2668             :  */
    2669             : static void
    2670          54 : RangeVarCallbackForRenameAttribute(const RangeVar *rv, Oid relid, Oid oldrelid,
    2671             :                                    void *arg)
    2672             : {
    2673             :     HeapTuple   tuple;
    2674             :     Form_pg_class form;
    2675             : 
    2676          54 :     tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
    2677          54 :     if (!HeapTupleIsValid(tuple))
    2678          59 :         return;                 /* concurrently dropped */
    2679          48 :     form = (Form_pg_class) GETSTRUCT(tuple);
    2680          48 :     renameatt_check(relid, form, false);
    2681          47 :     ReleaseSysCache(tuple);
    2682             : }
    2683             : 
    2684             : /*
    2685             :  *      renameatt       - changes the name of an attribute in a relation
    2686             :  *
    2687             :  * The returned ObjectAddress is that of the renamed column.
    2688             :  */
    2689             : ObjectAddress
    2690          44 : renameatt(RenameStmt *stmt)
    2691             : {
    2692             :     Oid         relid;
    2693             :     AttrNumber  attnum;
    2694             :     ObjectAddress address;
    2695             : 
    2696             :     /* lock level taken here should match renameatt_internal */
    2697          44 :     relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
    2698          44 :                                      stmt->missing_ok, false,
    2699             :                                      RangeVarCallbackForRenameAttribute,
    2700             :                                      NULL);
    2701             : 
    2702          42 :     if (!OidIsValid(relid))
    2703             :     {
    2704           4 :         ereport(NOTICE,
    2705             :                 (errmsg("relation \"%s\" does not exist, skipping",
    2706             :                         stmt->relation->relname)));
    2707           4 :         return InvalidObjectAddress;
    2708             :     }
    2709             : 
    2710          38 :     attnum =
    2711          76 :         renameatt_internal(relid,
    2712          38 :                            stmt->subname,    /* old att name */
    2713          38 :                            stmt->newname,    /* new att name */
    2714          38 :                            stmt->relation->inh, /* recursive? */
    2715             :                            false,   /* recursing? */
    2716             :                            0,   /* expected inhcount */
    2717             :                            stmt->behavior);
    2718             : 
    2719          24 :     ObjectAddressSubSet(address, RelationRelationId, relid, attnum);
    2720             : 
    2721          24 :     return address;
    2722             : }
    2723             : 
    2724             : /*
    2725             :  * same logic as renameatt_internal
    2726             :  */
    2727             : static ObjectAddress
    2728          12 : rename_constraint_internal(Oid myrelid,
    2729             :                            Oid mytypid,
    2730             :                            const char *oldconname,
    2731             :                            const char *newconname,
    2732             :                            bool recurse,
    2733             :                            bool recursing,
    2734             :                            int expected_parents)
    2735             : {
    2736          12 :     Relation    targetrelation = NULL;
    2737             :     Oid         constraintOid;
    2738             :     HeapTuple   tuple;
    2739             :     Form_pg_constraint con;
    2740             :     ObjectAddress address;
    2741             : 
    2742          12 :     AssertArg(!myrelid || !mytypid);
    2743             : 
    2744          12 :     if (mytypid)
    2745             :     {
    2746           1 :         constraintOid = get_domain_constraint_oid(mytypid, oldconname, false);
    2747             :     }
    2748             :     else
    2749             :     {
    2750          11 :         targetrelation = relation_open(myrelid, AccessExclusiveLock);
    2751             : 
    2752             :         /*
    2753             :          * don't tell it whether we're recursing; we allow changing typed
    2754             :          * tables here
    2755             :          */
    2756          11 :         renameatt_check(myrelid, RelationGetForm(targetrelation), false);
    2757             : 
    2758          11 :         constraintOid = get_relation_constraint_oid(myrelid, oldconname, false);
    2759             :     }
    2760             : 
    2761          12 :     tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constraintOid));
    2762          12 :     if (!HeapTupleIsValid(tuple))
    2763           0 :         elog(ERROR, "cache lookup failed for constraint %u",
    2764             :              constraintOid);
    2765          12 :     con = (Form_pg_constraint) GETSTRUCT(tuple);
    2766             : 
    2767          12 :     if (myrelid && con->contype == CONSTRAINT_CHECK && !con->connoinherit)
    2768             :     {
    2769           7 :         if (recurse)
    2770             :         {
    2771             :             List       *child_oids,
    2772             :                        *child_numparents;
    2773             :             ListCell   *lo,
    2774             :                        *li;
    2775             : 
    2776           4 :             child_oids = find_all_inheritors(myrelid, AccessExclusiveLock,
    2777             :                                              &child_numparents);
    2778             : 
    2779          10 :             forboth(lo, child_oids, li, child_numparents)
    2780             :             {
    2781           6 :                 Oid         childrelid = lfirst_oid(lo);
    2782           6 :                 int         numparents = lfirst_int(li);
    2783             : 
    2784           6 :                 if (childrelid == myrelid)
    2785           4 :                     continue;
    2786             : 
    2787           2 :                 rename_constraint_internal(childrelid, InvalidOid, oldconname, newconname, false, true, numparents);
    2788             :             }
    2789             :         }
    2790             :         else
    2791             :         {
    2792           4 :             if (expected_parents == 0 &&
    2793           1 :                 find_inheritance_children(myrelid, NoLock) != NIL)
    2794           1 :                 ereport(ERROR,
    2795             :                         (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    2796             :                          errmsg("inherited constraint \"%s\" must be renamed in child tables too",
    2797             :                                 oldconname)));
    2798             :         }
    2799             : 
    2800           6 :         if (con->coninhcount > expected_parents)
    2801           1 :             ereport(ERROR,
    2802             :                     (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    2803             :                      errmsg("cannot rename inherited constraint \"%s\"",
    2804             :                             oldconname)));
    2805             :     }
    2806             : 
    2807          10 :     if (con->conindid
    2808           2 :         && (con->contype == CONSTRAINT_PRIMARY
    2809           1 :             || con->contype == CONSTRAINT_UNIQUE
    2810           0 :             || con->contype == CONSTRAINT_EXCLUSION))
    2811             :         /* rename the index; this renames the constraint as well */
    2812           2 :         RenameRelationInternal(con->conindid, newconname, false);
    2813             :     else
    2814           8 :         RenameConstraintById(constraintOid, newconname);
    2815             : 
    2816          10 :     ObjectAddressSet(address, ConstraintRelationId, constraintOid);
    2817             : 
    2818          10 :     ReleaseSysCache(tuple);
    2819             : 
    2820          10 :     if (targetrelation)
    2821           9 :         relation_close(targetrelation, NoLock); /* close rel but keep lock */
    2822             : 
    2823          10 :     return address;
    2824             : }
    2825             : 
    2826             : ObjectAddress
    2827          11 : RenameConstraint(RenameStmt *stmt)
    2828             : {
    2829          11 :     Oid         relid = InvalidOid;
    2830          11 :     Oid         typid = InvalidOid;
    2831             : 
    2832          11 :     if (stmt->renameType == OBJECT_DOMCONSTRAINT)
    2833             :     {
    2834             :         Relation    rel;
    2835             :         HeapTuple   tup;
    2836             : 
    2837           1 :         typid = typenameTypeId(NULL, makeTypeNameFromNameList(castNode(List, stmt->object)));
    2838           1 :         rel = heap_open(TypeRelationId, RowExclusiveLock);
    2839           1 :         tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
    2840           1 :         if (!HeapTupleIsValid(tup))
    2841           0 :             elog(ERROR, "cache lookup failed for type %u", typid);
    2842           1 :         checkDomainOwner(tup);
    2843           1 :         ReleaseSysCache(tup);
    2844           1 :         heap_close(rel, NoLock);
    2845             :     }
    2846             :     else
    2847             :     {
    2848             :         /* lock level taken here should match rename_constraint_internal */
    2849          10 :         relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
    2850          10 :                                          stmt->missing_ok, false,
    2851             :                                          RangeVarCallbackForRenameAttribute,
    2852             :                                          NULL);
    2853          10 :         if (!OidIsValid(relid))
    2854             :         {
    2855           1 :             ereport(NOTICE,
    2856             :                     (errmsg("relation \"%s\" does not exist, skipping",
    2857             :                             stmt->relation->relname)));
    2858           1 :             return InvalidObjectAddress;
    2859             :         }
    2860             :     }
    2861             : 
    2862          10 :     return
    2863          10 :         rename_constraint_internal(relid, typid,
    2864          10 :                                    stmt->subname,
    2865          10 :                                    stmt->newname,
    2866          19 :                                    (stmt->relation &&
    2867           9 :                                     stmt->relation->inh), /* recursive? */
    2868             :                                    false,   /* recursing? */
    2869             :                                    0 /* expected inhcount */ );
    2870             : 
    2871             : }
    2872             : 
    2873             : /*
    2874             :  * Execute ALTER TABLE/INDEX/SEQUENCE/VIEW/MATERIALIZED VIEW/FOREIGN TABLE
    2875             :  * RENAME
    2876             :  */
    2877             : ObjectAddress
    2878          27 : RenameRelation(RenameStmt *stmt)
    2879             : {
    2880             :     Oid         relid;
    2881             :     ObjectAddress address;
    2882             : 
    2883             :     /*
    2884             :      * Grab an exclusive lock on the target table, index, sequence, view,
    2885             :      * materialized view, or foreign table, which we will NOT release until
    2886             :      * end of transaction.
    2887             :      *
    2888             :      * Lock level used here should match RenameRelationInternal, to avoid lock
    2889             :      * escalation.
    2890             :      */
    2891          27 :     relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
    2892          27 :                                      stmt->missing_ok, false,
    2893             :                                      RangeVarCallbackForAlterRelation,
    2894             :                                      (void *) stmt);
    2895             : 
    2896          25 :     if (!OidIsValid(relid))
    2897             :     {
    2898           3 :         ereport(NOTICE,
    2899             :                 (errmsg("relation \"%s\" does not exist, skipping",
    2900             :                         stmt->relation->relname)));
    2901           3 :         return InvalidObjectAddress;
    2902             :     }
    2903             : 
    2904             :     /* Do the work */
    2905          22 :     RenameRelationInternal(relid, stmt->newname, false);
    2906             : 
    2907          20 :     ObjectAddressSet(address, RelationRelationId, relid);
    2908             : 
    2909          20 :     return address;
    2910             : }
    2911             : 
    2912             : /*
    2913             :  *      RenameRelationInternal - change the name of a relation
    2914             :  *
    2915             :  *      XXX - When renaming sequences, we don't bother to modify the
    2916             :  *            sequence name that is stored within the sequence itself
    2917             :  *            (this would cause problems with MVCC). In the future,
    2918             :  *            the sequence name should probably be removed from the
    2919             :  *            sequence, AFAIK there's no need for it to be there.
    2920             :  */
    2921             : void
    2922         109 : RenameRelationInternal(Oid myrelid, const char *newrelname, bool is_internal)
    2923             : {
    2924             :     Relation    targetrelation;
    2925             :     Relation    relrelation;    /* for RELATION relation */
    2926             :     HeapTuple   reltup;
    2927             :     Form_pg_class relform;
    2928             :     Oid         namespaceId;
    2929             : 
    2930             :     /*
    2931             :      * Grab an exclusive lock on the target table, index, sequence, view,
    2932             :      * materialized view, or foreign table, which we will NOT release until
    2933             :      * end of transaction.
    2934             :      */
    2935         109 :     targetrelation = relation_open(myrelid, AccessExclusiveLock);
    2936         109 :     namespaceId = RelationGetNamespace(targetrelation);
    2937             : 
    2938             :     /*
    2939             :      * Find relation's pg_class tuple, and make sure newrelname isn't in use.
    2940             :      */
    2941         109 :     relrelation = heap_open(RelationRelationId, RowExclusiveLock);
    2942             : 
    2943         109 :     reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(myrelid));
    2944         109 :     if (!HeapTupleIsValid(reltup))  /* shouldn't happen */
    2945           0 :         elog(ERROR, "cache lookup failed for relation %u", myrelid);
    2946         109 :     relform = (Form_pg_class) GETSTRUCT(reltup);
    2947             : 
    2948         109 :     if (get_relname_relid(newrelname, namespaceId) != InvalidOid)
    2949           2 :         ereport(ERROR,
    2950             :                 (errcode(ERRCODE_DUPLICATE_TABLE),
    2951             :                  errmsg("relation \"%s\" already exists",
    2952             :                         newrelname)));
    2953             : 
    2954             :     /*
    2955             :      * Update pg_class tuple with new relname.  (Scribbling on reltup is OK
    2956             :      * because it's a copy...)
    2957             :      */
    2958         107 :     namestrcpy(&(relform->relname), newrelname);
    2959             : 
    2960         107 :     CatalogTupleUpdate(relrelation, &reltup->t_self, reltup);
    2961             : 
    2962         107 :     InvokeObjectPostAlterHookArg(RelationRelationId, myrelid, 0,
    2963             :                                  InvalidOid, is_internal);
    2964             : 
    2965         107 :     heap_freetuple(reltup);
    2966         107 :     heap_close(relrelation, RowExclusiveLock);
    2967             : 
    2968             :     /*
    2969             :      * Also rename the associated type, if any.
    2970             :      */
    2971         107 :     if (OidIsValid(targetrelation->rd_rel->reltype))
    2972          59 :         RenameTypeInternal(targetrelation->rd_rel->reltype,
    2973             :                            newrelname, namespaceId);
    2974             : 
    2975             :     /*
    2976             :      * Also rename the associated constraint, if any.
    2977             :      */
    2978         107 :     if (targetrelation->rd_rel->relkind == RELKIND_INDEX)
    2979             :     {
    2980          48 :         Oid         constraintId = get_index_constraint(myrelid);
    2981             : 
    2982          48 :         if (OidIsValid(constraintId))
    2983           3 :             RenameConstraintById(constraintId, newrelname);
    2984             :     }
    2985             : 
    2986             :     /*
    2987             :      * Close rel, but keep exclusive lock!
    2988             :      */
    2989         107 :     relation_close(targetrelation, NoLock);
    2990         107 : }
    2991             : 
    2992             : /*
    2993             :  * Disallow ALTER TABLE (and similar commands) when the current backend has
    2994             :  * any open reference to the target table besides the one just acquired by
    2995             :  * the calling command; this implies there's an open cursor or active plan.
    2996             :  * We need this check because our lock doesn't protect us against stomping
    2997             :  * on our own foot, only other people's feet!
    2998             :  *
    2999             :  * For ALTER TABLE, the only case known to cause serious trouble is ALTER
    3000             :  * COLUMN TYPE, and some changes are obviously pretty benign, so this could
    3001             :  * possibly be relaxed to only error out for certain types of alterations.
    3002             :  * But the use-case for allowing any of these things is not obvious, so we
    3003             :  * won't work hard at it for now.
    3004             :  *
    3005             :  * We also reject these commands if there are any pending AFTER trigger events
    3006             :  * for the rel.  This is certainly necessary for the rewriting variants of
    3007             :  * ALTER TABLE, because they don't preserve tuple TIDs and so the pending
    3008             :  * events would try to fetch the wrong tuples.  It might be overly cautious
    3009             :  * in other cases, but again it seems better to err on the side of paranoia.
    3010             :  *
    3011             :  * REINDEX calls this with "rel" referencing the index to be rebuilt; here
    3012             :  * we are worried about active indexscans on the index.  The trigger-event
    3013             :  * check can be skipped, since we are doing no damage to the parent table.
    3014             :  *
    3015             :  * The statement name (eg, "ALTER TABLE") is passed for use in error messages.
    3016             :  */
    3017             : void
    3018        5135 : CheckTableNotInUse(Relation rel, const char *stmt)
    3019             : {
    3020             :     int         expected_refcnt;
    3021             : 
    3022        5135 :     expected_refcnt = rel->rd_isnailed ? 2 : 1;
    3023        5135 :     if (rel->rd_refcnt != expected_refcnt)
    3024           2 :         ereport(ERROR,
    3025             :                 (errcode(ERRCODE_OBJECT_IN_USE),
    3026             :         /* translator: first %s is a SQL command, eg ALTER TABLE */
    3027             :                  errmsg("cannot %s \"%s\" because "
    3028             :                         "it is being used by active queries in this session",
    3029             :                         stmt, RelationGetRelationName(rel))));
    3030             : 
    3031        9183 :     if (rel->rd_rel->relkind != RELKIND_INDEX &&
    3032        4050 :         AfterTriggerPendingOnRel(RelationGetRelid(rel)))
    3033           2 :         ereport(ERROR,
    3034             :                 (errcode(ERRCODE_OBJECT_IN_USE),
    3035             :         /* translator: first %s is a SQL command, eg ALTER TABLE */
    3036             :                  errmsg("cannot %s \"%s\" because "
    3037             :                         "it has pending trigger events",
    3038             :                         stmt, RelationGetRelationName(rel))));
    3039        5131 : }
    3040             : 
    3041             : /*
    3042             :  * AlterTableLookupRelation
    3043             :  *      Look up, and lock, the OID for the relation named by an alter table
    3044             :  *      statement.
    3045             :  */
    3046             : Oid
    3047        1025 : AlterTableLookupRelation(AlterTableStmt *stmt, LOCKMODE lockmode)
    3048             : {
    3049        1025 :     return RangeVarGetRelidExtended(stmt->relation, lockmode, stmt->missing_ok, false,
    3050             :                                     RangeVarCallbackForAlterRelation,
    3051             :                                     (void *) stmt);
    3052             : }
    3053             : 
    3054             : /*
    3055             :  * AlterTable
    3056             :  *      Execute ALTER TABLE, which can be a list of subcommands
    3057             :  *
    3058             :  * ALTER TABLE is performed in three phases:
    3059             :  *      1. Examine subcommands and perform pre-transformation checking.
    3060             :  *      2. Update system catalogs.
    3061             :  *      3. Scan table(s) to check new constraints, and optionally recopy
    3062             :  *         the data into new table(s).
    3063             :  * Phase 3 is not performed unless one or more of the subcommands requires
    3064             :  * it.  The intention of this design is to allow multiple independent
    3065             :  * updates of the table schema to be performed with only one pass over the
    3066             :  * data.
    3067             :  *
    3068             :  * ATPrepCmd performs phase 1.  A "work queue" entry is created for
    3069             :  * each table to be affected (there may be multiple affected tables if the
    3070             :  * commands traverse a table inheritance hierarchy).  Also we do preliminary
    3071             :  * validation of the subcommands, including parse transformation of those
    3072             :  * expressions that need to be evaluated with respect to the old table
    3073             :  * schema.
    3074             :  *
    3075             :  * ATRewriteCatalogs performs phase 2 for each affected table.  (Note that
    3076             :  * phases 2 and 3 normally do no explicit recursion, since phase 1 already
    3077             :  * did it --- although some subcommands have to recurse in phase 2 instead.)
    3078             :  * Certain subcommands need to be performed before others to avoid
    3079             :  * unnecessary conflicts; for example, DROP COLUMN should come before
    3080             :  * ADD COLUMN.  Therefore phase 1 divides the subcommands into multiple
    3081             :  * lists, one for each logical "pass" of phase 2.
    3082             :  *
    3083             :  * ATRewriteTables performs phase 3 for those tables that need it.
    3084             :  *
    3085             :  * Thanks to the magic of MVCC, an error anywhere along the way rolls back
    3086             :  * the whole operation; we don't have to do anything special to clean up.
    3087             :  *
    3088             :  * The caller must lock the relation, with an appropriate lock level
    3089             :  * for the subcommands requested, using AlterTableGetLockLevel(stmt->cmds)
    3090             :  * or higher. We pass the lock level down
    3091             :  * so that we can apply it recursively to inherited tables. Note that the
    3092             :  * lock level we want as we recurse might well be higher than required for
    3093             :  * that specific subcommand. So we pass down the overall lock requirement,
    3094             :  * rather than reassess it at lower levels.
    3095             :  */
    3096             : void
    3097         985 : AlterTable(Oid relid, LOCKMODE lockmode, AlterTableStmt *stmt)
    3098             : {
    3099             :     Relation    rel;
    3100             : 
    3101             :     /* Caller is required to provide an adequate lock. */
    3102         985 :     rel = relation_open(relid, NoLock);
    3103             : 
    3104         985 :     CheckTableNotInUse(rel, "ALTER TABLE");
    3105             : 
    3106         983 :     ATController(stmt, rel, stmt->cmds, stmt->relation->inh, lockmode);
    3107         720 : }
    3108             : 
    3109             : /*
    3110             :  * AlterTableInternal
    3111             :  *
    3112             :  * ALTER TABLE with target specified by OID
    3113             :  *
    3114             :  * We do not reject if the relation is already open, because it's quite
    3115             :  * likely that one or more layers of caller have it open.  That means it
    3116             :  * is unsafe to use this entry point for alterations that could break
    3117             :  * existing query plans.  On the assumption it's not used for such, we
    3118             :  * don't have to reject pending AFTER triggers, either.
    3119             :  */
    3120             : void
    3121          43 : AlterTableInternal(Oid relid, List *cmds, bool recurse)
    3122             : {
    3123             :     Relation    rel;
    3124          43 :     LOCKMODE    lockmode = AlterTableGetLockLevel(cmds);
    3125             : 
    3126          43 :     rel = relation_open(relid, lockmode);
    3127             : 
    3128          43 :     EventTriggerAlterTableRelid(relid);
    3129             : 
    3130          43 :     ATController(NULL, rel, cmds, recurse, lockmode);
    3131          42 : }
    3132             : 
    3133             : /*
    3134             :  * AlterTableGetLockLevel
    3135             :  *
    3136             :  * Sets the overall lock level required for the supplied list of subcommands.
    3137             :  * Policy for doing this set according to needs of AlterTable(), see
    3138             :  * comments there for overall explanation.
    3139             :  *
    3140             :  * Function is called before and after parsing, so it must give same
    3141             :  * answer each time it is called. Some subcommands are transformed
    3142             :  * into other subcommand types, so the transform must never be made to a
    3143             :  * lower lock level than previously assigned. All transforms are noted below.
    3144             :  *
    3145             :  * Since this is called before we lock the table we cannot use table metadata
    3146             :  * to influence the type of lock we acquire.
    3147             :  *
    3148             :  * There should be no lockmodes hardcoded into the subcommand functions. All
    3149             :  * lockmode decisions for ALTER TABLE are made here only. The one exception is
    3150             :  * ALTER TABLE RENAME which is treated as a different statement type T_RenameStmt
    3151             :  * and does not travel through this section of code and cannot be combined with
    3152             :  * any of the subcommands given here.
    3153             :  *
    3154             :  * Note that Hot Standby only knows about AccessExclusiveLocks on the master
    3155             :  * so any changes that might affect SELECTs running on standbys need to use
    3156             :  * AccessExclusiveLocks even if you think a lesser lock would do, unless you
    3157             :  * have a solution for that also.
    3158             :  *
    3159             :  * Also note that pg_dump uses only an AccessShareLock, meaning that anything
    3160             :  * that takes a lock less than AccessExclusiveLock can change object definitions
    3161             :  * while pg_dump is running. Be careful to check that the appropriate data is
    3162             :  * derived by pg_dump using an MVCC snapshot, rather than syscache lookups,
    3163             :  * otherwise we might end up with an inconsistent dump that can't restore.
    3164             :  */
    3165             : LOCKMODE
    3166        1068 : AlterTableGetLockLevel(List *cmds)
    3167             : {
    3168             :     /*
    3169             :      * This only works if we read catalog tables using MVCC snapshots.
    3170             :      */
    3171             :     ListCell   *lcmd;
    3172        1068 :     LOCKMODE    lockmode = ShareUpdateExclusiveLock;
    3173             : 
    3174        2162 :     foreach(lcmd, cmds)
    3175             :     {
    3176        1094 :         AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);
    3177        1094 :         LOCKMODE    cmd_lockmode = AccessExclusiveLock; /* default for compiler */
    3178             : 
    3179        1094 :         switch (cmd->subtype)
    3180             :         {
    3181             :                 /*
    3182             :                  * These subcommands rewrite the heap, so require full locks.
    3183             :                  */
    3184             :             case AT_AddColumn:  /* may rewrite heap, in some cases and visible
    3185             :                                  * to SELECT */
    3186             :             case AT_SetTableSpace:  /* must rewrite heap */
    3187             :             case AT_AlterColumnType:    /* must rewrite heap */
    3188             :             case AT_AddOids:    /* must rewrite heap */
    3189         245 :                 cmd_lockmode = AccessExclusiveLock;
    3190         245 :                 break;
    3191             : 
    3192             :                 /*
    3193             :                  * These subcommands may require addition of toast tables. If
    3194             :                  * we add a toast table to a table currently being scanned, we
    3195             :                  * might miss data added to the new toast table by concurrent
    3196             :                  * insert transactions.
    3197             :                  */
    3198             :             case AT_SetStorage: /* may add toast tables, see
    3199             :                                  * ATRewriteCatalogs() */
    3200          14 :                 cmd_lockmode = AccessExclusiveLock;
    3201          14 :                 break;
    3202             : 
    3203             :                 /*
    3204             :                  * Removing constraints can affect SELECTs that have been
    3205             :                  * optimised assuming the constraint holds true.
    3206             :                  */
    3207             :             case AT_DropConstraint: /* as DROP INDEX */
    3208             :             case AT_DropNotNull:    /* may change some SQL plans */
    3209          67 :                 cmd_lockmode = AccessExclusiveLock;
    3210          67 :                 break;
    3211             : 
    3212             :                 /*
    3213             :                  * Subcommands that may be visible to concurrent SELECTs
    3214             :                  */
    3215             :             case AT_DropColumn: /* change visible to SELECT */
    3216             :             case AT_AddColumnToView:    /* CREATE VIEW */
    3217             :             case AT_DropOids:   /* calls AT_DropColumn */
    3218             :             case AT_EnableAlwaysRule:   /* may change SELECT rules */
    3219             :             case AT_EnableReplicaRule:  /* may change SELECT rules */
    3220             :             case AT_EnableRule: /* may change SELECT rules */
    3221             :             case AT_DisableRule:    /* may change SELECT rules */
    3222         120 :                 cmd_lockmode = AccessExclusiveLock;
    3223         120 :                 break;
    3224             : 
    3225             :                 /*
    3226             :                  * Changing owner may remove implicit SELECT privileges
    3227             :                  */
    3228             :             case AT_ChangeOwner:    /* change visible to SELECT */
    3229          18 :                 cmd_lockmode = AccessExclusiveLock;
    3230          18 :                 break;
    3231             : 
    3232             :                 /*
    3233             :                  * Changing foreign table options may affect optimization.
    3234             :                  */
    3235             :             case AT_GenericOptions:
    3236             :             case AT_AlterColumnGenericOptions:
    3237          11 :                 cmd_lockmode = AccessExclusiveLock;
    3238          11 :                 break;
    3239             : 
    3240             :                 /*
    3241             :                  * These subcommands affect write operations only.
    3242             :                  */
    3243             :             case AT_EnableTrig:
    3244             :             case AT_EnableAlwaysTrig:
    3245             :             case AT_EnableReplicaTrig:
    3246             :             case AT_EnableTrigAll:
    3247             :             case AT_EnableTrigUser:
    3248             :             case AT_DisableTrig:
    3249             :             case AT_DisableTrigAll:
    3250             :             case AT_DisableTrigUser:
    3251           6 :                 cmd_lockmode = ShareRowExclusiveLock;
    3252           6 :                 break;
    3253             : 
    3254             :                 /*
    3255             :                  * These subcommands affect write operations only. XXX
    3256             :                  * Theoretically, these could be ShareRowExclusiveLock.
    3257             :                  */
    3258             :             case AT_ColumnDefault:
    3259             :             case AT_AlterConstraint:
    3260             :             case AT_AddIndex:   /* from ADD CONSTRAINT */
    3261             :             case AT_AddIndexConstraint:
    3262             :             case AT_ReplicaIdentity:
    3263             :             case AT_SetNotNull:
    3264             :             case AT_EnableRowSecurity:
    3265             :             case AT_DisableRowSecurity:
    3266             :             case AT_ForceRowSecurity:
    3267             :             case AT_NoForceRowSecurity:
    3268             :             case AT_AddIdentity:
    3269             :             case AT_DropIdentity:
    3270             :             case AT_SetIdentity:
    3271         164 :                 cmd_lockmode = AccessExclusiveLock;
    3272         164 :                 break;
    3273             : 
    3274             :             case AT_AddConstraint:
    3275             :             case AT_ProcessedConstraint:    /* becomes AT_AddConstraint */
    3276             :             case AT_AddConstraintRecurse:   /* becomes AT_AddConstraint */
    3277             :             case AT_ReAddConstraint:    /* becomes AT_AddConstraint */
    3278         243 :                 if (IsA(cmd->def, Constraint))
    3279             :                 {
    3280         243 :                     Constraint *con = (Constraint *) cmd->def;
    3281             : 
    3282         243 :                     switch (con->contype)
    3283             :                     {
    3284             :                         case CONSTR_EXCLUSION:
    3285             :                         case CONSTR_PRIMARY:
    3286             :                         case CONSTR_UNIQUE:
    3287             : 
    3288             :                             /*
    3289             :                              * Cases essentially the same as CREATE INDEX. We
    3290             :                              * could reduce the lock strength to ShareLock if
    3291             :                              * we can work out how to allow concurrent catalog
    3292             :                              * updates. XXX Might be set down to
    3293             :                              * ShareRowExclusiveLock but requires further
    3294             :                              * analysis.
    3295             :                              */
    3296          41 :                             cmd_lockmode = AccessExclusiveLock;
    3297          41 :                             break;
    3298             :                         case CONSTR_FOREIGN:
    3299             : 
    3300             :                             /*
    3301             :                              * We add triggers to both tables when we add a
    3302             :                              * Foreign Key, so the lock level must be at least
    3303             :                              * as strong as CREATE TRIGGER.
    3304             :                              */
    3305         135 :                             cmd_lockmode = ShareRowExclusiveLock;
    3306         135 :                             break;
    3307             : 
    3308             :                         default:
    3309          67 :                             cmd_lockmode = AccessExclusiveLock;
    3310             :                     }
    3311             :                 }
    3312         243 :                 break;
    3313             : 
    3314             :                 /*
    3315             :                  * These subcommands affect inheritance behaviour. Queries
    3316             :                  * started before us will continue to see the old inheritance
    3317             :                  * behaviour, while queries started after we commit will see
    3318             :                  * new behaviour. No need to prevent reads or writes to the
    3319             :                  * subtable while we hook it up though. Changing the TupDesc
    3320             :                  * may be a problem, so keep highest lock.
    3321             :                  */
    3322             :             case AT_AddInherit:
    3323             :             case AT_DropInherit:
    3324          30 :                 cmd_lockmode = AccessExclusiveLock;
    3325          30 :                 break;
    3326             : 
    3327             :                 /*
    3328             :                  * These subcommands affect implicit row type conversion. They
    3329             :                  * have affects similar to CREATE/DROP CAST on queries. don't
    3330             :                  * provide for invalidating parse trees as a result of such
    3331             :                  * changes, so we keep these at AccessExclusiveLock.
    3332             :                  */
    3333             :             case AT_AddOf:
    3334             :             case AT_DropOf:
    3335          10 :                 cmd_lockmode = AccessExclusiveLock;
    3336          10 :                 break;
    3337             : 
    3338             :                 /*
    3339             :                  * Only used by CREATE OR REPLACE VIEW which must conflict
    3340             :                  * with an SELECTs currently using the view.
    3341             :                  */
    3342             :             case AT_ReplaceRelOptions:
    3343          20 :                 cmd_lockmode = AccessExclusiveLock;
    3344          20 :                 break;
    3345             : 
    3346             :                 /*
    3347             :                  * These subcommands affect general strategies for performance
    3348             :                  * and maintenance, though don't change the semantic results
    3349             :                  * from normal data reads and writes. Delaying an ALTER TABLE
    3350             :                  * behind currently active writes only delays the point where
    3351             :                  * the new strategy begins to take effect, so there is no
    3352             :                  * benefit in waiting. In this case the minimum restriction
    3353             :                  * applies: we don't currently allow concurrent catalog
    3354             :                  * updates.
    3355             :                  */
    3356             :             case AT_SetStatistics:  /* Uses MVCC in getTableAttrs() */
    3357             :             case AT_ClusterOn:  /* Uses MVCC in getIndexes() */
    3358             :             case AT_DropCluster:    /* Uses MVCC in getIndexes() */
    3359             :             case AT_SetOptions: /* Uses MVCC in getTableAttrs() */
    3360             :             case AT_ResetOptions:   /* Uses MVCC in getTableAttrs() */
    3361          19 :                 cmd_lockmode = ShareUpdateExclusiveLock;
    3362          19 :                 break;
    3363             : 
    3364             :             case AT_SetLogged:
    3365             :             case AT_SetUnLogged:
    3366           9 :                 cmd_lockmode = AccessExclusiveLock;
    3367           9 :                 break;
    3368             : 
    3369             :             case AT_ValidateConstraint: /* Uses MVCC in getConstraints() */
    3370          15 :                 cmd_lockmode = ShareUpdateExclusiveLock;
    3371          15 :                 break;
    3372             : 
    3373             :                 /*
    3374             :                  * Rel options are more complex than first appears. Options
    3375             :                  * are set here for tables, views and indexes; for historical
    3376             :                  * reasons these can all be used with ALTER TABLE, so we can't
    3377             :                  * decide between them using the basic grammar.
    3378             :                  */
    3379             :             case AT_SetRelOptions:  /* Uses MVCC in getIndexes() and
    3380             :                                      * getTables() */
    3381             :             case AT_ResetRelOptions:    /* Uses MVCC in getIndexes() and
    3382             :                                          * getTables() */
    3383          34 :                 cmd_lockmode = AlterTableGetRelOptionsLockLevel((List *) cmd->def);
    3384          34 :                 break;
    3385             : 
    3386             :             case AT_AttachPartition:
    3387             :             case AT_DetachPartition:
    3388          69 :                 cmd_lockmode = AccessExclusiveLock;
    3389          69 :                 break;
    3390             : 
    3391             :             default:            /* oops */
    3392           0 :                 elog(ERROR, "unrecognized alter table type: %d",
    3393             :                      (int) cmd->subtype);
    3394             :                 break;
    3395             :         }
    3396             : 
    3397             :         /*
    3398             :          * Take the greatest lockmode from any subcommand
    3399             :          */
    3400        1094 :         if (cmd_lockmode > lockmode)
    3401        1012 :             lockmode = cmd_lockmode;
    3402             :     }
    3403             : 
    3404        1068 :     return lockmode;
    3405             : }
    3406             : 
    3407             : /*
    3408             :  * ATController provides top level control over the phases.
    3409             :  *
    3410             :  * parsetree is passed in to allow it to be passed to event triggers
    3411             :  * when requested.
    3412             :  */
    3413             : static void
    3414        1026 : ATController(AlterTableStmt *parsetree,
    3415             :              Relation rel, List *cmds, bool recurse, LOCKMODE lockmode)
    3416             : {
    3417        1026 :     List       *wqueue = NIL;
    3418             :     ListCell   *lcmd;
    3419             : 
    3420             :     /* Phase 1: preliminary examination of commands, create work queue */
    3421        2050 :     foreach(lcmd, cmds)
    3422             :     {
    3423        1057 :         AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);
    3424             : 
    3425        1057 :         ATPrepCmd(&wqueue, rel, cmd, recurse, false, lockmode);
    3426             :     }
    3427             : 
    3428             :     /* Close the relation, but keep lock until commit */
    3429         993 :     relation_close(rel, NoLock);
    3430             : 
    3431             :     /* Phase 2: update system catalogs */
    3432         993 :     ATRewriteCatalogs(&wqueue, lockmode);
    3433             : 
    3434             :     /* Phase 3: scan/rewrite tables as needed */
    3435         786 :     ATRewriteTables(parsetree, &wqueue, lockmode);
    3436         762 : }
    3437             : 
    3438             : /*
    3439             :  * ATPrepCmd
    3440             :  *
    3441             :  * Traffic cop for ALTER TABLE Phase 1 operations, including simple
    3442             :  * recursion and permission checks.
    3443             :  *
    3444             :  * Caller must have acquired appropriate lock type on relation already.
    3445             :  * This lock should be held until commit.
    3446             :  */
    3447             : static void
    3448        1125 : ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
    3449             :           bool recurse, bool recursing, LOCKMODE lockmode)
    3450             : {
    3451             :     AlteredTableInfo *tab;
    3452        1125 :     int         pass = AT_PASS_UNSET;
    3453             : 
    3454             :     /* Find or create work queue entry for this table */
    3455        1125 :     tab = ATGetQueueEntry(wqueue, rel);
    3456             : 
    3457             :     /*
    3458             :      * Copy the original subcommand for each table.  This avoids conflicts
    3459             :      * when different child tables need to make different parse
    3460             :      * transformations (for example, the same column may have different column
    3461             :      * numbers in different children).
    3462             :      */
    3463        1125 :     cmd = copyObject(cmd);
    3464             : 
    3465             :     /*
    3466             :      * Do permissions checking, recursion to child tables if needed, and any
    3467             :      * additional phase-1 processing needed.
    3468             :      */
    3469        1125 :     switch (cmd->subtype)
    3470             :     {
    3471             :         case AT_AddColumn:      /* ADD COLUMN */
    3472         149 :             ATSimplePermissions(rel,
    3473             :                                 ATT_TABLE | ATT_COMPOSITE_TYPE | ATT_FOREIGN_TABLE);
    3474         149 :             ATPrepAddColumn(wqueue, rel, recurse, recursing, false, cmd,
    3475             :                             lockmode);
    3476             :             /* Recursion occurs during execution phase */
    3477         147 :             pass = AT_PASS_ADD_COL;
    3478         147 :             break;
    3479             :         case AT_AddColumnToView:    /* add column via CREATE OR REPLACE VIEW */
    3480           2 :             ATSimplePermissions(rel, ATT_VIEW);
    3481           2 :             ATPrepAddColumn(wqueue, rel, recurse, recursing, true, cmd,
    3482             :                             lockmode);
    3483             :             /* Recursion occurs during execution phase */
    3484           2 :             pass = AT_PASS_ADD_COL;
    3485           2 :             break;
    3486             :         case AT_ColumnDefault:  /* ALTER COLUMN DEFAULT */
    3487             : 
    3488             :             /*
    3489             :              * We allow defaults on views so that INSERT into a view can have
    3490             :              * default-ish behavior.  This works because the rewriter
    3491             :              * substitutes default values into INSERTs before it expands
    3492             :              * rules.
    3493             :              */
    3494          36 :             ATSimplePermissions(rel, ATT_TABLE | ATT_VIEW | ATT_FOREIGN_TABLE);
    3495          36 :             ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode);
    3496             :             /* No command-specific prep needed */
    3497          36 :             pass = cmd->def ? AT_PASS_ADD_CONSTR : AT_PASS_DROP;
    3498          36 :             break;
    3499             :         case AT_AddIdentity:
    3500           6 :             ATSimplePermissions(rel, ATT_TABLE | ATT_VIEW | ATT_FOREIGN_TABLE);
    3501           6 :             pass = AT_PASS_ADD_CONSTR;
    3502           6 :             break;
    3503             :         case AT_DropIdentity:
    3504           4 :             ATSimplePermissions(rel, ATT_TABLE | ATT_VIEW | ATT_FOREIGN_TABLE);
    3505           4 :             pass = AT_PASS_DROP;
    3506           4 :             break;
    3507             :         case AT_SetIdentity:
    3508           4 :             ATSimplePermissions(rel, ATT_TABLE | ATT_VIEW | ATT_FOREIGN_TABLE);
    3509           4 :             pass = AT_PASS_COL_ATTRS;
    3510           4 :             break;
    3511             :         case AT_DropNotNull:    /* ALTER COLUMN DROP NOT NULL */
    3512          20 :             ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);
    3513          19 :             ATPrepDropNotNull(rel, recurse, recursing);
    3514          18 :             ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode);
    3515             :             /* No command-specific prep needed */
    3516          18 :             pass = AT_PASS_DROP;
    3517          18 :             break;
    3518             :         case AT_SetNotNull:     /* ALTER COLUMN SET NOT NULL */
    3519          55 :             ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);
    3520          54 :             ATPrepSetNotNull(rel, recurse, recursing);
    3521          53 :             ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode);
    3522             :             /* No command-specific prep needed */
    3523          53 :             pass = AT_PASS_ADD_CONSTR;
    3524          53 :             break;
    3525             :         case AT_SetStatistics:  /* ALTER COLUMN SET STATISTICS */
    3526          15 :             ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode);
    3527             :             /* Performs own permission checks */
    3528          15 :             ATPrepSetStatistics(rel, cmd->name, cmd->def, lockmode);
    3529          15 :             pass = AT_PASS_MISC;
    3530          15 :             break;
    3531             :         case AT_SetOptions:     /* ALTER COLUMN SET ( options ) */
    3532             :         case AT_ResetOptions:   /* ALTER COLUMN RESET ( options ) */
    3533           5 :             ATSimplePermissions(rel, ATT_TABLE | ATT_MATVIEW | ATT_INDEX | ATT_FOREIGN_TABLE);
    3534             :             /* This command never recurses */
    3535           5 :             pass = AT_PASS_MISC;
    3536           5 :             break;
    3537             :         case AT_SetStorage:     /* ALTER COLUMN SET STORAGE */
    3538          17 :             ATSimplePermissions(rel, ATT_TABLE | ATT_MATVIEW | ATT_FOREIGN_TABLE);
    3539          17 :             ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode);
    3540             :             /* No command-specific prep needed */
    3541          17 :             pass = AT_PASS_MISC;
    3542          17 :             break;
    3543             :         case AT_DropColumn:     /* DROP COLUMN */
    3544         115 :             ATSimplePermissions(rel,
    3545             :                                 ATT_TABLE | ATT_COMPOSITE_TYPE | ATT_FOREIGN_TABLE);
    3546         114 :             ATPrepDropColumn(wqueue, rel, recurse, recursing, cmd, lockmode);
    3547             :             /* Recursion occurs during execution phase */
    3548         112 :             pass = AT_PASS_DROP;
    3549         112 :             break;
    3550             :         case AT_AddIndex:       /* ADD INDEX */
    3551          34 :             ATSimplePermissions(rel, ATT_TABLE);
    3552             :             /* This command never recurses */
    3553             :             /* No command-specific prep needed */
    3554          34 :             pass = AT_PASS_ADD_INDEX;
    3555          34 :             break;
    3556             :         case AT_AddConstraint:  /* ADD CONSTRAINT */
    3557         204 :             ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);
    3558             :             /* Recursion occurs during execution phase */
    3559             :             /* No command-specific prep needed except saving recurse flag */
    3560         204 :             if (recurse)
    3561         202 :                 cmd->subtype = AT_AddConstraintRecurse;
    3562         204 :             pass = AT_PASS_ADD_CONSTR;
    3563         204 :             break;
    3564             :         case AT_AddIndexConstraint: /* ADD CONSTRAINT USING INDEX */
    3565           2 :             ATSimplePermissions(rel, ATT_TABLE);
    3566             :             /* This command never recurses */
    3567             :             /* No command-specific prep needed */
    3568           2 :             pass = AT_PASS_ADD_CONSTR;
    3569           2 :             break;
    3570             :         case AT_DropConstraint: /* DROP CONSTRAINT */
    3571          45 :             ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);
    3572             :             /* Recursion occurs during execution phase */
    3573             :             /* No command-specific prep needed except saving recurse flag */
    3574          45 :             if (recurse)
    3575          41 :                 cmd->subtype = AT_DropConstraintRecurse;
    3576          45 :             pass = AT_PASS_DROP;
    3577          45 :             break;
    3578             :         case AT_AlterColumnType:    /* ALTER COLUMN TYPE */
    3579          90 :             ATSimplePermissions(rel,
    3580             :                                 ATT_TABLE | ATT_COMPOSITE_TYPE | ATT_FOREIGN_TABLE);
    3581             :             /* Performs own recursion */
    3582          90 :             ATPrepAlterColumnType(wqueue, tab, rel, recurse, recursing, cmd, lockmode);
    3583          70 :             pass = AT_PASS_ALTER_TYPE;
    3584          70 :             break;
    3585             :         case AT_AlterColumnGenericOptions:
    3586           7 :             ATSimplePermissions(rel, ATT_FOREIGN_TABLE);
    3587             :             /* This command never recurses */
    3588             :             /* No command-specific prep needed */
    3589           7 :             pass = AT_PASS_MISC;
    3590           7 :             break;
    3591             :         case AT_ChangeOwner:    /* ALTER OWNER */
    3592             :             /* This command never recurses */
    3593             :             /* No command-specific prep needed */
    3594          17 :             pass = AT_PASS_MISC;
    3595          17 :             break;
    3596             :         case AT_ClusterOn:      /* CLUSTER ON */
    3597             :         case AT_DropCluster:    /* SET WITHOUT CLUSTER */
    3598           5 :             ATSimplePermissions(rel, ATT_TABLE | ATT_MATVIEW);
    3599             :             /* These commands never recurse */
    3600             :             /* No command-specific prep needed */
    3601           5 :             pass = AT_PASS_MISC;
    3602           5 :             break;
    3603             :         case AT_SetLogged:      /* SET LOGGED */
    3604           4 :             ATSimplePermissions(rel, ATT_TABLE);
    3605           4 :             tab->chgPersistence = ATPrepChangePersistence(rel, true);
    3606             :             /* force rewrite if necessary; see comment in ATRewriteTables */
    3607           3 :             if (tab->chgPersistence)
    3608             :             {
    3609           2 :                 tab->rewrite |= AT_REWRITE_ALTER_PERSISTENCE;
    3610           2 :                 tab->newrelpersistence = RELPERSISTENCE_PERMANENT;
    3611             :             }
    3612           3 :             pass = AT_PASS_MISC;
    3613           3 :             break;
    3614             :         case AT_SetUnLogged:    /* SET UNLOGGED */
    3615           5 :             ATSimplePermissions(rel, ATT_TABLE);
    3616           5 :             tab->chgPersistence = ATPrepChangePersistence(rel, false);
    3617             :             /* force rewrite if necessary; see comment in ATRewriteTables */
    3618           4 :             if (tab->chgPersistence)
    3619             :             {
    3620           3 :                 tab->rewrite |= AT_REWRITE_ALTER_PERSISTENCE;
    3621           3 :                 tab->newrelpersistence = RELPERSISTENCE_UNLOGGED;
    3622             :             }
    3623           4 :             pass = AT_PASS_MISC;
    3624           4 :             break;
    3625             :         case AT_AddOids:        /* SET WITH OIDS */
    3626           9 :             ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);
    3627           9 :             if (!rel->rd_rel->relhasoids || recursing)
    3628           9 :                 ATPrepAddOids(wqueue, rel, recurse, cmd, lockmode);
    3629             :             /* Recursion occurs during execution phase */
    3630           9 :             pass = AT_PASS_ADD_COL;
    3631           9 :             break;
    3632             :         case AT_DropOids:       /* SET WITHOUT OIDS */
    3633          10 :             ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);
    3634             :             /* Performs own recursion */
    3635          10 :             if (rel->rd_rel->relhasoids)
    3636             :             {
    3637          10 :                 AlterTableCmd *dropCmd = makeNode(AlterTableCmd);
    3638             : 
    3639          10 :                 dropCmd->subtype = AT_DropColumn;
    3640          10 :                 dropCmd->name = pstrdup("oid");
    3641          10 :                 dropCmd->behavior = cmd->behavior;
    3642          10 :                 ATPrepCmd(wqueue, rel, dropCmd, recurse, false, lockmode);
    3643             :             }
    3644          10 :             pass = AT_PASS_DROP;
    3645          10 :             break;
    3646             :         case AT_SetTableSpace:  /* SET TABLESPACE */
    3647           9 :             ATSimplePermissions(rel, ATT_TABLE | ATT_MATVIEW | ATT_INDEX);
    3648             :             /* This command never recurses */
    3649           9 :             ATPrepSetTableSpace(tab, rel, cmd->name, lockmode);
    3650           9 :             pass = AT_PASS_MISC;    /* doesn't actually matter */
    3651           9 :             break;
    3652             :         case AT_SetRelOptions:  /* SET (...) */
    3653             :         case AT_ResetRelOptions:    /* RESET (...) */
    3654             :         case AT_ReplaceRelOptions:  /* reset them all, then set just these */
    3655          54 :             ATSimplePermissions(rel, ATT_TABLE | ATT_VIEW | ATT_MATVIEW | ATT_INDEX);
    3656             :             /* This command never recurses */
    3657             :             /* No command-specific prep needed */
    3658          54 :             pass = AT_PASS_MISC;
    3659          54 :             break;
    3660             :         case AT_AddInherit:     /* INHERIT */
    3661          25 :             ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);
    3662             :             /* This command never recurses */
    3663          25 :             ATPrepAddInherit(rel);
    3664          22 :             pass = AT_PASS_MISC;
    3665          22 :             break;
    3666             :         case AT_DropInherit:    /* NO INHERIT */
    3667           5 :             ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);
    3668             :             /* This command never recurses */
    3669             :             /* No command-specific prep needed */
    3670           5 :             pass = AT_PASS_MISC;
    3671           5 :             break;
    3672             :         case AT_AlterConstraint:    /* ALTER CONSTRAINT */
    3673           6 :             ATSimplePermissions(rel, ATT_TABLE);
    3674           5 :             pass = AT_PASS_MISC;
    3675           5 :             break;
    3676             :         case AT_ValidateConstraint: /* VALIDATE CONSTRAINT */
    3677          15 :             ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);
    3678             :             /* Recursion occurs during execution phase */
    3679             :             /* No command-specific prep needed except saving recurse flag */
    3680          15 :             if (recurse)
    3681          15 :                 cmd->subtype = AT_ValidateConstraintRecurse;
    3682          15 :             pass = AT_PASS_MISC;
    3683          15 :             break;
    3684             :         case AT_ReplicaIdentity:    /* REPLICA IDENTITY ... */
    3685          15 :             ATSimplePermissions(rel, ATT_TABLE | ATT_MATVIEW);
    3686          15 :             pass = AT_PASS_MISC;
    3687             :             /* This command never recurses */
    3688             :             /* No command-specific prep needed */
    3689          15 :             break;
    3690             :         case AT_EnableTrig:     /* ENABLE TRIGGER variants */
    3691             :         case AT_EnableAlwaysTrig:
    3692             :         case AT_EnableReplicaTrig:
    3693             :         case AT_EnableTrigAll:
    3694             :         case AT_EnableTrigUser:
    3695             :         case AT_DisableTrig:    /* DISABLE TRIGGER variants */
    3696             :         case AT_DisableTrigAll:
    3697             :         case AT_DisableTrigUser:
    3698           6 :             ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);
    3699           6 :             pass = AT_PASS_MISC;
    3700           6 :             break;
    3701             :         case AT_EnableRule:     /* ENABLE/DISABLE RULE variants */
    3702             :         case AT_EnableAlwaysRule:
    3703             :         case AT_EnableReplicaRule:
    3704             :         case AT_DisableRule:
    3705             :         case AT_AddOf:          /* OF */
    3706             :         case AT_DropOf:         /* NOT OF */
    3707             :         case AT_EnableRowSecurity:
    3708             :         case AT_DisableRowSecurity:
    3709             :         case AT_ForceRowSecurity:
    3710             :         case AT_NoForceRowSecurity:
    3711          63 :             ATSimplePermissions(rel, ATT_TABLE);
    3712             :             /* These commands never recurse */
    3713             :             /* No command-specific prep needed */
    3714          63 :             pass = AT_PASS_MISC;
    3715          63 :             break;
    3716             :         case AT_GenericOptions:
    3717           1 :             ATSimplePermissions(rel, ATT_FOREIGN_TABLE);
    3718             :             /* No command-specific prep needed */
    3719           1 :             pass = AT_PASS_MISC;
    3720           1 :             break;
    3721             :         case AT_AttachPartition:
    3722             :         case AT_DetachPartition:
    3723          66 :             ATSimplePermissions(rel, ATT_TABLE);
    3724             :             /* No command-specific prep needed */
    3725          66 :             pass = AT_PASS_MISC;
    3726          66 :             break;
    3727             :         default:                /* oops */
    3728           0 :             elog(ERROR, "unrecognized alter table type: %d",
    3729             :                  (int) cmd->subtype);
    3730             :             pass = AT_PASS_UNSET;   /* keep compiler quiet */
    3731             :             break;
    3732             :     }
    3733        1090 :     Assert(pass > AT_PASS_UNSET);
    3734             : 
    3735             :     /* Add the subcommand to the appropriate list for phase 2 */
    3736        1090 :     tab->subcmds[pass] = lappend(tab->subcmds[pass], cmd);
    3737        1090 : }
    3738             : 
    3739             : /*
    3740             :  * ATRewriteCatalogs
    3741             :  *
    3742             :  * Traffic cop for ALTER TABLE Phase 2 operations.  Subcommands are
    3743             :  * dispatched in a "safe" execution order (designed to avoid unnecessary
    3744             :  * conflicts).
    3745             :  */
    3746             : static void
    3747         993 : ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode)
    3748             : {
    3749             :     int         pass;
    3750             :     ListCell   *ltab;
    3751             : 
    3752             :     /*
    3753             :      * We process all the tables "in parallel", one pass at a time.  This is
    3754             :      * needed because we may have to propagate work from one table to another
    3755             :      * (specifically, ALTER TYPE on a foreign key's PK has to dispatch the
    3756             :      * re-adding of the foreign key constraint to the other table).  Work can
    3757             :      * only be propagated into later passes, however.
    3758             :      */
    3759        9207 :     for (pass = 0; pass < AT_NUM_PASSES; pass++)
    3760             :     {
    3761             :         /* Go through each table that needs to be processed */
    3762       17439 :         foreach(ltab, *wqueue)
    3763             :         {
    3764        9225 :             AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
    3765        9225 :             List       *subcmds = tab->subcmds[pass];
    3766             :             Relation    rel;
    3767             :             ListCell   *lcmd;
    3768             : 
    3769        9225 :             if (subcmds == NIL)
    3770        8141 :                 continue;
    3771             : 
    3772             :             /*
    3773             :              * Appropriate lock was obtained by phase 1, needn't get it again
    3774             :              */
    3775        1084 :             rel = relation_open(tab->relid, NoLock);
    3776             : 
    3777        2002 :             foreach(lcmd, subcmds)
    3778        1125 :                 ATExecCmd(wqueue, tab, rel, (AlterTableCmd *) lfirst(lcmd), lockmode);
    3779             : 
    3780             :             /*
    3781             :              * After the ALTER TYPE pass, do cleanup work (this is not done in
    3782             :              * ATExecAlterColumnType since it should be done only once if
    3783             :              * multiple columns of a table are altered).
    3784             :              */
    3785         877 :             if (pass == AT_PASS_ALTER_TYPE)
    3786          67 :                 ATPostAlterTypeCleanup(wqueue, tab, lockmode);
    3787             : 
    3788         877 :             relation_close(rel, NoLock);
    3789             :         }
    3790             :     }
    3791             : 
    3792             :     /* Check to see if a toast table must be added. */
    3793        1737 :     foreach(ltab, *wqueue)
    3794             :     {
    3795         951 :         AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
    3796             : 
    3797             :         /*
    3798             :          * If the table is source table of ATTACH PARTITION command, we did
    3799             :          * not modify anything about it that will change its toasting
    3800             :          * requirement, so no need to check.
    3801             :          */
    3802        1158 :         if (((tab->relkind == RELKIND_RELATION ||
    3803        1023 :               tab->relkind == RELKIND_PARTITIONED_TABLE) &&
    3804         974 :              tab->partition_constraint == NULL) ||
    3805         158 :             tab->relkind == RELKIND_MATVIEW)
    3806         793 :             AlterTableCreateToastTable(tab->relid, (Datum) 0, lockmode);
    3807             :     }
    3808         786 : }
    3809             : 
    3810             : /*
    3811             :  * ATExecCmd: dispatch a subcommand to appropriate execution routine
    3812             :  */
    3813             : static void
    3814        1125 : ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
    3815             :           AlterTableCmd *cmd, LOCKMODE lockmode)
    3816             : {
    3817        1125 :     ObjectAddress address = InvalidObjectAddress;
    3818             : 
    3819        1125 :     switch (cmd->subtype)
    3820             :     {
    3821             :         case AT_AddColumn:      /* ADD COLUMN */
    3822             :         case AT_AddColumnToView:    /* add column via CREATE OR REPLACE VIEW */
    3823          11 :             address = ATExecAddColumn(wqueue, tab, rel, (ColumnDef *) cmd->def,
    3824             :                                       false, false, false,
    3825             :                                       false, lockmode);
    3826           7 :             break;
    3827             :         case AT_AddColumnRecurse:
    3828         137 :             address = ATExecAddColumn(wqueue, tab, rel, (ColumnDef *) cmd->def,
    3829             :                                       false, true, false,
    3830         137 :                                       cmd->missing_ok, lockmode);
    3831         126 :             break;
    3832             :         case AT_ColumnDefault:  /* ALTER COLUMN DEFAULT */
    3833          36 :             address = ATExecColumnDefault(rel, cmd->name, cmd->def, lockmode);
    3834          29 :             break;
    3835             :         case AT_AddIdentity:
    3836           6 :             address = ATExecAddIdentity(rel, cmd->name, cmd->def, lockmode);
    3837           2 :             break;
    3838             :         case AT_SetIdentity:
    3839           4 :             address = ATExecSetIdentity(rel, cmd->name, cmd->def, lockmode);
    3840           3 :             break;
    3841             :         case AT_DropIdentity:
    3842           4 :             address = ATExecDropIdentity(rel, cmd->name, cmd->missing_ok, lockmode);
    3843           3 :             break;
    3844             :         case AT_DropNotNull:    /* ALTER COLUMN DROP NOT NULL */
    3845          18 :             address = ATExecDropNotNull(rel, cmd->name, lockmode);
    3846          10 :             break;
    3847             :         case AT_SetNotNull:     /* ALTER COLUMN SET NOT NULL */
    3848          53 :             address = ATExecSetNotNull(tab, rel, cmd->name, lockmode);
    3849          49 :             break;
    3850             :         case AT_SetStatistics:  /* ALTER COLUMN SET STATISTICS */
    3851          15 :             address = ATExecSetStatistics(rel, cmd->name, cmd->def, lockmode);
    3852          13 :             break;
    3853             :         case AT_SetOptions:     /* ALTER COLUMN SET ( options ) */
    3854           4 :             address = ATExecSetOptions(rel, cmd->name, cmd->def, false, lockmode);
    3855           4 :             break;
    3856             :         case AT_ResetOptions:   /* ALTER COLUMN RESET ( options ) */
    3857           1 :             address = ATExecSetOptions(rel, cmd->name, cmd->def, true, lockmode);
    3858           1 :             break;
    3859             :         case AT_SetStorage:     /* ALTER COLUMN SET STORAGE */
    3860          17 :             address = ATExecSetStorage(rel, cmd->name, cmd->def, lockmode);
    3861          15 :             break;
    3862             :         case AT_DropColumn:     /* DROP COLUMN */
    3863          13 :             address = ATExecDropColumn(wqueue, rel, cmd->name,
    3864             :                                        cmd->behavior, false, false,
    3865          13 :                                        cmd->missing_ok, lockmode);
    3866          10 :             break;
    3867             :         case AT_DropColumnRecurse:  /* DROP COLUMN with recursion */
    3868          99 :             address = ATExecDropColumn(wqueue, rel, cmd->name,
    3869             :                                        cmd->behavior, true, false,
    3870          99 :                                        cmd->missing_ok, lockmode);
    3871          80 :             break;
    3872             :         case AT_AddIndex:       /* ADD INDEX */
    3873          34 :             address = ATExecAddIndex(tab, rel, (IndexStmt *) cmd->def, false,
    3874             :                                      lockmode);
    3875          21 :             break;
    3876             :         case AT_ReAddIndex:     /* ADD INDEX */
    3877          19 :             address = ATExecAddIndex(tab, rel, (IndexStmt *) cmd->def, true,
    3878             :                                      lockmode);
    3879          19 :             break;
    3880             :         case AT_AddConstraint:  /* ADD CONSTRAINT */
    3881           2 :             address =
    3882           2 :                 ATExecAddConstraint(wqueue, tab, rel, (Constraint *) cmd->def,
    3883             :                                     false, false, lockmode);
    3884           1 :             break;
    3885             :         case AT_AddConstraintRecurse:   /* ADD CONSTRAINT with recursion */
    3886         202 :             address =
    3887         202 :                 ATExecAddConstraint(wqueue, tab, rel, (Constraint *) cmd->def,
    3888             :                                     true, false, lockmode);
    3889         152 :             break;
    3890             :         case AT_ReAddConstraint:    /* Re-add pre-existing check constraint */
    3891          13 :             address =
    3892          13 :                 ATExecAddConstraint(wqueue, tab, rel, (Constraint *) cmd->def,
    3893             :                                     true, true, lockmode);
    3894          11 :             break;
    3895             :         case AT_ReAddComment:   /* Re-add existing comment */
    3896           7 :             address = CommentObject((CommentStmt *) cmd->def);
    3897           7 :             break;
    3898             :         case AT_AddIndexConstraint: /* ADD CONSTRAINT USING INDEX */
    3899           2 :             address = ATExecAddIndexConstraint(tab, rel, (IndexStmt *) cmd->def,
    3900             :                                                lockmode);
    3901           2 :             break;
    3902             :         case AT_AlterConstraint:    /* ALTER CONSTRAINT */
    3903           5 :             address = ATExecAlterConstraint(rel, cmd, false, false, lockmode);
    3904           5 :             break;
    3905             :         case AT_ValidateConstraint: /* VALIDATE CONSTRAINT */
    3906           0 :             address = ATExecValidateConstraint(rel, cmd->name, false, false,
    3907             :                                                lockmode);
    3908           0 :             break;
    3909             :         case AT_ValidateConstraintRecurse:  /* VALIDATE CONSTRAINT with
    3910             :                                              * recursion */
    3911          15 :             address = ATExecValidateConstraint(rel, cmd->name, true, false,
    3912             :                                                lockmode);
    3913          11 :             break;
    3914             :         case AT_DropConstraint: /* DROP CONSTRAINT */
    3915           4 :             ATExecDropConstraint(rel, cmd->name, cmd->behavior,
    3916             :                                  false, false,
    3917           4 :                                  cmd->missing_ok, lockmode);
    3918           3 :             break;
    3919             :         case AT_DropConstraintRecurse:  /* DROP CONSTRAINT with recursion */
    3920          41 :             ATExecDropConstraint(rel, cmd->name, cmd->behavior,
    3921             :                                  true, false,
    3922          41 :                                  cmd->missing_ok, lockmode);
    3923          30 :             break;
    3924             :         case AT_AlterColumnType:    /* ALTER COLUMN TYPE */
    3925          69 :             address = ATExecAlterColumnType(tab, rel, cmd, lockmode);
    3926          68 :             break;
    3927             :         case AT_AlterColumnGenericOptions:  /* ALTER COLUMN OPTIONS */
    3928           7 :             address =
    3929           7 :                 ATExecAlterColumnGenericOptions(rel, cmd->name,
    3930           7 :                                                 (List *) cmd->def, lockmode);
    3931           6 :             break;
    3932             :         case AT_ChangeOwner:    /* ALTER OWNER */
    3933          17 :             ATExecChangeOwner(RelationGetRelid(rel),
    3934          17 :                               get_rolespec_oid(cmd->newowner, false),
    3935             :                               false, lockmode);
    3936          14 :             break;
    3937             :         case AT_ClusterOn:      /* CLUSTER ON */
    3938           3 :             address = ATExecClusterOn(rel, cmd->name, lockmode);
    3939           3 :             break;
    3940             :         case AT_DropCluster:    /* SET WITHOUT CLUSTER */
    3941           2 :             ATExecDropCluster(rel, lockmode);
    3942           2 :             break;
    3943             :         case AT_SetLogged:      /* SET LOGGED */
    3944             :         case AT_SetUnLogged:    /* SET UNLOGGED */
    3945           7 :             break;
    3946             :         case AT_AddOids:        /* SET WITH OIDS */
    3947             :             /* Use the ADD COLUMN code, unless prep decided to do nothing */
    3948           0 :             if (cmd->def != NULL)
    3949           0 :                 address =
    3950           0 :                     ATExecAddColumn(wqueue, tab, rel, (ColumnDef *) cmd->def,
    3951             :                                     true, false, false,
    3952           0 :                                     cmd->missing_ok, lockmode);
    3953           0 :             break;
    3954             :         case AT_AddOidsRecurse: /* SET WITH OIDS */
    3955             :             /* Use the ADD COLUMN code, unless prep decided to do nothing */
    3956           9 :             if (cmd->def != NULL)
    3957           9 :                 address =
    3958           9 :                     ATExecAddColumn(wqueue, tab, rel, (ColumnDef *) cmd->def,
    3959             :                                     true, true, false,
    3960           9 :                                     cmd->missing_ok, lockmode);
    3961           9 :             break;
    3962             :         case AT_DropOids:       /* SET WITHOUT OIDS */
    3963             : 
    3964             :             /*
    3965             :              * Nothing to do here; we'll have generated a DropColumn
    3966             :              * subcommand to do the real work
    3967             :              */
    3968           8 :             break;
    3969             :         case AT_SetTableSpace:  /* SET TABLESPACE */
    3970             : 
    3971             :             /*
    3972             :              * Nothing to do here; Phase 3 does the work
    3973             :              */
    3974           9 :             break;
    3975             :         case AT_SetRelOptions:  /* SET (...) */
    3976             :         case AT_ResetRelOptions:    /* RESET (...) */
    3977             :         case AT_ReplaceRelOptions:  /* replace entire option list */
    3978          54 :             ATExecSetRelOptions(rel, (List *) cmd->def, cmd->subtype, lockmode);
    3979          50 :             break;
    3980             :         case AT_EnableTrig:     /* ENABLE TRIGGER name */
    3981           2 :             ATExecEnableDisableTrigger(rel, cmd->name,
    3982             :                                        TRIGGER_FIRES_ON_ORIGIN, false, lockmode);
    3983           2 :             break;
    3984             :         case AT_EnableAlwaysTrig:   /* ENABLE ALWAYS TRIGGER name */
    3985           0 :             ATExecEnableDisableTrigger(rel, cmd->name,
    3986             :                                        TRIGGER_FIRES_ALWAYS, false, lockmode);
    3987           0 :             break;
    3988             :         case AT_EnableReplicaTrig:  /* ENABLE REPLICA TRIGGER name */
    3989           0 :             ATExecEnableDisableTrigger(rel, cmd->name,
    3990             :                                        TRIGGER_FIRES_ON_REPLICA, false, lockmode);
    3991           0 :             break;
    3992             :         case AT_DisableTrig:    /* DISABLE TRIGGER name */
    3993           2 :             ATExecEnableDisableTrigger(rel, cmd->name,
    3994             :                                        TRIGGER_DISABLED, false, lockmode);
    3995           2 :             break;
    3996             :         case AT_EnableTrigAll:  /* ENABLE TRIGGER ALL */
    3997           0 :             ATExecEnableDisableTrigger(rel, NULL,
    3998             :                                        TRIGGER_FIRES_ON_ORIGIN, false, lockmode);
    3999           0 :             break;
    4000             :         case AT_DisableTrigAll: /* DISABLE TRIGGER ALL */
    4001           1 :             ATExecEnableDisableTrigger(rel, NULL,
    4002             :                                        TRIGGER_DISABLED, false, lockmode);
    4003           1 :             break;
    4004             :         case AT_EnableTrigUser: /* ENABLE TRIGGER USER */
    4005           0 :             ATExecEnableDisableTrigger(rel, NULL,
    4006             :                                        TRIGGER_FIRES_ON_ORIGIN, true, lockmode);
    4007           0 :             break;
    4008             :         case AT_DisableTrigUser:    /* DISABLE TRIGGER USER */
    4009           1 :             ATExecEnableDisableTrigger(rel, NULL,
    4010             :                                        TRIGGER_DISABLED, true, lockmode);
    4011           1 :             break;
    4012             : 
    4013             :         case AT_EnableRule:     /* ENABLE RULE name */
    4014           0 :             ATExecEnableDisableRule(rel, cmd->name,
    4015             :                                     RULE_FIRES_ON_ORIGIN, lockmode);
    4016           0 :             break;
    4017             :         case AT_EnableAlwaysRule:   /* ENABLE ALWAYS RULE name */
    4018           0 :             ATExecEnableDisableRule(rel, cmd->name,
    4019             :                                     RULE_FIRES_ALWAYS, lockmode);
    4020           0 :             break;
    4021             :         case AT_EnableReplicaRule:  /* ENABLE REPLICA RULE name */
    4022           0 :             ATExecEnableDisableRule(rel, cmd->name,
    4023             :                                     RULE_FIRES_ON_REPLICA, lockmode);
    4024           0 :             break;
    4025             :         case AT_DisableRule:    /* DISABLE RULE name */
    4026           0 :             ATExecEnableDisableRule(rel, cmd->name,
    4027             :                                     RULE_DISABLED, lockmode);
    4028           0 :             break;
    4029             : 
    4030             :         case AT_AddInherit:
    4031          22 :             address = ATExecAddInherit(rel, (RangeVar *) cmd->def, lockmode);
    4032          11 :             break;
    4033             :         case AT_DropInherit:
    4034           5 :             address = ATExecDropInherit(rel, (RangeVar *) cmd->def, lockmode);
    4035           4 :             break;
    4036             :         case AT_AddOf:
    4037           9 :             address = ATExecAddOf(rel, (TypeName *) cmd->def, lockmode);
    4038           3 :             break;
    4039             :         case AT_DropOf:
    4040           1 :             ATExecDropOf(rel, lockmode);
    4041           1 :             break;
    4042             :         case AT_ReplicaIdentity:
    4043          15 :             ATExecReplicaIdentity(rel, (ReplicaIdentityStmt *) cmd->def, lockmode);
    4044           8 :             break;
    4045             :         case AT_EnableRowSecurity:
    4046          37 :             ATExecEnableRowSecurity(rel);
    4047          37 :             break;
    4048             :         case AT_DisableRowSecurity:
    4049           2 :             ATExecDisableRowSecurity(rel);
    4050           2 :             break;
    4051             :         case AT_ForceRowSecurity:
    4052          10 :             ATExecForceNoForceRowSecurity(rel, true);
    4053          10 :             break;
    4054             :         case AT_NoForceRowSecurity:
    4055           4 :             ATExecForceNoForceRowSecurity(rel, false);
    4056           4 :             break;
    4057             :         case AT_GenericOptions:
    4058           1 :             ATExecGenericOptions(rel, (List *) cmd->def);
    4059           1 :             break;
    4060             :         case AT_AttachPartition:
    4061          55 :             ATExecAttachPartition(wqueue, rel, (PartitionCmd *) cmd->def);
    4062          33 :             break;
    4063             :         case AT_DetachPartition:
    4064          11 :             ATExecDetachPartition(rel, ((PartitionCmd *) cmd->def)->name);
    4065           8 :             break;
    4066             :         default:                /* oops */
    4067           0 :             elog(ERROR, "unrecognized alter table type: %d",
    4068             :                  (int) cmd->subtype);
    4069             :             break;
    4070             :     }
    4071             : 
    4072             :     /*
    4073             :      * Report the subcommand to interested event triggers.
    4074             :      */
    4075         918 :     EventTriggerCollectAlterTableSubcmd((Node *) cmd, address);
    4076             : 
    4077             :     /*
    4078             :      * Bump the command counter to ensure the next subcommand in the sequence
    4079             :      * can see the changes so far
    4080             :      */
    4081         918 :     CommandCounterIncrement();
    4082         918 : }
    4083             : 
    4084             : /*
    4085             :  * ATRewriteTables: ALTER TABLE phase 3
    4086             :  */
    4087             : static void
    4088         786 : ATRewriteTables(AlterTableStmt *parsetree, List **wqueue, LOCKMODE lockmode)
    4089             : {
    4090             :     ListCell   *ltab;
    4091             : 
    4092             :     /* Go through each table that needs to be checked or rewritten */
    4093        1715 :     foreach(ltab, *wqueue)
    4094             :     {
    4095         951 :         AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
    4096             : 
    4097             :         /* Foreign tables have no storage, nor do partitioned tables. */
    4098        1827 :         if (tab->relkind == RELKIND_FOREIGN_TABLE ||
    4099         876 :             tab->relkind == RELKIND_PARTITIONED_TABLE)
    4100         147 :             continue;
    4101             : 
    4102             :         /*
    4103             :          * If we change column data types or add/remove OIDs, the operation
    4104             :          * has to be propagated to tables that use this table's rowtype as a
    4105             :          * column type.  tab->newvals will also be non-NULL in the case where
    4106             :          * we're adding a column with a default.  We choose to forbid that
    4107             :          * case as well, since composite types might eventually support
    4108             :          * defaults.
    4109             :          *
    4110             :          * (Eventually we'll probably need to check for composite type
    4111             :          * dependencies even when we're just scanning the table without a
    4112             :          * rewrite, but at the moment a composite type does not enforce any
    4113             :          * constraints, so it's not necessary/appropriate to enforce them just
    4114             :          * during ALTER.)
    4115             :          */
    4116         804 :         if (tab->newvals != NIL || tab->rewrite > 0)
    4117             :         {
    4118             :             Relation    rel;
    4119             : 
    4120          87 :             rel = heap_open(tab->relid, NoLock);
    4121          87 :             find_composite_type_dependencies(rel->rd_rel->reltype, rel, NULL);
    4122          85 :             heap_close(rel, NoLock);
    4123             :         }
    4124             : 
    4125             :         /*
    4126             :          * We only need to rewrite the table if at least one column needs to
    4127             :          * be recomputed, we are adding/removing the OID column, or we are
    4128             :          * changing its persistence.
    4129             :          *
    4130             :          * There are two reasons for requiring a rewrite when changing
    4131             :          * persistence: on one hand, we need to ensure that the buffers
    4132             :          * belonging to each of the two relations are marked with or without
    4133             :          * BM_PERMANENT properly.  On the other hand, since rewriting creates
    4134             :          * and assigns a new relfilenode, we automatically create or drop an
    4135             :          * init fork for the relation as appropriate.
    4136             :          */
    4137         802 :         if (tab->rewrite > 0)
    4138             :         {
    4139             :             /* Build a temporary relation and copy data */
    4140             :             Relation    OldHeap;
    4141             :             Oid         OIDNewHeap;
    4142             :             Oid         NewTableSpace;
    4143             :             char        persistence;
    4144             : 
    4145          75 :             OldHeap = heap_open(tab->relid, NoLock);
    4146             : 
    4147             :             /*
    4148             :              * We don't support rewriting of system catalogs; there are too
    4149             :              * many corner cases and too little benefit.  In particular this
    4150             :              * is certainly not going to work for mapped catalogs.
    4151             :              */
    4152          75 :             if (IsSystemRelation(OldHeap))
    4153           0 :                 ereport(ERROR,
    4154             :                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    4155             :                          errmsg("cannot rewrite system relation \"%s\"",
    4156             :                                 RelationGetRelationName(OldHeap))));
    4157             : 
    4158          75 :             if (RelationIsUsedAsCatalogTable(OldHeap))
    4159           0 :                 ereport(ERROR,
    4160             :                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    4161             :                          errmsg("cannot rewrite table \"%s\" used as a catalog table",
    4162             :                                 RelationGetRelationName(OldHeap))));
    4163             : 
    4164             :             /*
    4165             :              * Don't allow rewrite on temp tables of other backends ... their
    4166             :              * local buffer manager is not going to cope.
    4167             :              */
    4168          75 :             if (RELATION_IS_OTHER_TEMP(OldHeap))
    4169           0 :                 ereport(ERROR,
    4170             :                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    4171             :                          errmsg("cannot rewrite temporary tables of other sessions")));
    4172             : 
    4173             :             /*
    4174             :              * Select destination tablespace (same as original unless user
    4175             :              * requested a change)
    4176             :              */
    4177          75 :             if (tab->newTableSpace)
    4178           0 :                 NewTableSpace = tab->newTableSpace;
    4179             :             else
    4180          75 :                 NewTableSpace = OldHeap->rd_rel->reltablespace;
    4181             : 
    4182             :             /*
    4183             :              * Select persistence of transient table (same as original unless
    4184             :              * user requested a change)
    4185             :              */
    4186         145 :             persistence = tab->chgPersistence ?
    4187          70 :                 tab->newrelpersistence : OldHeap->rd_rel->relpersistence;
    4188             : 
    4189          75 :             heap_close(OldHeap, NoLock);
    4190             : 
    4191             :             /*
    4192             :              * Fire off an Event Trigger now, before actually rewriting the
    4193             :              * table.
    4194             :              *
    4195             :              * We don't support Event Trigger for nested commands anywhere,
    4196             :              * here included, and parsetree is given NULL when coming from
    4197             :              * AlterTableInternal.
    4198             :              *
    4199             :              * And fire it only once.
    4200             :              */
    4201          75 :             if (parsetree)
    4202          75 :                 EventTriggerTableRewrite((Node *) parsetree,
    4203             :                                          tab->relid,
    4204             :                                          tab->rewrite);
    4205             : 
    4206             :             /*
    4207             :              * Create transient table that will receive the modified data.
    4208             :              *
    4209             :              * Ensure it is marked correctly as logged or unlogged.  We have
    4210             :              * to do this here so that buffers for the new relfilenode will
    4211             :              * have the right persistence set, and at the same time ensure
    4212             :              * that the original filenode's buffers will get read in with the
    4213             :              * correct setting (i.e. the original one).  Otherwise a rollback
    4214             :              * after the rewrite would possibly result with buffers for the
    4215             :              * original filenode having the wrong persistence setting.
    4216             :              *
    4217             :              * NB: This relies on swap_relation_files() also swapping the
    4218             :              * persistence. That wouldn't work for pg_class, but that can't be
    4219             :              * unlogged anyway.
    4220             :              */
    4221          73 :             OIDNewHeap = make_new_heap(tab->relid, NewTableSpace, persistence,
    4222             :                                        lockmode);
    4223             : 
    4224             :             /*
    4225             :              * Copy the heap data into the new table with the desired
    4226             :              * modifications, and test the current data within the table
    4227             :              * against new constraints generated by ALTER TABLE commands.
    4228             :              */
    4229          73 :             ATRewriteTable(tab, OIDNewHeap, lockmode);
    4230             : 
    4231             :             /*
    4232             :              * Swap the physical files of the old and new heaps, then rebuild
    4233             :              * indexes and discard the old heap.  We can use RecentXmin for
    4234             :              * the table's new relfrozenxid because we rewrote all the tuples
    4235             :              * in ATRewriteTable, so no older Xid remains in the table.  Also,
    4236             :              * we never try to swap toast tables by content, since we have no
    4237             :              * interest in letting this code work on system catalogs.
    4238             :              */
    4239         142 :             finish_heap_swap(tab->relid, OIDNewHeap,
    4240             :                              false, false, true,
    4241          71 :                              !OidIsValid(tab->newTableSpace),
    4242             :                              RecentXmin,
    4243             :                              ReadNextMultiXactId(),
    4244             :                              persistence);
    4245             :         }
    4246             :         else
    4247             :         {
    4248             :             /*
    4249             :              * Test the current data within the table against new constraints
    4250             :              * generated by ALTER TABLE commands, but don't rebuild data.
    4251             :              */
    4252        1328 :             if (tab->constraints != NIL || tab->new_notnull ||
    4253         601 :                 tab->partition_constraint != NULL)
    4254         149 :                 ATRewriteTable(tab, InvalidOid, lockmode);
    4255             : 
    4256             :             /*
    4257             :              * If we had SET TABLESPACE but no reason to reconstruct tuples,
    4258             :              * just do a block-by-block copy.
    4259             :              */
    4260         712 :             if (tab->newTableSpace)
    4261           9 :                 ATExecSetTableSpace(tab->relid, tab->newTableSpace, lockmode);
    4262             :         }
    4263             :     }
    4264             : 
    4265             :     /*
    4266             :      * Foreign key constraints are checked in a final pass, since (a) it's
    4267             :      * generally best to examine each one separately, and (b) it's at least
    4268             :      * theoretically possible that we have changed both relations of the
    4269             :      * foreign key, and we'd better have finished both rewrites before we try
    4270             :      * to read the tables.
    4271             :      */
    4272        1684 :     foreach(ltab, *wqueue)
    4273             :     {
    4274         922 :         AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
    4275         922 :         Relation    rel = NULL;
    4276             :         ListCell   *lcon;
    4277             : 
    4278        1029 :         foreach(lcon, tab->constraints)
    4279             :         {
    4280         109 :             NewConstraint *con = lfirst(lcon);
    4281             : 
    4282         109 :             if (con->contype == CONSTR_FOREIGN)
    4283             :             {
    4284          23 :                 Constraint *fkconstraint = (Constraint *) con->qual;
    4285             :                 Relation    refrel;
    4286             : 
    4287          23 :                 if (rel == NULL)
    4288             :                 {
    4289             :                     /* Long since locked, no need for another */
    4290          23 :                     rel = heap_open(tab->relid, NoLock);
    4291             :                 }
    4292             : 
    4293          23 :                 refrel = heap_open(con->refrelid, RowShareLock);
    4294             : 
    4295          23 :                 validateForeignKeyConstraint(fkconstraint->conname, rel, refrel,
    4296             :                                              con->refindid,
    4297             :                                              con->conid);
    4298             : 
    4299             :                 /*
    4300             :                  * No need to mark the constraint row as validated, we did
    4301             :                  * that when we inserted the row earlier.
    4302             :                  */
    4303             : 
    4304          21 :                 heap_close(refrel, NoLock);
    4305             :             }
    4306             :         }
    4307             : 
    4308         920 :         if (rel)
    4309          21 :             heap_close(rel, NoLock);
    4310             :     }
    4311         762 : }
    4312             : 
    4313             : /*
    4314             :  * ATRewriteTable: scan or rewrite one table
    4315             :  *
    4316             :  * OIDNewHeap is InvalidOid if we don't need to rewrite
    4317             :  */
    4318             : static void
    4319         222 : ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
    4320             : {
    4321             :     Relation    oldrel;
    4322             :     Relation    newrel;
    4323             :     TupleDesc   oldTupDesc;
    4324             :     TupleDesc   newTupDesc;
    4325         222 :     bool        needscan = false;
    4326             :     List       *notnull_attrs;
    4327             :     int         i;
    4328             :     ListCell   *l;
    4329             :     EState     *estate;
    4330             :     CommandId   mycid;
    4331             :     BulkInsertState bistate;
    4332             :     int         hi_options;
    4333         222 :     ExprState  *partqualstate = NULL;
    4334             : 
    4335             :     /*
    4336             :      * Open the relation(s).  We have surely already locked the existing
    4337             :      * table.
    4338             :      */
    4339         222 :     oldrel = heap_open(tab->relid, NoLock);
    4340         222 :     oldTupDesc = tab->oldDesc;
    4341         222 :     newTupDesc = RelationGetDescr(oldrel);  /* includes all mods */
    4342             : 
    4343         222 :     if (OidIsValid(OIDNewHeap))
    4344          73 :         newrel = heap_open(OIDNewHeap, lockmode);
    4345             :     else
    4346         149 :         newrel = NULL;
    4347             : 
    4348             :     /*
    4349             :      * Prepare a BulkInsertState and options for heap_insert. Because we're
    4350             :      * building a new heap, we can skip WAL-logging and fsync it to disk at
    4351             :      * the end instead (unless WAL-logging is required for archiving or
    4352             :      * streaming replication). The FSM is empty too, so don't bother using it.
    4353             :      */
    4354         222 :     if (newrel)
    4355             :     {
    4356          73 :         mycid = GetCurrentCommandId(true);
    4357          73 :         bistate = GetBulkInsertState();
    4358             : 
    4359          73 :         hi_options = HEAP_INSERT_SKIP_FSM;
    4360          73 :         if (!XLogIsNeeded())
    4361           0 :             hi_options |= HEAP_INSERT_SKIP_WAL;
    4362             :     }
    4363             :     else
    4364             :     {
    4365             :         /* keep compiler quiet about using these uninitialized */
    4366         149 :         mycid = 0;
    4367         149 :         bistate = NULL;
    4368         149 :         hi_options = 0;
    4369             :     }
    4370             : 
    4371             :     /*
    4372             :      * Generate the constraint and default execution states
    4373             :      */
    4374             : 
    4375         222 :     estate = CreateExecutorState();
    4376             : 
    4377             :     /* Build the needed expression execution states */
    4378         320 :     foreach(l, tab->constraints)
    4379             :     {
    4380          98 :         NewConstraint *con = lfirst(l);
    4381             : 
    4382          98 :         switch (con->contype)
    4383             :         {
    4384             :             case CONSTR_CHECK:
    4385          75 :                 needscan = true;
    4386          75 :                 con->qualstate = ExecPrepareExpr((Expr *) con->qual, estate);
    4387          75 :                 break;
    4388             :             case CONSTR_FOREIGN:
    4389             :                 /* Nothing to do here */
    4390          23 :                 break;
    4391             :             default:
    4392           0 :                 elog(ERROR, "unrecognized constraint type: %d",
    4393             :                      (int) con->contype);
    4394             :         }
    4395             :     }
    4396             : 
    4397             :     /* Build expression execution states for partition check quals */
    4398         222 :     if (tab->partition_constraint)
    4399             :     {
    4400          23 :         needscan = true;
    4401          23 :         partqualstate = ExecPrepareExpr(tab->partition_constraint, estate);
    4402             :     }
    4403             : 
    4404         277 :     foreach(l, tab->newvals)
    4405             :     {
    4406          55 :         NewColumnValue *ex = lfirst(l);
    4407             : 
    4408             :         /* expr already planned */
    4409          55 :         ex->exprstate = ExecInitExpr((Expr *) ex->expr, NULL);
    4410             :     }
    4411             : 
    4412         222 :     notnull_attrs = NIL;
    4413         222 :     if (newrel || tab->new_notnull)
    4414             :     {
    4415             :         /*
    4416             :          * If we are rebuilding the tuples OR if we added any new NOT NULL
    4417             :          * constraints, check all not-null constraints.  This is a bit of
    4418             :          * overkill but it minimizes risk of bugs, and heap_attisnull is a
    4419             :          * pretty cheap test anyway.
    4420             :          */
    4421         420 :         for (i = 0; i < newTupDesc->natts; i++)
    4422             :         {
    4423         309 :             Form_pg_attribute attr = TupleDescAttr(newTupDesc, i);
    4424             : 
    4425         309 :             if (attr->attnotnull && !attr->attisdropped)
    4426          87 :                 notnull_attrs = lappend_int(notnull_attrs, i);
    4427             :         }
    4428         111 :         if (notnull_attrs)
    4429          69 :             needscan = true;
    4430             :     }
    4431             : 
    4432         222 :     if (newrel || needscan)
    4433             :     {
    4434             :         ExprContext *econtext;
    4435             :         Datum      *values;
    4436             :         bool       *isnull;
    4437             :         TupleTableSlot *oldslot;
    4438             :         TupleTableSlot *newslot;
    4439             :         HeapScanDesc scan;
    4440             :         HeapTuple   tuple;
    4441             :         MemoryContext oldCxt;
    4442         200 :         List       *dropped_attrs = NIL;
    4443             :         ListCell   *lc;
    4444             :         Snapshot    snapshot;
    4445             : 
    4446         200 :         if (newrel)
    4447          73 :             ereport(DEBUG1,
    4448             :                     (errmsg("rewriting table \"%s\"",
    4449             :                             RelationGetRelationName(oldrel))));
    4450             :         else
    4451         127 :             ereport(DEBUG1,
    4452             :                     (errmsg("verifying table \"%s\"",
    4453             :                             RelationGetRelationName(oldrel))));
    4454             : 
    4455         200 :         if (newrel)
    4456             :         {
    4457             :             /*
    4458             :              * All predicate locks on the tuples or pages are about to be made
    4459             :              * invalid, because we move tuples around.  Promote them to
    4460             :              * relation locks.
    4461             :              */
    4462          73 :             TransferPredicateLocksToHeapRelation(oldrel);
    4463             :         }
    4464             : 
    4465         200 :         econtext = GetPerTupleExprContext(estate);
    4466             : 
    4467             :         /*
    4468             :          * Make tuple slots for old and new tuples.  Note that even when the
    4469             :          * tuples are the same, the tupDescs might not be (consider ADD COLUMN
    4470             :          * without a default).
    4471             :          */
    4472         200 :         oldslot = MakeSingleTupleTableSlot(oldTupDesc);
    4473         200 :         newslot = MakeSingleTupleTableSlot(newTupDesc);
    4474             : 
    4475             :         /* Preallocate values/isnull arrays */
    4476         200 :         i = Max(newTupDesc->natts, oldTupDesc->natts);
    4477         200 :         values = (Datum *) palloc(i * sizeof(Datum));
    4478         200 :         isnull = (bool *) palloc(i * sizeof(bool));
    4479         200 :         memset(values, 0, i * sizeof(Datum));
    4480         200 :         memset(isnull, true, i * sizeof(bool));
    4481             : 
    4482             :         /*
    4483             :          * Any attributes that are dropped according to the new tuple
    4484             :          * descriptor can be set to NULL. We precompute the list of dropped
    4485             :          * attributes to avoid needing to do so in the per-tuple loop.
    4486             :          */
    4487         758 :         for (i = 0; i < newTupDesc->natts; i++)
    4488             :         {
    4489         558 :             if (TupleDescAttr(newTupDesc, i)->attisdropped)
    4490          65 :                 dropped_attrs = lappend_int(dropped_attrs, i);
    4491             :         }
    4492             : 
    4493             :         /*
    4494             :          * Scan through the rows, generating a new row if needed and then
    4495             :          * checking all the constraints.
    4496             :          */
    4497         200 :         snapshot = RegisterSnapshot(GetLatestSnapshot());
    4498         200 :         scan = heap_beginscan(oldrel, snapshot, 0, NULL);
    4499             : 
    4500             :         /*
    4501             :          * Switch to per-tuple memory context and reset it for each tuple
    4502             :          * produced, so we don't leak memory.
    4503             :          */
    4504         200 :         oldCxt = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
    4505             : 
    4506        7000 :         while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
    4507             :         {
    4508        6617 :             if (tab->rewrite > 0)
    4509             :             {
    4510        5553 :                 Oid         tupOid = InvalidOid;
    4511             : 
    4512             :                 /* Extract data from old tuple */
    4513        5553 :                 heap_deform_tuple(tuple, oldTupDesc, values, isnull);
    4514        5553 :                 if (oldTupDesc->tdhasoid)
    4515           7 :                     tupOid = HeapTupleGetOid(tuple);
    4516             : 
    4517             :                 /* Set dropped attributes to null in new tuple */
    4518        5568 :                 foreach(lc, dropped_attrs)
    4519          15 :                     isnull[lfirst_int(lc)] = true;
    4520             : 
    4521             :                 /*
    4522             :                  * Process supplied expressions to replace selected columns.
    4523             :                  * Expression inputs come from the old tuple.
    4524             :                  */
    4525        5553 :                 ExecStoreTuple(tuple, oldslot, InvalidBuffer, false);
    4526        5553 :                 econtext->ecxt_scantuple = oldslot;
    4527             : 
    4528       12098 :                 foreach(l, tab->newvals)
    4529             :                 {
    4530        6547 :                     NewColumnValue *ex = lfirst(l);
    4531             : 
    4532       13094 :                     values[ex->attnum - 1] = ExecEvalExpr(ex->exprstate,
    4533             :                                                           econtext,
    4534        6547 :                                                           &isnull[ex->attnum - 1]);
    4535             :                 }
    4536             : 
    4537             :                 /*
    4538             :                  * Form the new tuple. Note that we don't explicitly pfree it,
    4539             :                  * since the per-tuple memory context will be reset shortly.
    4540             :                  */
    4541        5551 :                 tuple = heap_form_tuple(newTupDesc, values, isnull);
    4542             : 
    4543             :                 /* Preserve OID, if any */
    4544        5551 :                 if (newTupDesc->tdhasoid)
    4545           6 :                     HeapTupleSetOid(tuple, tupOid);
    4546             : 
    4547             :                 /*
    4548             :                  * Constraints might reference the tableoid column, so
    4549             :                  * initialize t_tableOid before evaluating them.
    4550             :                  */
    4551        5551 :                 tuple->t_tableOid = RelationGetRelid(oldrel);
    4552             :             }
    4553             : 
    4554             :             /* Now check any constraints on the possibly-changed tuple */
    4555        6615 :             ExecStoreTuple(tuple, newslot, InvalidBuffer, false);
    4556        6615 :             econtext->ecxt_scantuple = newslot;
    4557             : 
    4558        7155 :             foreach(l, notnull_attrs)
    4559             :             {
    4560         545 :                 int         attn = lfirst_int(l);
    4561             : 
    4562         545 :                 if (heap_attisnull(tuple, attn + 1))
    4563             :                 {
    4564           5 :                     Form_pg_attribute attr = TupleDescAttr(newTupDesc, attn);
    4565             : 
    4566           5 :                     ereport(ERROR,
    4567             :                             (errcode(ERRCODE_NOT_NULL_VIOLATION),
    4568             :                              errmsg("column \"%s\" contains null values",
    4569             :                                     NameStr(attr->attname)),
    4570             :                              errtablecol(oldrel, attn + 1)));
    4571             :                 }
    4572             :             }
    4573             : 
    4574        7658 :             foreach(l, tab->constraints)
    4575             :             {
    4576        1053 :                 NewConstraint *con = lfirst(l);
    4577             : 
    4578        1053 :                 switch (con->contype)
    4579             :                 {
    4580             :                     case CONSTR_CHECK:
    4581        1048 :                         if (!ExecCheck(con->qualstate, econtext))
    4582           5 :                             ereport(ERROR,
    4583             :                                     (errcode(ERRCODE_CHECK_VIOLATION),
    4584             :                                      errmsg("check constraint \"%s\" is violated by some row",
    4585             :                                             con->name),
    4586             :                                      errtableconstraint(oldrel, con->name)));
    4587        1043 :                         break;
    4588             :                     case CONSTR_FOREIGN:
    4589             :                         /* Nothing to do here */
    4590           5 :                         break;
    4591             :                     default:
    4592           0 :                         elog(ERROR, "unrecognized constraint type: %d",
    4593             :                              (int) con->contype);
    4594             :                 }
    4595             :             }
    4596             : 
    4597        6605 :             if (partqualstate && !ExecCheck(partqualstate, econtext))
    4598           5 :                 ereport(ERROR,
    4599             :                         (errcode(ERRCODE_CHECK_VIOLATION),
    4600             :                          errmsg("partition constraint is violated by some row")));
    4601             : 
    4602             :             /* Write the tuple out to the new relation */
    4603        6600 :             if (newrel)
    4604        5551 :                 heap_insert(newrel, tuple, mycid, hi_options, bistate);
    4605             : 
    4606        6600 :             ResetExprContext(econtext);
    4607             : 
    4608        6600 :             CHECK_FOR_INTERRUPTS();
    4609             :         }
    4610             : 
    4611         183 :         MemoryContextSwitchTo(oldCxt);
    4612         183 :         heap_endscan(scan);
    4613         183 :         UnregisterSnapshot(snapshot);
    4614             : 
    4615         183 :         ExecDropSingleTupleTableSlot(oldslot);
    4616         183 :         ExecDropSingleTupleTableSlot(newslot);
    4617             :     }
    4618             : 
    4619         205 :     FreeExecutorState(estate);
    4620             : 
    4621         205 :     heap_close(oldrel, NoLock);
    4622         205 :     if (newrel)
    4623             :     {
    4624          71 :         FreeBulkInsertState(bistate);
    4625             : 
    4626             :         /* If we skipped writing WAL, then we need to sync the heap. */
    4627          71 :         if (hi_options & HEAP_INSERT_SKIP_WAL)
    4628           0 :             heap_sync(newrel);
    4629             : 
    4630          71 :         heap_close(newrel, NoLock);
    4631             :     }
    4632         205 : }
    4633             : 
    4634             : /*
    4635             :  * ATGetQueueEntry: find or create an entry in the ALTER TABLE work queue
    4636             :  */
    4637             : static AlteredTableInfo *
    4638        1289 : ATGetQueueEntry(List **wqueue, Relation rel)
    4639             : {
    4640        1289 :     Oid         relid = RelationGetRelid(rel);
    4641             :     AlteredTableInfo *tab;
    4642             :     ListCell   *ltab;
    4643             : 
    4644        1626 :     foreach(ltab, *wqueue)
    4645             :     {
    4646         430 :         tab = (AlteredTableInfo *) lfirst(ltab);
    4647         430 :         if (tab->relid == relid)
    4648          93 :             return tab;
    4649             :     }
    4650             : 
    4651             :     /*
    4652             :      * Not there, so add it.  Note that we make a copy of the relation's
    4653             :      * existing descriptor before anything interesting can happen to it.
    4654             :      */
    4655        1196 :     tab = (AlteredTableInfo *) palloc0(sizeof(AlteredTableInfo));
    4656        1196 :     tab->relid = relid;
    4657        1196 :     tab->relkind = rel->rd_rel->relkind;
    4658        1196 :     tab->oldDesc = CreateTupleDescCopy(RelationGetDescr(rel));
    4659        1196 :     tab->newrelpersistence = RELPERSISTENCE_PERMANENT;
    4660        1196 :     tab->chgPersistence = false;
    4661             : 
    4662        1196 :     *wqueue = lappend(*wqueue, tab);
    4663             : 
    4664        1196 :     return tab;
    4665             : }
    4666             : 
    4667             : /*
    4668             :  * ATSimplePermissions
    4669             :  *
    4670             :  * - Ensure that it is a relation (or possibly a view)
    4671             :  * - Ensure this user is the owner
    4672             :  * - Ensure that it is not a system table
    4673             :  */
    4674             : static void
    4675        1298 : ATSimplePermissions(Relation rel, int allowed_targets)
    4676             : {
    4677             :     int         actual_target;
    4678             : 
    4679        1298 :     switch (rel->rd_rel->relkind)
    4680             :     {
    4681             :         case RELKIND_RELATION:
    4682             :         case RELKIND_PARTITIONED_TABLE:
    4683        1127 :             actual_target = ATT_TABLE;
    4684        1127 :             break;
    4685             :         case RELKIND_VIEW:
    4686          44 :             actual_target = ATT_VIEW;
    4687          44 :             break;
    4688             :         case RELKIND_MATVIEW:
    4689           0 :             actual_target = ATT_MATVIEW;
    4690           0 :             break;
    4691             :         case RELKIND_INDEX:
    4692           8 :             actual_target = ATT_INDEX;
    4693           8 :             break;
    4694             :         case RELKIND_COMPOSITE_TYPE:
    4695          25 :             actual_target = ATT_COMPOSITE_TYPE;
    4696          25 :             break;
    4697             :         case RELKIND_FOREIGN_TABLE:
    4698          94 :             actual_target = ATT_FOREIGN_TABLE;
    4699          94 :             break;
    4700             :         default:
    4701           0 :             actual_target = 0;
    4702           0 :             break;
    4703             :     }
    4704             : 
    4705             :     /* Wrong target type? */
    4706        1298 :     if ((actual_target & allowed_targets) == 0)
    4707           4 :         ATWrongRelkindError(rel, allowed_targets);
    4708             : 
    4709             :     /* Permissions checks */
    4710        1294 :     if (!pg_class_ownercheck(RelationGetRelid(rel), GetUserId()))
    4711           1 :         aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
    4712           1 :                        RelationGetRelationName(rel));
    4713             : 
    4714        1293 :     if (!allowSystemTableMods && IsSystemRelation(rel))
    4715           0 :         ereport(ERROR,
    4716             :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
    4717             :                  errmsg("permission denied: \"%s\" is a system catalog",
    4718             :                         RelationGetRelationName(rel))));
    4719        1293 : }
    4720             : 
    4721             : /*
    4722             :  * ATWrongRelkindError
    4723             :  *
    4724             :  * Throw an error when a relation has been determined to be of the wrong
    4725             :  * type.
    4726             :  */
    4727             : static void
    4728           4 : ATWrongRelkindError(Relation rel, int allowed_targets)
    4729             : {
    4730             :     char       *msg;
    4731             : 
    4732           4 :     switch (allowed_targets)
    4733             :     {
    4734             :         case ATT_TABLE:
    4735           1 :             msg = _("\"%s\" is not a table");
    4736           1 :             break;
    4737             :         case ATT_TABLE | ATT_VIEW:
    4738           0 :             msg = _("\"%s\" is not a table or view");
    4739           0 :             break;
    4740             :         case ATT_TABLE | ATT_VIEW | ATT_FOREIGN_TABLE:
    4741           0 :             msg = _("\"%s\" is not a table, view, or foreign table");
    4742           0 :             break;
    4743             :         case ATT_TABLE | ATT_VIEW | ATT_MATVIEW | ATT_INDEX:
    4744           0 :             msg = _("\"%s\" is not a table, view, materialized view, or index");
    4745           0 :             break;
    4746             :         case ATT_TABLE | ATT_MATVIEW:
    4747           0 :             msg = _("\"%s\" is not a table or materialized view");
    4748           0 :             break;
    4749             :         case ATT_TABLE | ATT_MATVIEW | ATT_INDEX:
    4750           0 :             msg = _("\"%s\" is not a table, materialized view, or index");
    4751           0 :             break;
    4752             :         case ATT_TABLE | ATT_MATVIEW | ATT_FOREIGN_TABLE:
    4753           0 :             msg = _("\"%s\" is not a table, materialized view, or foreign table");
    4754           0 :             break;
    4755             :         case ATT_TABLE | ATT_FOREIGN_TABLE:
    4756           2 :             msg = _("\"%s\" is not a table or foreign table");
    4757           2 :             break;
    4758             :         case ATT_TABLE | ATT_COMPOSITE_TYPE | ATT_FOREIGN_TABLE:
    4759           1 :             msg = _("\"%s\" is not a table, composite type, or foreign table");
    4760           1 :             break;
    4761             :         case ATT_TABLE | ATT_MATVIEW | ATT_INDEX | ATT_FOREIGN_TABLE:
    4762           0 :             msg = _("\"%s\" is not a table, materialized view, index, or foreign table");
    4763           0 :             break;
    4764             :         case ATT_VIEW:
    4765           0 :             msg = _("\"%s\" is not a view");
    4766           0 :             break;
    4767             :         case ATT_FOREIGN_TABLE:
    4768           0 :             msg = _("\"%s\" is not a foreign table");
    4769           0 :             break;
    4770             :         default:
    4771             :             /* shouldn't get here, add all necessary cases above */
    4772           0 :             msg = _("\"%s\" is of the wrong type");
    4773           0 :             break;
    4774             :     }
    4775             : 
    4776           4 :     ereport(ERROR,
    4777             :             (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    4778             :              errmsg(msg, RelationGetRelationName(rel))));
    4779             : }
    4780             : 
    4781             : /*
    4782             :  * ATSimpleRecursion
    4783             :  *
    4784             :  * Simple table recursion sufficient for most ALTER TABLE operations.
    4785             :  * All direct and indirect children are processed in an unspecified order.
    4786             :  * Note that if a child inherits from the original table via multiple
    4787             :  * inheritance paths, it will be visited just once.
    4788             :  */
    4789             : static void
    4790         139 : ATSimpleRecursion(List **wqueue, Relation rel,
    4791             :                   AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode)
    4792             : {
    4793             :     /*
    4794             :      * Propagate to children if desired.  Only plain tables and foreign tables
    4795             :      * have children, so no need to search for other relkinds.
    4796             :      */
    4797         241 :     if (recurse &&
    4798         120 :         (rel->rd_rel->relkind == RELKIND_RELATION ||
    4799          26 :          rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE ||
    4800           8 :          rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE))
    4801             :     {
    4802          98 :         Oid         relid = RelationGetRelid(rel);
    4803             :         ListCell   *child;
    4804             :         List       *children;
    4805             : 
    4806          98 :         children = find_all_inheritors(relid, lockmode, NULL);
    4807             : 
    4808             :         /*
    4809             :          * find_all_inheritors does the recursive search of the inheritance
    4810             :          * hierarchy, so all we have to do is process all of the relids in the
    4811             :          * list that it returns.
    4812             :          */
    4813         229 :         foreach(child, children)
    4814             :         {
    4815         131 :             Oid         childrelid = lfirst_oid(child);
    4816             :             Relation    childrel;
    4817             : 
    4818         131 :             if (childrelid == relid)
    4819          98 :                 continue;
    4820             :             /* find_all_inheritors already got lock */
    4821          33 :             childrel = relation_open(childrelid, NoLock);
    4822          33 :             CheckTableNotInUse(childrel, "ALTER TABLE");
    4823          33 :             ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode);
    4824          33 :             relation_close(childrel, NoLock);
    4825             :         }
    4826             :     }
    4827         139 : }
    4828             : 
    4829             : /*
    4830             :  * ATTypedTableRecursion
    4831             :  *
    4832             :  * Propagate ALTER TYPE operations to the typed tables of that type.
    4833             :  * Also check the RESTRICT/CASCADE behavior.  Given CASCADE, also permit
    4834             :  * recursion to inheritance children of the typed tables.
    4835             :  */
    4836             : static void
    4837          22 : ATTypedTableRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd,
    4838             :                       LOCKMODE lockmode)
    4839             : {
    4840             :     ListCell   *child;
    4841             :     List       *children;
    4842             : 
    4843          22 :     Assert(rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE);
    4844             : 
    4845          44 :     children = find_typed_table_dependencies(rel->rd_rel->reltype,
    4846          22 :                                              RelationGetRelationName(rel),
    4847             :                                              cmd->behavior);
    4848             : 
    4849          24 :     foreach(child, children)
    4850             :     {
    4851           5 :         Oid         childrelid = lfirst_oid(child);
    4852             :         Relation    childrel;
    4853             : 
    4854           5 :         childrel = relation_open(childrelid, lockmode);
    4855           5 :         CheckTableNotInUse(childrel, "ALTER TABLE");
    4856           5 :         ATPrepCmd(wqueue, childrel, cmd, true, true, lockmode);
    4857           5 :         relation_close(childrel, NoLock);
    4858             :     }
    4859          19 : }
    4860             : 
    4861             : 
    4862             : /*
    4863             :  * find_composite_type_dependencies
    4864             :  *
    4865             :  * Check to see if the type "typeOid" is being used as a column in some table
    4866             :  * (possibly nested several levels deep in composite types, arrays, etc!).
    4867             :  * Eventually, we'd like to propagate the check or rewrite operation
    4868             :  * into such tables, but for now, just error out if we find any.
    4869             :  *
    4870             :  * Caller should provide either the associated relation of a rowtype,
    4871             :  * or a type name (not both) for use in the error message, if any.
    4872             :  *
    4873             :  * Note that "typeOid" is not necessarily a composite type; it could also be
    4874             :  * another container type such as an array or range, or a domain over one of
    4875             :  * these things.  The name of this function is therefore somewhat historical,
    4876             :  * but it's not worth changing.
    4877             :  *
    4878             :  * We assume that functions and views depending on the type are not reasons
    4879             :  * to reject the ALTER.  (How safe is this really?)
    4880             :  */
    4881             : void
    4882         242 : find_composite_type_dependencies(Oid typeOid, Relation origRelation,
    4883             :                                  const char *origTypeName)
    4884             : {
    4885             :     Relation    depRel;
    4886             :     ScanKeyData key[2];
    4887             :     SysScanDesc depScan;
    4888             :     HeapTuple   depTup;
    4889             : 
    4890             :     /* since this function recurses, it could be driven to stack overflow */
    4891         242 :     check_stack_depth();
    4892             : 
    4893             :     /*
    4894             :      * We scan pg_depend to find those things that depend on the given type.
    4895             :      * (We assume we can ignore refobjsubid for a type.)
    4896             :      */
    4897         242 :     depRel = heap_open(DependRelationId, AccessShareLock);
    4898             : 
    4899         242 :     ScanKeyInit(&key[0],
    4900             :                 Anum_pg_depend_refclassid,
    4901             :                 BTEqualStrategyNumber, F_OIDEQ,
    4902             :                 ObjectIdGetDatum(TypeRelationId));
    4903         242 :     ScanKeyInit(&key[1],
    4904             :                 Anum_pg_depend_refobjid,
    4905             :                 BTEqualStrategyNumber, F_OIDEQ,
    4906             :                 ObjectIdGetDatum(typeOid));
    4907             : 
    4908         242 :     depScan = systable_beginscan(depRel, DependReferenceIndexId, true,
    4909             :                                  NULL, 2, key);
    4910             : 
    4911         615 :     while (HeapTupleIsValid(depTup = systable_getnext(depScan)))
    4912             :     {
    4913         143 :         Form_pg_depend pg_depend = (Form_pg_depend) GETSTRUCT(depTup);
    4914             :         Relation    rel;
    4915             :         Form_pg_attribute att;
    4916             : 
    4917             :         /* Check for directly dependent types */
    4918         143 :         if (pg_depend->classid == TypeRelationId)
    4919             :         {
    4920             :             /*
    4921             :              * This must be an array, domain, or range containing the given
    4922             :              * type, so recursively check for uses of this type.  Note that
    4923             :              * any error message will mention the original type not the
    4924             :              * container; this is intentional.
    4925             :              */
    4926         118 :             find_composite_type_dependencies(pg_depend->objid,
    4927             :                                              origRelation, origTypeName);
    4928         115 :             continue;
    4929             :         }
    4930             : 
    4931             :         /* Else, ignore dependees that aren't user columns of relations */
    4932             :         /* (we assume system columns are never of interesting types) */
    4933          47 :         if (pg_depend->classid != RelationRelationId ||
    4934          22 :             pg_depend->objsubid <= 0)
    4935          16 :             continue;
    4936             : 
    4937           9 :         rel = relation_open(pg_depend->objid, AccessShareLock);
    4938           9 :         att = TupleDescAttr(rel->rd_att, pg_depend->objsubid - 1);
    4939             : 
    4940           9 :         if (rel->rd_rel->relkind == RELKIND_RELATION ||
    4941           0 :             rel->rd_rel->relkind == RELKIND_MATVIEW ||
    4942           0 :             rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
    4943             :         {
    4944           9 :             if (origTypeName)
    4945           4 :                 ereport(ERROR,
    4946             :                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    4947             :                          errmsg("cannot alter type \"%s\" because column \"%s.%s\" uses it",
    4948             :                                 origTypeName,
    4949             :                                 RelationGetRelationName(rel),
    4950             :                                 NameStr(att->attname))));
    4951           5 :             else if (origRelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
    4952           2 :                 ereport(ERROR,
    4953             :                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    4954             :                          errmsg("cannot alter type \"%s\" because column \"%s.%s\" uses it",
    4955             :                                 RelationGetRelationName(origRelation),
    4956             :                                 RelationGetRelationName(rel),
    4957             :                                 NameStr(att->attname))));
    4958           3 :             else if (origRelation->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
    4959           1 :                 ereport(ERROR,
    4960             :                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    4961             :                          errmsg("cannot alter foreign table \"%s\" because column \"%s.%s\" uses its row type",
    4962             :                                 RelationGetRelationName(origRelation),
    4963             :                                 RelationGetRelationName(rel),
    4964             :                                 NameStr(att->attname))));
    4965             :             else
    4966           2 :                 ereport(ERROR,
    4967             :                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    4968             :                          errmsg("cannot alter table \"%s\" because column \"%s.%s\" uses its row type",
    4969             :                                 RelationGetRelationName(origRelation),
    4970             :                                 RelationGetRelationName(rel),
    4971             :                                 NameStr(att->attname))));
    4972             :         }
    4973           0 :         else if (OidIsValid(rel->rd_rel->reltype))
    4974             :         {
    4975             :             /*
    4976             :              * A view or composite type itself isn't a problem, but we must
    4977             :              * recursively check for indirect dependencies via its rowtype.
    4978             :              */
    4979           0 :             find_composite_type_dependencies(rel->rd_rel->reltype,
    4980             :                                              origRelation, origTypeName);
    4981             :         }
    4982             : 
    4983           0 :         relation_close(rel, AccessShareLock);
    4984             :     }
    4985             : 
    4986         230 :     systable_endscan(depScan);
    4987             : 
    4988         230 :     relation_close(depRel, AccessShareLock);
    4989         230 : }
    4990             : 
    4991             : 
    4992             : /*
    4993             :  * find_typed_table_dependencies
    4994             :  *
    4995             :  * Check to see if a composite type is being used as the type of a
    4996             :  * typed table.  Abort if any are found and behavior is RESTRICT.
    4997             :  * Else return the list of tables.
    4998             :  */
    4999             : static List *
    5000          26 : find_typed_table_dependencies(Oid typeOid, const char *typeName, DropBehavior behavior)
    5001             : {
    5002             :     Relation    classRel;
    5003             :     ScanKeyData key[1];
    5004             :     HeapScanDesc scan;
    5005             :     HeapTuple   tuple;
    5006          26 :     List       *result = NIL;
    5007             : 
    5008          26 :     classRel = heap_open(RelationRelationId, AccessShareLock);
    5009             : 
    5010          26 :     ScanKeyInit(&key[0],
    5011             :                 Anum_pg_class_reloftype,
    5012             :                 BTEqualStrategyNumber, F_OIDEQ,
    5013             :                 ObjectIdGetDatum(typeOid));
    5014             : 
    5015          26 :     scan = heap_beginscan_catalog(classRel, 1, key);
    5016             : 
    5017          58 :     while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
    5018             :     {
    5019          10 :         if (behavior == DROP_RESTRICT)
    5020           4 :             ereport(ERROR,
    5021             :                     (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
    5022             :                      errmsg("cannot alter type \"%s\" because it is the type of a typed table",
    5023             :                             typeName),
    5024             :                      errhint("Use ALTER ... CASCADE to alter the typed tables too.")));
    5025             :         else
    5026           6 :             result = lappend_oid(result, HeapTupleGetOid(tuple));
    5027             :     }
    5028             : 
    5029          22 :     heap_endscan(scan);
    5030          22 :     heap_close(classRel, AccessShareLock);
    5031             : 
    5032          22 :     return result;
    5033             : }
    5034             : 
    5035             : 
    5036             : /*
    5037             :  * check_of_type
    5038             :  *
    5039             :  * Check whether a type is suitable for CREATE TABLE OF/ALTER TABLE OF.  If it
    5040             :  * isn't suitable, throw an error.  Currently, we require that the type
    5041             :  * originated with CREATE TYPE AS.  We could support any row type, but doing so
    5042             :  * would require handling a number of extra corner cases in the DDL commands.
    5043             :  */
    5044             : void
    5045          24 : check_of_type(HeapTuple typetuple)
    5046             : {
    5047          24 :     Form_pg_type typ = (Form_pg_type) GETSTRUCT(typetuple);
    5048          24 :     bool        typeOk = false;
    5049             : 
    5050          24 :     if (typ->typtype == TYPTYPE_COMPOSITE)
    5051             :     {
    5052             :         Relation    typeRelation;
    5053             : 
    5054          24 :         Assert(OidIsValid(typ->typrelid));
    5055          24 :         typeRelation = relation_open(typ->typrelid, AccessShareLock);
    5056          24 :         typeOk = (typeRelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE);
    5057             : 
    5058             :         /*
    5059             :          * Close the parent rel, but keep our AccessShareLock on it until xact
    5060             :          * commit.  That will prevent someone else from deleting or ALTERing
    5061             :          * the type before the typed table creation/conversion commits.
    5062             :          */
    5063          24 :         relation_close(typeRelation, NoLock);
    5064             :     }
    5065          24 :     if (!typeOk)
    5066           1 :         ereport(ERROR,
    5067             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    5068             :                  errmsg("type %s is not a composite type",
    5069             :                         format_type_be(HeapTupleGetOid(typetuple)))));
    5070          23 : }
    5071             : 
    5072             : 
    5073             : /*
    5074             :  * ALTER TABLE ADD COLUMN
    5075             :  *
    5076             :  * Adds an additional attribute to a relation making the assumption that
    5077             :  * CHECK, NOT NULL, and FOREIGN KEY constraints will be removed from the
    5078             :  * AT_AddColumn AlterTableCmd by parse_utilcmd.c and added as independent
    5079             :  * AlterTableCmd's.
    5080             :  *
    5081             :  * ADD COLUMN cannot use the normal ALTER TABLE recursion mechanism, because we
    5082             :  * have to decide at runtime whether to recurse or not depending on whether we
    5083             :  * actually add a column or merely merge with an existing column.  (We can't
    5084             :  * check this in a static pre-pass because it won't handle multiple inheritance
    5085             :  * situations correctly.)
    5086             :  */
    5087             : static void
    5088         160 : ATPrepAddColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
    5089             :                 bool is_view, AlterTableCmd *cmd, LOCKMODE lockmode)
    5090             : {
    5091         160 :     if (rel->rd_rel->reloftype && !recursing)
    5092           1 :         ereport(ERROR,
    5093             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    5094             :                  errmsg("cannot add column to typed table")));
    5095             : 
    5096         159 :     if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
    5097           8 :         ATTypedTableRecursion(wqueue, rel, cmd, lockmode);
    5098             : 
    5099         158 :     if (recurse && !is_view)
    5100         147 :         cmd->subtype = AT_AddColumnRecurse;
    5101         158 : }
    5102             : 
    5103             : /*
    5104             :  * Add a column to a table; this handles the AT_AddOids cases as well.  The
    5105             :  * return value is the address of the new column in the parent relation.
    5106             :  */
    5107             : static ObjectAddress
    5108         202 : ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
    5109             :                 ColumnDef *colDef, bool isOid,
    5110             :                 bool recurse, bool recursing,
    5111             :                 bool if_not_exists, LOCKMODE lockmode)
    5112             : {
    5113         202 :     Oid         myrelid = RelationGetRelid(rel);
    5114             :     Relation    pgclass,
    5115             :                 attrdesc;
    5116             :     HeapTuple   reltup;
    5117             :     FormData_pg_attribute attribute;
    5118             :     int         newattnum;
    5119             :     char        relkind;
    5120             :     HeapTuple   typeTuple;
    5121             :     Oid         typeOid;
    5122             :     int32       typmod;
    5123             :     Oid         collOid;
    5124             :     Form_pg_type tform;
    5125             :     Expr       *defval;
    5126             :     List       *children;
    5127             :     ListCell   *child;
    5128             :     AclResult   aclresult;
    5129             :     ObjectAddress address;
    5130             : 
    5131             :     /* At top level, permission check was done in ATPrepCmd, else do it */
    5132         202 :     if (recursing)
    5133          45 :         ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);
    5134             : 
    5135         202 :     if (rel->rd_rel->relispartition && !recursing)
    5136           2 :         ereport(ERROR,
    5137             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    5138             :                  errmsg("cannot add column to a partition")));
    5139             : 
    5140         200 :     attrdesc = heap_open(AttributeRelationId, RowExclusiveLock);
    5141             : 
    5142             :     /*
    5143             :      * Are we adding the column to a recursion child?  If so, check whether to
    5144             :      * merge with an existing definition for the column.  If we do merge, we
    5145             :      * must not recurse.  Children will already have the column, and recursing
    5146             :      * into them would mess up attinhcount.
    5147             :      */
    5148         200 :     if (colDef->inhcount > 0)
    5149             :     {
    5150             :         HeapTuple   tuple;
    5151             : 
    5152             :         /* Does child already have a column by this name? */
    5153          45 :         tuple = SearchSysCacheCopyAttName(myrelid, colDef->colname);
    5154          45 :         if (HeapTupleIsValid(tuple))
    5155             :         {
    5156           5 :             Form_pg_attribute childatt = (Form_pg_attribute) GETSTRUCT(tuple);
    5157             :             Oid         ctypeId;
    5158             :             int32       ctypmod;
    5159             :             Oid         ccollid;
    5160             : 
    5161             :             /* Child column must match on type, typmod, and collation */
    5162           5 :             typenameTypeIdAndMod(NULL, colDef->typeName, &ctypeId, &ctypmod);
    5163          10 :             if (ctypeId != childatt->atttypid ||
    5164           5 :                 ctypmod != childatt->atttypmod)
    5165           0 :                 ereport(ERROR,
    5166             :                         (errcode(ERRCODE_DATATYPE_MISMATCH),
    5167             :                          errmsg("child table \"%s\" has different type for column \"%s\"",
    5168             :                                 RelationGetRelationName(rel), colDef->colname)));
    5169           5 :             ccollid = GetColumnDefCollation(NULL, colDef, ctypeId);
    5170           5 :             if (ccollid != childatt->attcollation)
    5171           0 :                 ereport(ERROR,
    5172             :                         (errcode(ERRCODE_COLLATION_MISMATCH),
    5173             :                          errmsg("child table \"%s\" has different collation for column \"%s\"",
    5174             :                                 RelationGetRelationName(rel), colDef->colname),
    5175             :                          errdetail("\"%s\" versus \"%s\"",
    5176             :                                    get_collation_name(ccollid),
    5177             :                                    get_collation_name(childatt->attcollation))));
    5178             : 
    5179             :             /* If it's OID, child column must actually be OID */
    5180           5 :             if (isOid && childatt->attnum != ObjectIdAttributeNumber)
    5181           0 :                 ereport(ERROR,
    5182             :                         (errcode(ERRCODE_DATATYPE_MISMATCH),
    5183             :                          errmsg("child table \"%s\" has a conflicting \"%s\" column",
    5184             :                                 RelationGetRelationName(rel), colDef->colname)));
    5185             : 
    5186             :             /* Bump the existing child att's inhcount */
    5187           5 :             childatt->attinhcount++;
    5188           5 :             CatalogTupleUpdate(attrdesc, &tuple->t_self, tuple);
    5189             : 
    5190           5 :             heap_freetuple(tuple);
    5191             : 
    5192             :             /* Inform the user about the merge */
    5193           5 :             ereport(NOTICE,
    5194             :                     (errmsg("merging definition of column \"%s\" for child \"%s\"",
    5195             :                             colDef->colname, RelationGetRelationName(rel))));
    5196             : 
    5197           5 :             heap_close(attrdesc, RowExclusiveLock);
    5198           5 :             return InvalidObjectAddress;
    5199             :         }
    5200             :     }
    5201             : 
    5202         195 :     pgclass = heap_open(RelationRelationId, RowExclusiveLock);
    5203             : 
    5204         195 :     reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(myrelid));
    5205         195 :     if (!HeapTupleIsValid(reltup))
    5206           0 :         elog(ERROR, "cache lookup failed for relation %u", myrelid);
    5207         195 :     relkind = ((Form_pg_class) GETSTRUCT(reltup))->relkind;
    5208             : 
    5209             :     /*
    5210             :      * Cannot add identity column if table has children, because identity does
    5211             :      * not inherit.  (Adding column and identity separately will work.)
    5212             :      */
    5213         195 :     if (colDef->identity &&
    5214           1 :         recurse &&
    5215           1 :         find_inheritance_children(myrelid, NoLock) != NIL)
    5216           1 :         ereport(ERROR,
    5217             :                 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    5218             :                  errmsg("cannot recursively add identity column to table that has child tables")));
    5219             : 
    5220             :     /* skip if the name already exists and if_not_exists is true */
    5221         194 :     if (!check_for_column_name_collision(rel, colDef->colname, if_not_exists))
    5222             :     {
    5223           6 :         heap_close(attrdesc, RowExclusiveLock);
    5224           6 :         heap_freetuple(reltup);
    5225           6 :         heap_close(pgclass, RowExclusiveLock);
    5226           6 :         return InvalidObjectAddress;
    5227             :     }
    5228             : 
    5229             :     /* Determine the new attribute's number */
    5230         184 :     if (isOid)
    5231          11 :         newattnum = ObjectIdAttributeNumber;
    5232             :     else
    5233             :     {
    5234         173 :         newattnum = ((Form_pg_class) GETSTRUCT(reltup))->relnatts + 1;
    5235         173 :         if (newattnum > MaxHeapAttributeNumber)
    5236           0 :             ereport(ERROR,
    5237             :                     (errcode(ERRCODE_TOO_MANY_COLUMNS),
    5238             :                      errmsg("tables can have at most %d columns",
    5239             :                             MaxHeapAttributeNumber)));
    5240             :     }
    5241             : 
    5242         184 :     typeTuple = typenameType(NULL, colDef->typeName, &typmod);
    5243         184 :     tform = (Form_pg_type) GETSTRUCT(typeTuple);
    5244         184 :     typeOid = HeapTupleGetOid(typeTuple);
    5245             : 
    5246         184 :     aclresult = pg_type_aclcheck(typeOid, GetUserId(), ACL_USAGE);
    5247         184 :     if (aclresult != ACLCHECK_OK)
    5248           2 :         aclcheck_error_type(aclresult, typeOid);
    5249             : 
    5250         182 :     collOid = GetColumnDefCollation(NULL, colDef, typeOid);
    5251             : 
    5252             :     /* make sure datatype is legal for a column */
    5253         182 :     CheckAttributeType(colDef->colname, typeOid, collOid,
    5254         182 :                        list_make1_oid(rel->rd_rel->reltype),
    5255             :                        false);
    5256             : 
    5257             :     /* construct new attribute's pg_attribute entry */
    5258         178 :     attribute.attrelid = myrelid;
    5259         178 :     namestrcpy(&(attribute.attname), colDef->colname);
    5260         178 :     attribute.atttypid = typeOid;
    5261         178 :     attribute.attstattarget = (newattnum > 0) ? -1 : 0;
    5262         178 :     attribute.attlen = tform->typlen;
    5263         178 :     attribute.attcacheoff = -1;
    5264         178 :     attribute.atttypmod = typmod;
    5265         178 :     attribute.attnum = newattnum;
    5266         178 :     attribute.attbyval = tform->typbyval;
    5267         178 :     attribute.attndims = list_length(colDef->typeName->arrayBounds);
    5268         178 :     attribute.attstorage = tform->typstorage;
    5269         178 :     attribute.attalign = tform->typalign;
    5270         178 :     attribute.attnotnull = colDef->is_not_null;
    5271         178 :     attribute.atthasdef = false;
    5272         178 :     attribute.attidentity = colDef->identity;
    5273         178 :     attribute.attisdropped = false;
    5274         178 :     attribute.attislocal = colDef->is_local;
    5275         178 :     attribute.attinhcount = colDef->inhcount;
    5276         178 :     attribute.attcollation = collOid;
    5277             :     /* attribute.attacl is handled by InsertPgAttributeTuple */
    5278             : 
    5279         178 :     ReleaseSysCache(typeTuple);
    5280             : 
    5281         178 :     InsertPgAttributeTuple(attrdesc, &attribute, NULL);
    5282             : 
    5283         178 :     heap_close(attrdesc, RowExclusiveLock);
    5284             : 
    5285             :     /*
    5286             :      * Update pg_class tuple as appropriate
    5287             :      */
    5288         178 :     if (isOid)
    5289          11 :         ((Form_pg_class) GETSTRUCT(reltup))->relhasoids = true;
    5290             :     else
    5291         167 :         ((Form_pg_class) GETSTRUCT(reltup))->relnatts = newattnum;
    5292             : 
    5293         178 :     CatalogTupleUpdate(pgclass, &reltup->t_self, reltup);
    5294             : 
    5295         178 :     heap_freetuple(reltup);
    5296             : 
    5297             :     /* Post creation hook for new attribute */
    5298         178 :     InvokeObjectPostCreateHook(RelationRelationId, myrelid, newattnum);
    5299             : 
    5300         178 :     heap_close(pgclass, RowExclusiveLock);
    5301             : 
    5302             :     /* Make the attribute's catalog entry visible */
    5303         178 :     CommandCounterIncrement();
    5304             : 
    5305             :     /*
    5306             :      * Store the DEFAULT, if any, in the catalogs
    5307             :      */
    5308         178 :     if (colDef->raw_default)
    5309             :     {
    5310             :         RawColumnDefault *rawEnt;
    5311             : 
    5312          15 :         rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
    5313          15 :         rawEnt->attnum = attribute.attnum;
    5314          15 :         rawEnt->raw_default = copyObject(colDef->raw_default);
    5315             : 
    5316             :         /*
    5317             :          * This function is intended for CREATE TABLE, so it processes a
    5318             :          * _list_ of defaults, but we just do one.
    5319             :          */
    5320          15 :         AddRelationNewConstraints(rel, list_make1(rawEnt), NIL,
    5321             :                                   false, true, false);
    5322             : 
    5323             :         /* Make the additional catalog changes visible */
    5324          15 :         CommandCounterIncrement();
    5325             :     }
    5326             : 
    5327             :     /*
    5328             :      * Tell Phase 3 to fill in the default expression, if there is one.
    5329             :      *
    5330             :      * If there is no default, Phase 3 doesn't have to do anything, because
    5331             :      * that effectively means that the default is NULL.  The heap tuple access
    5332             :      * routines always check for attnum > # of attributes in tuple, and return
    5333             :      * NULL if so, so without any modification of the tuple data we will get
    5334             :      * the effect of NULL values in the new column.
    5335             :      *
    5336             :      * An exception occurs when the new column is of a domain type: the domain
    5337             :      * might have a NOT NULL constraint, or a check constraint that indirectly
    5338             :      * rejects nulls.  If there are any domain constraints then we construct
    5339             :      * an explicit NULL default value that will be passed through
    5340             :      * CoerceToDomain processing.  (This is a tad inefficient, since it causes
    5341             :      * rewriting the table which we really don't have to do, but the present
    5342             :      * design of domain processing doesn't offer any simple way of checking
    5343             :      * the constraints more directly.)
    5344             :      *
    5345             :      * Note: we use build_column_default, and not just the cooked default
    5346             :      * returned by AddRelationNewConstraints, so that the right thing happens
    5347             :      * when a datatype's default applies.
    5348             :      *
    5349             :      * We skip this step completely for views and foreign tables.  For a view,
    5350             :      * we can only get here from CREATE OR REPLACE VIEW, which historically
    5351             :      * doesn't set up defaults, not even for domain-typed columns.  And in any
    5352             :      * case we mustn't invoke Phase 3 on a view or foreign table, since they
    5353             :      * have no storage.
    5354             :      */
    5355         178 :     if (relkind != RELKIND_VIEW && relkind != RELKIND_COMPOSITE_TYPE
    5356         171 :         && relkind != RELKIND_FOREIGN_TABLE && attribute.attnum > 0)
    5357             :     {
    5358         143 :         defval = (Expr *) build_column_default(rel, attribute.attnum);
    5359             : 
    5360         143 :         if (!defval && DomainHasConstraints(typeOid))
    5361             :         {
    5362             :             Oid         baseTypeId;
    5363             :             int32       baseTypeMod;
    5364             :             Oid         baseTypeColl;
    5365             : 
    5366           1 :             baseTypeMod = typmod;
    5367           1 :             baseTypeId = getBaseTypeAndTypmod(typeOid, &baseTypeMod);
    5368           1 :             baseTypeColl = get_typcollation(baseTypeId);
    5369           1 :             defval = (Expr *) makeNullConst(baseTypeId, baseTypeMod, baseTypeColl);
    5370           1 :             defval = (Expr *) coerce_to_target_type(NULL,
    5371             :                                                     (Node *) defval,
    5372             :                                                     baseTypeId,
    5373             :                                                     typeOid,
    5374             :                                                     typmod,
    5375             :                                                     COERCION_ASSIGNMENT,
    5376             :                                                     COERCE_IMPLICIT_CAST,
    5377             :                                                     -1);
    5378           1 :             if (defval == NULL) /* should not happen */
    5379           0 :                 elog(ERROR, "failed to coerce base type to domain");
    5380             :         }
    5381             : 
    5382         143 :         if (defval)
    5383             :         {
    5384             :             NewColumnValue *newval;
    5385             : 
    5386          13 :             newval = (NewColumnValue *) palloc0(sizeof(NewColumnValue));
    5387          13 :             newval->attnum = attribute.attnum;
    5388          13 :             newval->expr = expression_planner(defval);
    5389             : 
    5390          13 :             tab->newvals = lappend(tab->newvals, newval);
    5391          13 :             tab->rewrite |= AT_REWRITE_DEFAULT_VAL;
    5392             :         }
    5393             : 
    5394             :         /*
    5395             :          * If the new column is NOT NULL, tell Phase 3 it needs to test that.
    5396             :          * (Note we don't do this for an OID column.  OID will be marked not
    5397             :          * null, but since it's filled specially, there's no need to test
    5398             :          * anything.)
    5399             :          */
    5400         143 :         tab->new_notnull |= colDef->is_not_null;
    5401             :     }
    5402             : 
    5403             :     /*
    5404             :      * If we are adding an OID column, we have to tell Phase 3 to rewrite the
    5405             :      * table to fix that.
    5406             :      */
    5407         178 :     if (isOid)
    5408          11 :         tab->rewrite |= AT_REWRITE_ALTER_OID;
    5409             : 
    5410             :     /*
    5411             :      * Add needed dependency entries for the new column.
    5412             :      */
    5413         178 :     add_column_datatype_dependency(myrelid, newattnum, attribute.atttypid);
    5414         178 :     add_column_collation_dependency(myrelid, newattnum, attribute.attcollation);
    5415             : 
    5416             :     /*
    5417             :      * Propagate to children as appropriate.  Unlike most other ALTER
    5418             :      * routines, we have to do this one level of recursion at a time; we can't
    5419             :      * use find_all_inheritors to do it in one pass.
    5420             :      */
    5421         178 :     children = find_inheritance_children(RelationGetRelid(rel), lockmode);
    5422             : 
    5423             :     /*
    5424             :      * If we are told not to recurse, there had better not be any child
    5425             :      * tables; else the addition would put them out of step.
    5426             :      */
    5427         178 :     if (children && !recurse)
    5428           2 :         ereport(ERROR,
    5429             :                 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    5430             :                  errmsg("column must be added to child tables too")));
    5431             : 
    5432             :     /* Children should see column as singly inherited */
    5433         176 :     if (!recursing)
    5434             :     {
    5435         136 :         colDef = copyObject(colDef);
    5436         136 :         colDef->inhcount = 1;
    5437         136 :         colDef->is_local = false;
    5438             :     }
    5439             : 
    5440         221 :     foreach(child, children)
    5441             :     {
    5442          45 :         Oid         childrelid = lfirst_oid(child);
    5443             :         Relation    childrel;
    5444             :         AlteredTableInfo *childtab;
    5445             : 
    5446             :         /* find_inheritance_children already got lock */
    5447          45 :         childrel = heap_open(childrelid, NoLock);
    5448          45 :         CheckTableNotInUse(childrel, "ALTER TABLE");
    5449             : 
    5450             :         /* Find or create work queue entry for this table */
    5451          45 :         childtab = ATGetQueueEntry(wqueue, childrel);
    5452             : 
    5453             :         /* Recurse to child; return value is ignored */
    5454          45 :         ATExecAddColumn(wqueue, childtab, childrel,
    5455             :                         colDef, isOid, recurse, true,
    5456             :                         if_not_exists, lockmode);
    5457             : 
    5458          45 :         heap_close(childrel, NoLock);
    5459             :     }
    5460             : 
    5461         176 :     ObjectAddressSubSet(address, RelationRelationId, myrelid, newattnum);
    5462         176 :     return address;
    5463             : }
    5464             : 
    5465             : /*
    5466             :  * If a new or renamed column will collide with the name of an existing
    5467             :  * column and if_not_exists is false then error out, else do nothing.
    5468             :  */
    5469             : static bool
    5470         260 : check_for_column_name_collision(Relation rel, const char *colname,
    5471             :                                 bool if_not_exists)
    5472             : {
    5473             :     HeapTuple   attTuple;
    5474             :     int         attnum;
    5475             : 
    5476             :     /*
    5477             :      * this test is deliberately not attisdropped-aware, since if one tries to
    5478             :      * add a column matching a dropped column name, it's gonna fail anyway.
    5479             :      */
    5480         260 :     attTuple = SearchSysCache2(ATTNAME,
    5481             :                                ObjectIdGetDatum(RelationGetRelid(rel)),
    5482             :                                PointerGetDatum(colname));
    5483         260 :     if (!HeapTupleIsValid(attTuple))
    5484         248 :         return true;
    5485             : 
    5486          12 :     attnum = ((Form_pg_attribute) GETSTRUCT(attTuple))->attnum;
    5487          12 :     ReleaseSysCache(attTuple);
    5488             : 
    5489             :     /*
    5490             :      * We throw a different error message for conflicts with system column
    5491             :      * names, since they are normally not shown and the user might otherwise
    5492             :      * be confused about the reason for the conflict.
    5493             :      */
    5494          12 :     if (attnum <= 0)
    5495           2 :         ereport(ERROR,
    5496             :                 (errcode(ERRCODE_DUPLICATE_COLUMN),
    5497             :                  errmsg("column name \"%s\" conflicts with a system column name",
    5498             :                         colname)));
    5499             :     else
    5500             :     {
    5501          10 :         if (if_not_exists)
    5502             :         {
    5503           6 :             ereport(NOTICE,
    5504             :                     (errcode(ERRCODE_DUPLICATE_COLUMN),
    5505             :                      errmsg("column \"%s\" of relation \"%s\" already exists, skipping",
    5506             :                             colname, RelationGetRelationName(rel))));
    5507           6 :             return false;
    5508             :         }
    5509             : 
    5510           4 :         ereport(ERROR,
    5511             :                 (errcode(ERRCODE_DUPLICATE_COLUMN),
    5512             :                  errmsg("column \"%s\" of relation \"%s\" already exists",
    5513             :                         colname, RelationGetRelationName(rel))));
    5514             :     }
    5515             : 
    5516             :     return true;
    5517             : }
    5518             : 
    5519             : /*
    5520             :  * Install a column's dependency on its datatype.
    5521             :  */
    5522             : static void
    5523         246 : add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid)
    5524             : {
    5525             :     ObjectAddress myself,
    5526             :                 referenced;
    5527             : 
    5528         246 :     myself.classId = RelationRelationId;
    5529         246 :     myself.objectId = relid;
    5530         246 :     myself.objectSubId = attnum;
    5531         246 :     referenced.classId = TypeRelationId;
    5532         246 :     referenced.objectId = typid;
    5533         246 :     referenced.objectSubId = 0;
    5534         246 :     recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
    5535         246 : }
    5536             : 
    5537             : /*
    5538             :  * Install a column's dependency on its collation.
    5539             :  */
    5540             : static void
    5541         246 : add_column_collation_dependency(Oid relid, int32 attnum, Oid collid)
    5542             : {
    5543             :     ObjectAddress myself,
    5544             :                 referenced;
    5545             : 
    5546             :     /* We know the default collation is pinned, so don't bother recording it */
    5547         246 :     if (OidIsValid(collid) && collid != DEFAULT_COLLATION_OID)
    5548             :     {
    5549           1 :         myself.classId = RelationRelationId;
    5550           1 :         myself.objectId = relid;
    5551           1 :         myself.objectSubId = attnum;
    5552           1 :         referenced.classId = CollationRelationId;
    5553           1 :         referenced.objectId = collid;
    5554           1 :         referenced.objectSubId = 0;
    5555           1 :         recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
    5556             :     }
    5557         246 : }
    5558             : 
    5559             : /*
    5560             :  * ALTER TABLE SET WITH OIDS
    5561             :  *
    5562             :  * Basically this is an ADD COLUMN for the special OID column.  We have
    5563             :  * to cons up a ColumnDef node because the ADD COLUMN code needs one.
    5564             :  */
    5565             : static void
    5566           9 : ATPrepAddOids(List **wqueue, Relation rel, bool recurse, AlterTableCmd *cmd, LOCKMODE lockmode)
    5567             : {
    5568             :     /* If we're recursing to a child table, the ColumnDef is already set up */
    5569           9 :     if (cmd->def == NULL)
    5570             :     {
    5571           9 :         ColumnDef  *cdef = makeNode(ColumnDef);
    5572             : 
    5573           9 :         cdef->colname = pstrdup("oid");
    5574           9 :         cdef->typeName = makeTypeNameFromOid(OIDOID, -1);
    5575           9 :         cdef->inhcount = 0;
    5576           9 :         cdef->is_local = true;
    5577           9 :         cdef->is_not_null = true;
    5578           9 :         cdef->storage = 0;
    5579           9 :         cdef->location = -1;
    5580           9 :         cmd->def = (Node *) cdef;
    5581             :     }
    5582           9 :     ATPrepAddColumn(wqueue, rel, recurse, false, false, cmd, lockmode);
    5583             : 
    5584           9 :     if (recurse)
    5585           9 :         cmd->subtype = AT_AddOidsRecurse;
    5586           9 : }
    5587             : 
    5588             : /*
    5589             :  * ALTER TABLE ALTER COLUMN DROP NOT NULL
    5590             :  *
    5591             :  * Return the address of the modified column.  If the column was already
    5592             :  * nullable, InvalidObjectAddress is returned.
    5593             :  */
    5594             : 
    5595             : static void
    5596          19 : ATPrepDropNotNull(Relation rel, bool recurse, bool recursing)
    5597             : {
    5598             :     /*
    5599             :      * If the parent is a partitioned table, like check constraints, we do not
    5600             :      * support removing the NOT NULL while partitions exist.
    5601             :      */
    5602          19 :     if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
    5603             :     {
    5604           2 :         PartitionDesc partdesc = RelationGetPartitionDesc(rel);
    5605             : 
    5606           2 :         Assert(partdesc != NULL);
    5607           2 :         if (partdesc->nparts > 0 && !recurse && !recursing)
    5608           1 :             ereport(ERROR,
    5609             :                     (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    5610             :                      errmsg("cannot remove constraint from only the partitioned table when partitions exist"),
    5611             :                      errhint("Do not specify the ONLY keyword.")));
    5612             :     }
    5613          18 : }
    5614             : static ObjectAddress
    5615          18 : ATExecDropNotNull(Relation rel, const char *colName, LOCKMODE lockmode)
    5616             : {
    5617             :     HeapTuple   tuple;
    5618             :     AttrNumber  attnum;
    5619             :     Relation    attr_rel;
    5620             :     List       *indexoidlist;
    5621             :     ListCell   *indexoidscan;
    5622             :     ObjectAddress address;
    5623             : 
    5624             :     /*
    5625             :      * lookup the attribute
    5626             :      */
    5627          18 :     attr_rel = heap_open(AttributeRelationId, RowExclusiveLock);
    5628             : 
    5629          18 :     tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
    5630             : 
    5631          18 :     if (!HeapTupleIsValid(tuple))
    5632           3 :         ereport(ERROR,
    5633             :                 (errcode(ERRCODE_UNDEFINED_COLUMN),
    5634             :                  errmsg("column \"%s\" of relation \"%s\" does not exist",
    5635             :                         colName, RelationGetRelationName(rel))));
    5636             : 
    5637          15 :     attnum = ((Form_pg_attribute) GETSTRUCT(tuple))->attnum;
    5638             : 
    5639             :     /* Prevent them from altering a system attribute */
    5640          15 :     if (attnum <= 0)
    5641           1 :         ereport(ERROR,
    5642             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    5643             :                  errmsg("cannot alter system column \"%s\"",
    5644             :                         colName)));
    5645             : 
    5646          14 :     if (get_attidentity(RelationGetRelid(rel), attnum))
    5647           1 :         ereport(ERROR,
    5648             :                 (errcode(ERRCODE_SYNTAX_ERROR),
    5649             :                  errmsg("column \"%s\" of relation \"%s\" is an identity column",
    5650             :                         colName, RelationGetRelationName(rel))));
    5651             : 
    5652             :     /*
    5653             :      * Check that the attribute is not in a primary key
    5654             :      *
    5655             :      * Note: we'll throw error even if the pkey index is not valid.
    5656             :      */
    5657             : 
    5658             :     /* Loop over all indexes on the relation */
    5659          13 :     indexoidlist = RelationGetIndexList(rel);
    5660             : 
    5661          13 :     foreach(indexoidscan, indexoidlist)
    5662             :     {
    5663           1 :         Oid         indexoid = lfirst_oid(indexoidscan);
    5664             :         HeapTuple   indexTuple;
    5665             :         Form_pg_index indexStruct;
    5666             :         int         i;
    5667             : 
    5668           1 :         indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexoid));
    5669           1 :         if (!HeapTupleIsValid(indexTuple))
    5670           0 :             elog(ERROR, "cache lookup failed for index %u", indexoid);
    5671           1 :         indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
    5672             : 
    5673             :         /* If the index is not a primary key, skip the check */
    5674           1 :         if (indexStruct->indisprimary)
    5675             :         {
    5676             :             /*
    5677             :              * Loop over each attribute in the primary key and see if it
    5678             :              * matches the to-be-altered attribute
    5679             :              */
    5680           1 :             for (i = 0; i < indexStruct->indnatts; i++)
    5681             :             {
    5682           1 :                 if (indexStruct->indkey.values[i] == attnum)
    5683           1 :                     ereport(ERROR,
    5684             :                             (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    5685             :                              errmsg("column \"%s\" is in a primary key",
    5686             :                                     colName)));
    5687             :             }
    5688             :         }
    5689             : 
    5690           0 :         ReleaseSysCache(indexTuple);
    5691             :     }
    5692             : 
    5693          12 :     list_free(indexoidlist);
    5694             : 
    5695             :     /* If rel is partition, shouldn't drop NOT NULL if parent has the same */
    5696          12 :     if (rel->rd_rel->relispartition)
    5697             :     {
    5698           2 :         Oid         parentId = get_partition_parent(RelationGetRelid(rel));
    5699           2 :         Relation    parent = heap_open(parentId, AccessShareLock);
    5700           2 :         TupleDesc   tupDesc = RelationGetDescr(parent);
    5701             :         AttrNumber  parent_attnum;
    5702             : 
    5703           2 :         parent_attnum = get_attnum(parentId, colName);
    5704           2 :         if (TupleDescAttr(tupDesc, parent_attnum - 1)->attnotnull)
    5705           2 :             ereport(ERROR,
    5706             :                     (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    5707             :                      errmsg("column \"%s\" is marked NOT NULL in parent table",
    5708             :                             colName)));
    5709           0 :         heap_close(parent, AccessShareLock);
    5710             :     }
    5711             : 
    5712             :     /*
    5713             :      * Okay, actually perform the catalog change ... if needed
    5714             :      */
    5715          10 :     if (((Form_pg_attribute) GETSTRUCT(tuple))->attnotnull)
    5716             :     {
    5717          10 :         ((Form_pg_attribute) GETSTRUCT(tuple))->attnotnull = FALSE;
    5718             : 
    5719          10 :         CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
    5720             : 
    5721          10 :         ObjectAddressSubSet(address, RelationRelationId,
    5722             :                             RelationGetRelid(rel), attnum);
    5723             :     }
    5724             :     else
    5725           0 :         address = InvalidObjectAddress;
    5726             : 
    5727          10 :     InvokeObjectPostAlterHook(RelationRelationId,
    5728             :                               RelationGetRelid(rel), attnum);
    5729             : 
    5730          10 :     heap_close(attr_rel, RowExclusiveLock);
    5731             : 
    5732          10 :     return address;
    5733             : }
    5734             : 
    5735             : /*
    5736             :  * ALTER TABLE ALTER COLUMN SET NOT NULL
    5737             :  *
    5738             :  * Return the address of the modified column.  If the column was already NOT
    5739             :  * NULL, InvalidObjectAddress is returned.
    5740             :  */
    5741             : 
    5742             : static void
    5743          54 : ATPrepSetNotNull(Relation rel, bool recurse, bool recursing)
    5744             : {
    5745             :     /*
    5746             :      * If the parent is a partitioned table, like check constraints, NOT NULL
    5747             :      * constraints must be added to the child tables.  Complain if requested
    5748             :      * otherwise and partitions exist.
    5749             :      */
    5750          54 :     if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
    5751             :     {
    5752           8 :         PartitionDesc partdesc = RelationGetPartitionDesc(rel);
    5753             : 
    5754           8 :         if (partdesc && partdesc->nparts > 0 && !recurse && !recursing)
    5755           1 :             ereport(ERROR,
    5756             :                     (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    5757             :                      errmsg("cannot add constraint to only the partitioned table when partitions exist"),
    5758             :                      errhint("Do not specify the ONLY keyword.")));
    5759             :     }
    5760          53 : }
    5761             : 
    5762             : static ObjectAddress
    5763          53 : ATExecSetNotNull(AlteredTableInfo *tab, Relation rel,
    5764             :                  const char *colName, LOCKMODE lockmode)
    5765             : {
    5766             :     HeapTuple   tuple;
    5767             :     AttrNumber  attnum;
    5768             :     Relation    attr_rel;
    5769             :     ObjectAddress address;
    5770             : 
    5771             :     /*
    5772             :      * lookup the attribute
    5773             :      */
    5774          53 :     attr_rel = heap_open(AttributeRelationId, RowExclusiveLock);
    5775             : 
    5776          53 :     tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
    5777             : 
    5778          53 :     if (!HeapTupleIsValid(tuple))
    5779           3 :         ereport(ERROR,
    5780             :                 (errcode(ERRCODE_UNDEFINED_COLUMN),
    5781             :                  errmsg("column \"%s\" of relation \"%s\" does not exist",
    5782             :                         colName, RelationGetRelationName(rel))));
    5783             : 
    5784          50 :     attnum = ((Form_pg_attribute) GETSTRUCT(tuple))->attnum;
    5785             : 
    5786             :     /* Prevent them from altering a system attribute */
    5787          50 :     if (attnum <= 0)
    5788           1 :         ereport(ERROR,
    5789             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    5790             :                  errmsg("cannot alter system column \"%s\"",
    5791             :                         colName)));
    5792             : 
    5793             :     /*
    5794             :      * Okay, actually perform the catalog change ... if needed
    5795             :      */
    5796          49 :     if (!((Form_pg_attribute) GETSTRUCT(tuple))->attnotnull)
    5797             :     {
    5798          43 :         ((Form_pg_attribute) GETSTRUCT(tuple))->attnotnull = TRUE;
    5799             : 
    5800          43 :         CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
    5801             : 
    5802             :         /* Tell Phase 3 it needs to test the constraint */
    5803          43 :         tab->new_notnull = true;
    5804             : 
    5805          43 :         ObjectAddressSubSet(address, RelationRelationId,
    5806             :                             RelationGetRelid(rel), attnum);
    5807             :     }
    5808             :     else
    5809           6 :         address = InvalidObjectAddress;
    5810             : 
    5811          49 :     InvokeObjectPostAlterHook(RelationRelationId,
    5812             :                               RelationGetRelid(rel), attnum);
    5813             : 
    5814          49 :     heap_close(attr_rel, RowExclusiveLock);
    5815             : 
    5816          49 :     return address;
    5817             : }
    5818             : 
    5819             : /*
    5820             :  * ALTER TABLE ALTER COLUMN SET/DROP DEFAULT
    5821             :  *
    5822             :  * Return the address of the affected column.
    5823             :  */
    5824             : static ObjectAddress
    5825          36 : ATExecColumnDefault(Relation rel, const char *colName,
    5826             :                     Node *newDefault, LOCKMODE lockmode)
    5827             : {
    5828             :     AttrNumber  attnum;
    5829             :     ObjectAddress address;
    5830             : 
    5831             :     /*
    5832             :      * get the number of the attribute
    5833             :      */
    5834          36 :     attnum = get_attnum(RelationGetRelid(rel), colName);
    5835          36 :     if (attnum == InvalidAttrNumber)
    5836           5 :         ereport(ERROR,
    5837             :                 (errcode(ERRCODE_UNDEFINED_COLUMN),
    5838             :                  errmsg("column \"%s\" of relation \"%s\" does not exist",
    5839             :                         colName, RelationGetRelationName(rel))));
    5840             : 
    5841             :     /* Prevent them from altering a system attribute */
    5842          31 :     if (attnum <= 0)
    5843           0 :         ereport(ERROR,
    5844             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    5845             :                  errmsg("cannot alter system column \"%s\"",
    5846             :                         colName)));
    5847             : 
    5848          31 :     if (get_attidentity(RelationGetRelid(rel), attnum))
    5849           1 :         ereport(ERROR,
    5850             :                 (errcode(ERRCODE_SYNTAX_ERROR),
    5851             :                  errmsg("column \"%s\" of relation \"%s\" is an identity column",
    5852             :                         colName, RelationGetRelationName(rel)),
    5853             :                  newDefault ? 0 : errhint("Use ALTER TABLE ... ALTER COLUMN ... DROP IDENTITY instead.")));
    5854             : 
    5855             :     /*
    5856             :      * Remove any old default for the column.  We use RESTRICT here for
    5857             :      * safety, but at present we do not expect anything to depend on the
    5858             :      * default.
    5859             :      *
    5860             :      * We treat removing the existing default as an internal operation when it
    5861             :      * is preparatory to adding a new default, but as a user-initiated
    5862             :      * operation when the user asked for a drop.
    5863             :      */
    5864          30 :     RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT, false,
    5865             :                       newDefault == NULL ? false : true);
    5866             : 
    5867          30 :     if (newDefault)
    5868             :     {
    5869             :         /* SET DEFAULT */
    5870             :         RawColumnDefault *rawEnt;
    5871             : 
    5872          19 :         rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
    5873          19 :         rawEnt->attnum = attnum;
    5874          19 :         rawEnt->raw_default = newDefault;
    5875             : 
    5876             :         /*
    5877             :          * This function is intended for CREATE TABLE, so it processes a
    5878             :          * _list_ of defaults, but we just do one.
    5879             :          */
    5880          19 :         AddRelationNewConstraints(rel, list_make1(rawEnt), NIL,
    5881             :                                   false, true, false);
    5882             :     }
    5883             : 
    5884          29 :     ObjectAddressSubSet(address, RelationRelationId,
    5885             :                         RelationGetRelid(rel), attnum);
    5886          29 :     return address;
    5887             : }
    5888             : 
    5889             : /*
    5890             :  * ALTER TABLE ALTER COLUMN ADD IDENTITY
    5891             :  *
    5892             :  * Return the address of the affected column.
    5893             :  */
    5894             : static ObjectAddress
    5895           6 : ATExecAddIdentity(Relation rel, const char *colName,
    5896             :                   Node *def, LOCKMODE lockmode)
    5897             : {
    5898             :     Relation    attrelation;
    5899             :     HeapTuple   tuple;
    5900             :     Form_pg_attribute attTup;
    5901             :     AttrNumber  attnum;
    5902             :     ObjectAddress address;
    5903           6 :     ColumnDef  *cdef = castNode(ColumnDef, def);
    5904             : 
    5905           6 :     attrelation = heap_open(AttributeRelationId, RowExclusiveLock);
    5906             : 
    5907           6 :     tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
    5908           6 :     if (!HeapTupleIsValid(tuple))
    5909           0 :         ereport(ERROR,
    5910             :                 (errcode(ERRCODE_UNDEFINED_COLUMN),
    5911             :                  errmsg("column \"%s\" of relation \"%s\" does not exist",
    5912             :                         colName, RelationGetRelationName(rel))));
    5913           6 :     attTup = (Form_pg_attribute) GETSTRUCT(tuple);
    5914           6 :     attnum = attTup->attnum;
    5915             : 
    5916             :     /* Can't alter a system attribute */
    5917           6 :     if (attnum <= 0)
    5918           0 :         ereport(ERROR,
    5919             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    5920             :                  errmsg("cannot alter system column \"%s\"",
    5921             :                         colName)));
    5922             : 
    5923             :     /*
    5924             :      * Creating a column as identity implies NOT NULL, so adding the identity
    5925             :      * to an existing column that is not NOT NULL would create a state that
    5926             :      * cannot be reproduced without contortions.
    5927             :      */
    5928           6 :     if (!attTup->attnotnull)
    5929           1 :         ereport(ERROR,
    5930             :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    5931             :                  errmsg("column \"%s\" of relation \"%s\" must be declared NOT NULL before identity can be added",
    5932             :                         colName, RelationGetRelationName(rel))));
    5933             : 
    5934           5 :     if (attTup->attidentity)
    5935           2 :         ereport(ERROR,
    5936             :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    5937             :                  errmsg("column \"%s\" of relation \"%s\" is already an identity column",
    5938             :                         colName, RelationGetRelationName(rel))));
    5939             : 
    5940           3 :     if (attTup->atthasdef)
    5941           1 :         ereport(ERROR,
    5942             :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    5943             :                  errmsg("column \"%s\" of relation \"%s\" already has a default value",
    5944             :                         colName, RelationGetRelationName(rel))));
    5945             : 
    5946           2 :     attTup->attidentity = cdef->identity;
    5947           2 :     CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
    5948             : 
    5949           2 :     InvokeObjectPostAlterHook(RelationRelationId,
    5950             :                               RelationGetRelid(rel),
    5951             :                               attTup->attnum);
    5952           2 :     ObjectAddressSubSet(address, RelationRelationId,
    5953             :                         RelationGetRelid(rel), attnum);
    5954           2 :     heap_freetuple(tuple);
    5955             : 
    5956           2 :     heap_close(attrelation, RowExclusiveLock);
    5957             : 
    5958           2 :     return address;
    5959             : }
    5960             : 
    5961             : /*
    5962             :  * ALTER TABLE ALTER COLUMN SET { GENERATED or sequence options }
    5963             :  *
    5964             :  * Return the address of the affected column.
    5965             :  */
    5966             : static ObjectAddress
    5967           4 : ATExecSetIdentity(Relation rel, const char *colName, Node *def, LOCKMODE lockmode)
    5968             : {
    5969             :     ListCell   *option;
    5970           4 :     DefElem    *generatedEl = NULL;
    5971             :     HeapTuple   tuple;
    5972             :     Form_pg_attribute attTup;
    5973             :     AttrNumber  attnum;
    5974             :     Relation    attrelation;
    5975             :     ObjectAddress address;
    5976             : 
    5977           6 :     foreach(option, castNode(List, def))
    5978             :     {
    5979           2 :         DefElem    *defel = lfirst_node(DefElem, option);
    5980             : 
    5981           2 :         if (strcmp(defel->defname, "generated") == 0)
    5982             :         {
    5983           2 :             if (generatedEl)
    5984           0 :                 ereport(ERROR,
    5985             :                         (errcode(ERRCODE_SYNTAX_ERROR),
    5986             :                          errmsg("conflicting or redundant options")));
    5987           2 :             generatedEl = defel;
    5988             :         }
    5989             :         else
    5990           0 :             elog(ERROR, "option \"%s\" not recognized",
    5991             :                  defel->defname);
    5992             :     }
    5993             : 
    5994             :     /*
    5995             :      * Even if there is nothing to change here, we run all the checks.  There
    5996             :      * will be a subsequent ALTER SEQUENCE that relies on everything being
    5997             :      * there.
    5998             :      */
    5999             : 
    6000           4 :     attrelation = heap_open(AttributeRelationId, RowExclusiveLock);
    6001           4 :     tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
    6002           4 :     if (!HeapTupleIsValid(tuple))
    6003           0 :         ereport(ERROR,
    6004             :                 (errcode(ERRCODE_UNDEFINED_COLUMN),
    6005             :                  errmsg("column \"%s\" of relation \"%s\" does not exist",
    6006             :                         colName, RelationGetRelationName(rel))));
    6007             : 
    6008           4 :     attTup = (Form_pg_attribute) GETSTRUCT(tuple);
    6009           4 :     attnum = attTup->attnum;
    6010             : 
    6011           4 :     if (attnum <= 0)
    6012           0 :         ereport(ERROR,
    6013             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    6014             :                  errmsg("cannot alter system column \"%s\"",
    6015             :                         colName)));
    6016             : 
    6017           4 :     if (!attTup->attidentity)
    6018           1 :         ereport(ERROR,
    6019             :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    6020             :                  errmsg("column \"%s\" of relation \"%s\" is not an identity column",
    6021             :                         colName, RelationGetRelationName(rel))));
    6022             : 
    6023           3 :     if (generatedEl)
    6024             :     {
    6025           2 :         attTup->attidentity = defGetInt32(generatedEl);
    6026           2 :         CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
    6027             : 
    6028           2 :         InvokeObjectPostAlterHook(RelationRelationId,
    6029             :                                   RelationGetRelid(rel),
    6030             :                                   attTup->attnum);
    6031           2 :         ObjectAddressSubSet(address, RelationRelationId,
    6032             :                             RelationGetRelid(rel), attnum);
    6033             :     }
    6034             :     else
    6035           1 :         address = InvalidObjectAddress;
    6036             : 
    6037           3 :     heap_freetuple(tuple);
    6038           3 :     heap_close(attrelation, RowExclusiveLock);
    6039             : 
    6040           3 :     return address;
    6041             : }
    6042             : 
    6043             : /*
    6044             :  * ALTER TABLE ALTER COLUMN DROP IDENTITY
    6045             :  *
    6046             :  * Return the address of the affected column.
    6047             :  */
    6048             : static ObjectAddress
    6049           4 : ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode)
    6050             : {
    6051             :     HeapTuple   tuple;
    6052             :     Form_pg_attribute attTup;
    6053             :     AttrNumber  attnum;
    6054             :     Relation    attrelation;
    6055             :     ObjectAddress address;
    6056             :     Oid         seqid;
    6057             :     ObjectAddress seqaddress;
    6058             : 
    6059           4 :     attrelation = heap_open(AttributeRelationId, RowExclusiveLock);
    6060           4 :     tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
    6061           4 :     if (!HeapTupleIsValid(tuple))
    6062           0 :         ereport(ERROR,
    6063             :                 (errcode(ERRCODE_UNDEFINED_COLUMN),
    6064             :                  errmsg("column \"%s\" of relation \"%s\" does not exist",
    6065             :                         colName, RelationGetRelationName(rel))));
    6066             : 
    6067           4 :     attTup = (Form_pg_attribute) GETSTRUCT(tuple);
    6068           4 :     attnum = attTup->attnum;
    6069             : 
    6070           4 :     if (attnum <= 0)
    6071           0 :         ereport(ERROR,
    6072             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    6073             :                  errmsg("cannot alter system column \"%s\"",
    6074             :                         colName)));
    6075             : 
    6076           4 :     if (!attTup->attidentity)
    6077             :     {
    6078           2 :         if (!missing_ok)
    6079           1 :             ereport(ERROR,
    6080             :                     (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    6081             :                      errmsg("column \"%s\" of relation \"%s\" is not an identity column",
    6082             :                             colName, RelationGetRelationName(rel))));
    6083             :         else
    6084             :         {
    6085           1 :             ereport(NOTICE,
    6086             :                     (errmsg("column \"%s\" of relation \"%s\" is not an identity column, skipping",
    6087             :                             colName, RelationGetRelationName(rel))));
    6088           1 :             heap_freetuple(tuple);
    6089           1 :             heap_close(attrelation, RowExclusiveLock);
    6090           1 :             return InvalidObjectAddress;
    6091             :         }
    6092             :     }
    6093             : 
    6094           2 :     attTup->attidentity = '\0';
    6095           2 :     CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
    6096             : 
    6097           2 :     InvokeObjectPostAlterHook(RelationRelationId,
    6098             :                               RelationGetRelid(rel),
    6099             :                               attTup->attnum);
    6100           2 :     ObjectAddressSubSet(address, RelationRelationId,
    6101             :                         RelationGetRelid(rel), attnum);
    6102           2 :     heap_freetuple(tuple);
    6103             : 
    6104           2 :     heap_close(attrelation, RowExclusiveLock);
    6105             : 
    6106             :     /* drop the internal sequence */
    6107           2 :     seqid = getOwnedSequence(RelationGetRelid(rel), attnum);
    6108           2 :     deleteDependencyRecordsForClass(RelationRelationId, seqid,
    6109             :                                     RelationRelationId, DEPENDENCY_INTERNAL);
    6110           2 :     CommandCounterIncrement();
    6111           2 :     seqaddress.classId = RelationRelationId;
    6112           2 :     seqaddress.objectId = seqid;
    6113           2 :     seqaddress.objectSubId = 0;
    6114           2 :     performDeletion(&seqaddress, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
    6115             : 
    6116           2 :     return address;
    6117             : }
    6118             : 
    6119             : /*
    6120             :  * ALTER TABLE ALTER COLUMN SET STATISTICS
    6121             :  */
    6122             : static void
    6123          15 : ATPrepSetStatistics(Relation rel, const char *colName, Node *newValue, LOCKMODE lockmode)
    6124             : {
    6125             :     /*
    6126             :      * We do our own permission checking because (a) we want to allow SET
    6127             :      * STATISTICS on indexes (for expressional index columns), and (b) we want
    6128             :      * to allow SET STATISTICS on system catalogs without requiring
    6129             :      * allowSystemTableMods to be turned on.
    6130             :      */
    6131          21 :     if (rel->rd_rel->relkind != RELKIND_RELATION &&
    6132          12 :         rel->rd_rel->relkind != RELKIND_MATVIEW &&
    6133          12 :         rel->rd_rel->relkind != RELKIND_INDEX &&
    6134           6 :         rel->rd_rel->relkind != RELKIND_FOREIGN_TABLE &&
    6135           0 :         rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
    6136           0 :         ereport(ERROR,
    6137             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    6138             :                  errmsg("\"%s\" is not a table, materialized view, index, or foreign table",
    6139             :                         RelationGetRelationName(rel))));
    6140             : 
    6141             :     /* Permissions checks */
    6142          15 :     if (!pg_class_ownercheck(RelationGetRelid(rel), GetUserId()))
    6143           0 :         aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
    6144           0 :                        RelationGetRelationName(rel));
    6145          15 : }
    6146             : 
    6147             : /*
    6148             :  * Return value is the address of the modified column
    6149             :  */
    6150             : static ObjectAddress
    6151          15 : ATExecSetStatistics(Relation rel, const char *colName, Node *newValue, LOCKMODE lockmode)
    6152             : {
    6153             :     int         newtarget;
    6154             :     Relation    attrelation;
    6155             :     HeapTuple   tuple;
    6156             :     Form_pg_attribute attrtuple;
    6157             :     AttrNumber  attnum;
    6158             :     ObjectAddress address;
    6159             : 
    6160          15 :     Assert(IsA(newValue, Integer));
    6161          15 :     newtarget = intVal(newValue);
    6162             : 
    6163             :     /*
    6164             :      * Limit target to a sane range
    6165             :      */
    6166          15 :     if (newtarget < -1)
    6167             :     {
    6168           0 :         ereport(ERROR,
    6169             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    6170             :                  errmsg("statistics target %d is too low",
    6171             :                         newtarget)));
    6172             :     }
    6173          15 :     else if (newtarget > 10000)
    6174             :     {
    6175           0 :         newtarget = 10000;
    6176           0 :         ereport(WARNING,
    6177             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    6178             :                  errmsg("lowering statistics target to %d",
    6179             :                         newtarget)));
    6180             :     }
    6181             : 
    6182          15 :     attrelation = heap_open(AttributeRelationId, RowExclusiveLock);
    6183             : 
    6184          15 :     tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
    6185             : 
    6186          15 :     if (!HeapTupleIsValid(tuple))
    6187           2 :         ereport(ERROR,
    6188             :                 (errcode(ERRCODE_UNDEFINED_COLUMN),
    6189             :                  errmsg("column \"%s\" of relation \"%s\" does not exist",
    6190             :                         colName, RelationGetRelationName(rel))));
    6191          13 :     attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
    6192             : 
    6193          13 :     attnum = attrtuple->attnum;
    6194          13 :     if (attnum <= 0)
    6195           0 :         ereport(ERROR,
    6196             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    6197             :                  errmsg("cannot alter system column \"%s\"",
    6198             :                         colName)));
    6199             : 
    6200          13 :     attrtuple->attstattarget = newtarget;
    6201             : 
    6202          13 :     CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
    6203             : 
    6204          13 :     InvokeObjectPostAlterHook(RelationRelationId,
    6205             :                               RelationGetRelid(rel),
    6206             :                               attrtuple->attnum);
    6207          13 :     ObjectAddressSubSet(address, RelationRelationId,
    6208             :                         RelationGetRelid(rel), attnum);
    6209          13 :     heap_freetuple(tuple);
    6210             : 
    6211          13 :     heap_close(attrelation, RowExclusiveLock);
    6212             : 
    6213          13 :     return address;
    6214             : }
    6215             : 
    6216             : /*
    6217             :  * Return value is the address of the modified column
    6218             :  */
    6219             : static ObjectAddress
    6220           5 : ATExecSetOptions(Relation rel, const char *colName, Node *options,
    6221             :                  bool isReset, LOCKMODE lockmode)
    6222             : {
    6223             :     Relation    attrelation;
    6224             :     HeapTuple   tuple,
    6225             :                 newtuple;
    6226             :     Form_pg_attribute attrtuple;
    6227             :     AttrNumber  attnum;
    6228             :     Datum       datum,
    6229             :                 newOptions;
    6230             :     bool        isnull;
    6231             :     ObjectAddress address;
    6232             :     Datum       repl_val[Natts_pg_attribute];
    6233             :     bool        repl_null[Natts_pg_attribute];
    6234             :     bool        repl_repl[Natts_pg_attribute];
    6235             : 
    6236           5 :     attrelation = heap_open(AttributeRelationId, RowExclusiveLock);
    6237             : 
    6238           5 :     tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
    6239             : 
    6240           5 :     if (!HeapTupleIsValid(tuple))
    6241           0 :         ereport(ERROR,
    6242             :                 (errcode(ERRCODE_UNDEFINED_COLUMN),
    6243             :                  errmsg("column \"%s\" of relation \"%s\" does not exist",
    6244             :                         colName, RelationGetRelationName(rel))));
    6245           5 :     attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
    6246             : 
    6247           5 :     attnum = attrtuple->attnum;
    6248           5 :     if (attnum <= 0)
    6249           0 :         ereport(ERROR,
    6250             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    6251             :                  errmsg("cannot alter system column \"%s\"",
    6252             :                         colName)));
    6253             : 
    6254             :     /* Generate new proposed attoptions (text array) */
    6255           5 :     datum = SysCacheGetAttr(ATTNAME, tuple, Anum_pg_attribute_attoptions,
    6256             :                             &isnull);
    6257          10 :     newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
    6258           5 :                                      castNode(List, options), NULL, NULL,
    6259             :                                      false, isReset);
    6260             :     /* Validate new options */
    6261           5 :     (void) attribute_reloptions(newOptions, true);
    6262             : 
    6263             :     /* Build new tuple. */
    6264           5 :     memset(repl_null, false, sizeof(repl_null));
    6265           5 :     memset(repl_repl, false, sizeof(repl_repl));
    6266           5 :     if (newOptions != (Datum) 0)
    6267           5 :         repl_val[Anum_pg_attribute_attoptions - 1] = newOptions;
    6268             :     else
    6269           0 :         repl_null[Anum_pg_attribute_attoptions - 1] = true;
    6270           5 :     repl_repl[Anum_pg_attribute_attoptions - 1] = true;
    6271           5 :     newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrelation),
    6272             :                                  repl_val, repl_null, repl_repl);
    6273             : 
    6274             :     /* Update system catalog. */
    6275           5 :     CatalogTupleUpdate(attrelation, &newtuple->t_self, newtuple);
    6276             : 
    6277           5 :     InvokeObjectPostAlterHook(RelationRelationId,
    6278             :                               RelationGetRelid(rel),
    6279             :                               attrtuple->attnum);
    6280           5 :     ObjectAddressSubSet(address, RelationRelationId,
    6281             :                         RelationGetRelid(rel), attnum);
    6282             : 
    6283           5 :     heap_freetuple(newtuple);
    6284             : 
    6285           5 :     ReleaseSysCache(tuple);
    6286             : 
    6287           5 :     heap_close(attrelation, RowExclusiveLock);
    6288             : 
    6289           5 :     return address;
    6290             : }
    6291             : 
    6292             : /*
    6293             :  * ALTER TABLE ALTER COLUMN SET STORAGE
    6294             :  *
    6295             :  * Return value is the address of the modified column
    6296             :  */
    6297             : static ObjectAddress
    6298          17 : ATExecSetStorage(Relation rel, const char *colName, Node *newValue, LOCKMODE lockmode)
    6299             : {
    6300             :     char       *storagemode;
    6301             :     char        newstorage;
    6302             :     Relation    attrelation;
    6303             :     HeapTuple   tuple;
    6304             :     Form_pg_attribute attrtuple;
    6305             :     AttrNumber  attnum;
    6306             :     ObjectAddress address;
    6307             : 
    6308          17 :     Assert(IsA(newValue, String));
    6309          17 :     storagemode = strVal(newValue);
    6310             : 
    6311          17 :     if (pg_strcasecmp(storagemode, "plain") == 0)
    6312           4 :         newstorage = 'p';
    6313          13 :     else if (pg_strcasecmp(storagemode, "external") == 0)
    6314           9 :         newstorage = 'e';
    6315           4 :     else if (pg_strcasecmp(storagemode, "extended") == 0)
    6316           2 :         newstorage = 'x';
    6317           2 :     else if (pg_strcasecmp(storagemode, "main") == 0)
    6318           2 :         newstorage = 'm';
    6319             :     else
    6320             :     {
    6321           0 :         ereport(ERROR,
    6322             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    6323             :                  errmsg("invalid storage type \"%s\"",
    6324             :                         storagemode)));
    6325             :         newstorage = 0;         /* keep compiler quiet */
    6326             :     }
    6327             : 
    6328          17 :     attrelation = heap_open(AttributeRelationId, RowExclusiveLock);
    6329             : 
    6330          17 :     tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
    6331             : 
    6332          17 :     if (!HeapTupleIsValid(tuple))
    6333           2 :         ereport(ERROR,
    6334             :                 (errcode(ERRCODE_UNDEFINED_COLUMN),
    6335             :                  errmsg("column \"%s\" of relation \"%s\" does not exist",
    6336             :                         colName, RelationGetRelationName(rel))));
    6337          15 :     attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
    6338             : 
    6339          15 :     attnum = attrtuple->attnum;
    6340          15 :     if (attnum <= 0)
    6341           0 :         ereport(ERROR,
    6342             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    6343             :                  errmsg("cannot alter system column \"%s\"",
    6344             :                         colName)));
    6345             : 
    6346             :     /*
    6347             :      * safety check: do not allow toasted storage modes unless column datatype
    6348             :      * is TOAST-aware.
    6349             :      */
    6350          15 :     if (newstorage == 'p' || TypeIsToastable(attrtuple->atttypid))
    6351          15 :         attrtuple->attstorage = newstorage;
    6352             :     else
    6353           0 :         ereport(ERROR,
    6354             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    6355             :                  errmsg("column data type %s can only have storage PLAIN",
    6356             :                         format_type_be(attrtuple->atttypid))));
    6357             : 
    6358          15 :     CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
    6359             : 
    6360          15 :     InvokeObjectPostAlterHook(RelationRelationId,
    6361             :                               RelationGetRelid(rel),
    6362             :                               attrtuple->attnum);
    6363             : 
    6364          15 :     heap_freetuple(tuple);
    6365             : 
    6366          15 :     heap_close(attrelation, RowExclusiveLock);
    6367             : 
    6368          15 :     ObjectAddressSubSet(address, RelationRelationId,
    6369             :                         RelationGetRelid(rel), attnum);
    6370          15 :     return address;
    6371             : }
    6372             : 
    6373             : 
    6374             : /*
    6375             :  * ALTER TABLE DROP COLUMN
    6376             :  *
    6377             :  * DROP COLUMN cannot use the normal ALTER TABLE recursion mechanism,
    6378             :  * because we have to decide at runtime whether to recurse or not depending
    6379             :  * on whether attinhcount goes to zero or not.  (We can't check this in a
    6380             :  * static pre-pass because it won't handle multiple inheritance situations
    6381             :  * correctly.)
    6382             :  */
    6383             : static void
    6384         114 : ATPrepDropColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
    6385             :                  AlterTableCmd *cmd, LOCKMODE lockmode)
    6386             : {
    6387         114 :     if (rel->rd_rel->reloftype && !recursing)
    6388           1 :         ereport(ERROR,
    6389             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    6390             :                  errmsg("cannot drop column from typed table")));
    6391             : 
    6392         113 :     if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
    6393           8 :         ATTypedTableRecursion(wqueue, rel, cmd, lockmode);
    6394             : 
    6395         112 :     if (recurse)
    6396          99 :         cmd->subtype = AT_DropColumnRecurse;
    6397         112 : }
    6398             : 
    6399             : /*
    6400             :  * Checks if attnum is a partition attribute for rel
    6401             :  *
    6402             :  * Sets *used_in_expr if attnum is found to be referenced in some partition
    6403             :  * key expression.  It's possible for a column to be both used directly and
    6404             :  * as part of an expression; if that happens, *used_in_expr may end up as
    6405             :  * either true or false.  That's OK for current uses of this function, because
    6406             :  * *used_in_expr is only used to tailor the error message text.
    6407             :  */
    6408             : static bool
    6409         208 : is_partition_attr(Relation rel, AttrNumber attnum, bool *used_in_expr)
    6410             : {
    6411             :     PartitionKey key;
    6412             :     int         partnatts;
    6413             :     List       *partexprs;
    6414             :     ListCell   *partexprs_item;
    6415             :     int         i;
    6416             : 
    6417         208 :     if (rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
    6418         198 :         return false;
    6419             : 
    6420          10 :     key = RelationGetPartitionKey(rel);
    6421          10 :     partnatts = get_partition_natts(key);
    6422          10 :     partexprs = get_partition_exprs(key);
    6423             : 
    6424          10 :     partexprs_item = list_head(partexprs);
    6425          16 :     for (i = 0; i < partnatts; i++)
    6426             :     {
    6427          12 :         AttrNumber  partattno = get_partition_col_attnum(key, i);
    6428             : 
    6429          12 :         if (partattno != 0)
    6430             :         {
    6431          10 :             if (attnum == partattno)
    6432             :             {
    6433           4 :                 if (used_in_expr)
    6434           4 :                     *used_in_expr = false;
    6435           4 :                 return true;
    6436             :             }
    6437             :         }
    6438             :         else
    6439             :         {
    6440             :             /* Arbitrary expression */
    6441           2 :             Node       *expr = (Node *) lfirst(partexprs_item);
    6442           2 :             Bitmapset  *expr_attrs = NULL;
    6443             : 
    6444             :             /* Find all attributes referenced */
    6445           2 :             pull_varattnos(expr, 1, &expr_attrs);
    6446           2 :             partexprs_item = lnext(partexprs_item);
    6447             : 
    6448           2 :             if (bms_is_member(attnum - FirstLowInvalidHeapAttributeNumber,
    6449             :                               expr_attrs))
    6450             :             {
    6451           2 :                 if (used_in_expr)
    6452           2 :                     *used_in_expr = true;
    6453           2 :                 return true;
    6454             :             }
    6455             :         }
    6456             :     }
    6457             : 
    6458           4 :     return false;
    6459             : }
    6460             : 
    6461             : /*
    6462             :  * Return value is the address of the dropped column.
    6463             :  */
    6464             : static ObjectAddress
    6465         140 : ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
    6466             :                  DropBehavior behavior,
    6467             :                  bool recurse, bool recursing,
    6468             :                  bool missing_ok, LOCKMODE lockmode)
    6469             : {
    6470             :     HeapTuple   tuple;
    6471             :     Form_pg_attribute targetatt;
    6472             :     AttrNumber  attnum;
    6473             :     List       *children;
    6474             :     ObjectAddress object;
    6475             :     bool        is_expr;
    6476             : 
    6477             :     /* At top level, permission check was done in ATPrepCmd, else do it */
    6478         140 :     if (recursing)
    6479          28 :         ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);
    6480             : 
    6481             :     /*
    6482             :      * get the number of the attribute
    6483             :      */
    6484         140 :     tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
    6485         140 :     if (!HeapTupleIsValid(tuple))
    6486             :     {
    6487           9 :         if (!missing_ok)
    6488             :         {
    6489           6 :             ereport(ERROR,
    6490             :                     (errcode(ERRCODE_UNDEFINED_COLUMN),
    6491             :                      errmsg("column \"%s\" of relation \"%s\" does not exist",
    6492             :                             colName, RelationGetRelationName(rel))));
    6493             :         }
    6494             :         else
    6495             :         {
    6496           3 :             ereport(NOTICE,
    6497             :                     (errmsg("column \"%s\" of relation \"%s\" does not exist, skipping",
    6498             :                             colName, RelationGetRelationName(rel))));
    6499           3 :             return InvalidObjectAddress;
    6500             :         }
    6501             :     }
    6502         131 :     targetatt = (Form_pg_attribute) GETSTRUCT(tuple);
    6503             : 
    6504         131 :     attnum = targetatt->attnum;
    6505             : 
    6506             :     /* Can't drop a system attribute, except OID */
    6507         131 :     if (attnum <= 0 && attnum != ObjectIdAttributeNumber)
    6508           1 :         ereport(ERROR,
    6509             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    6510             :                  errmsg("cannot drop system column \"%s\"",
    6511             :                         colName)));
    6512             : 
    6513             :     /* Don't drop inherited columns */
    6514         130 :     if (targetatt->attinhcount > 0 && !recursing)
    6515          10 :         ereport(ERROR,
    6516             :                 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    6517             :                  errmsg("cannot drop inherited column \"%s\"",
    6518             :                         colName)));
    6519             : 
    6520             :     /* Don't drop columns used in the partition key */
    6521         120 :     if (is_partition_attr(rel, attnum, &is_expr))
    6522             :     {
    6523           3 :         if (!is_expr)
    6524           2 :             ereport(ERROR,
    6525             :                     (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    6526             :                      errmsg("cannot drop column named in partition key")));
    6527             :         else
    6528           1 :             ereport(ERROR,
    6529             :                     (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    6530             :                      errmsg("cannot drop column referenced in partition key expression")));
    6531             :     }
    6532             : 
    6533         117 :     ReleaseSysCache(tuple);
    6534             : 
    6535             :     /*
    6536             :      * Propagate to children as appropriate.  Unlike most other ALTER
    6537             :      * routines, we have to do this one level of recursion at a time; we can't
    6538             :      * use find_all_inheritors to do it in one pass.
    6539             :      */
    6540         117 :     children = find_inheritance_children(RelationGetRelid(rel), lockmode);
    6541             : 
    6542         117 :     if (children)
    6543             :     {
    6544             :         Relation    attr_rel;
    6545             :         ListCell   *child;
    6546             : 
    6547             :         /*
    6548             :          * In case of a partitioned table, the column must be dropped from the
    6549             :          * partitions as well.
    6550             :          */
    6551          31 :         if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && !recurse)
    6552           1 :             ereport(ERROR,
    6553             :                     (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    6554             :                      errmsg("cannot drop column from only the partitioned table when partitions exist"),
    6555             :                      errhint("Do not specify the ONLY keyword.")));
    6556             : 
    6557          30 :         attr_rel = heap_open(AttributeRelationId, RowExclusiveLock);
    6558          65 :         foreach(child, children)
    6559             :         {
    6560          36 :             Oid         childrelid = lfirst_oid(child);
    6561             :             Relation    childrel;
    6562             :             Form_pg_attribute childatt;
    6563             : 
    6564             :             /* find_inheritance_children already got lock */
    6565          36 :             childrel = heap_open(childrelid, NoLock);
    6566          36 :             CheckTableNotInUse(childrel, "ALTER TABLE");
    6567             : 
    6568          36 :             tuple = SearchSysCacheCopyAttName(childrelid, colName);
    6569          36 :             if (!HeapTupleIsValid(tuple))   /* shouldn't happen */
    6570           0 :                 elog(ERROR, "cache lookup failed for attribute \"%s\" of relation %u",
    6571             :                      colName, childrelid);
    6572          36 :             childatt = (Form_pg_attribute) GETSTRUCT(tuple);
    6573             : 
    6574          36 :             if (childatt->attinhcount <= 0) /* shouldn't happen */
    6575           0 :                 elog(ERROR, "relation %u has non-inherited attribute \"%s\"",
    6576             :                      childrelid, colName);
    6577             : 
    6578          36 :             if (recurse)
    6579             :             {
    6580             :                 /*
    6581             :                  * If the child column has other definition sources, just
    6582             :                  * decrement its inheritance count; if not, recurse to delete
    6583             :                  * it.
    6584             :                  */
    6585          32 :                 if (childatt->attinhcount == 1 && !childatt->attislocal)
    6586             :                 {
    6587             :                     /* Time to delete this child column, too */
    6588          28 :                     ATExecDropColumn(wqueue, childrel, colName,
    6589             :                                      behavior, true, true,
    6590             :                                      false, lockmode);
    6591             :                 }
    6592             :                 else
    6593             :                 {
    6594             :                     /* Child column must survive my deletion */
    6595           4 :                     childatt->attinhcount--;
    6596             : 
    6597           4 :                     CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
    6598             : 
    6599             :                     /* Make update visible */
    6600           4 :                     CommandCounterIncrement();
    6601             :                 }
    6602             :             }
    6603             :             else
    6604             :             {
    6605             :                 /*
    6606             :                  * If we were told to drop ONLY in this table (no recursion),
    6607             :                  * we need to mark the inheritors' attributes as locally
    6608             :                  * defined rather than inherited.
    6609             :                  */
    6610           4 :                 childatt->attinhcount--;
    6611           4 :                 childatt->attislocal = true;
    6612             : 
    6613           4 :                 CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
    6614             : 
    6615             :                 /* Make update visible */
    6616           4 :                 CommandCounterIncrement();
    6617             :             }
    6618             : 
    6619          35 :             heap_freetuple(tuple);
    6620             : 
    6621          35 :             heap_close(childrel, NoLock);
    6622             :         }
    6623          29 :         heap_close(attr_rel, RowExclusiveLock);
    6624             :     }
    6625             : 
    6626             :     /*
    6627             :      * Perform the actual column deletion
    6628             :      */
    6629         115 :     object.classId = RelationRelationId;
    6630         115 :     object.objectId = RelationGetRelid(rel);
    6631         115 :     object.objectSubId = attnum;
    6632             : 
    6633         115 :     performDeletion(&object, behavior, 0);
    6634             : 
    6635             :     /*
    6636             :      * If we dropped the OID column, must adjust pg_class.relhasoids and tell
    6637             :      * Phase 3 to physically get rid of the column.  We formerly left the
    6638             :      * column in place physically, but this caused subtle problems.  See
    6639             :      * http://archives.postgresql.org/pgsql-hackers/2009-02/msg00363.php
    6640             :      */
    6641         114 :     if (attnum == ObjectIdAttributeNumber)
    6642             :     {
    6643             :         Relation    class_rel;
    6644             :         Form_pg_class tuple_class;
    6645             :         AlteredTableInfo *tab;
    6646             : 
    6647          11 :         class_rel = heap_open(RelationRelationId, RowExclusiveLock);
    6648             : 
    6649          11 :         tuple = SearchSysCacheCopy1(RELOID,
    6650             :                                     ObjectIdGetDatum(RelationGetRelid(rel)));
    6651          11 :         if (!HeapTupleIsValid(tuple))
    6652           0 :             elog(ERROR, "cache lookup failed for relation %u",
    6653             :                  RelationGetRelid(rel));
    6654          11 :         tuple_class = (Form_pg_class) GETSTRUCT(tuple);
    6655             : 
    6656          11 :         tuple_class->relhasoids = false;
    6657          11 :         CatalogTupleUpdate(class_rel, &tuple->t_self, tuple);
    6658             : 
    6659          11 :         heap_close(class_rel, RowExclusiveLock);
    6660             : 
    6661             :         /* Find or create work queue entry for this table */
    6662          11 :         tab = ATGetQueueEntry(wqueue, rel);
    6663             : 
    6664             :         /* Tell Phase 3 to physically remove the OID column */
    6665          11 :         tab->rewrite |= AT_REWRITE_ALTER_OID;
    6666             :     }
    6667             : 
    6668         114 :     return object;
    6669             : }
    6670             : 
    6671             : /*
    6672             :  * ALTER TABLE ADD INDEX
    6673             :  *
    6674             :  * There is no such command in the grammar, but parse_utilcmd.c converts
    6675             :  * UNIQUE and PRIMARY KEY constraints into AT_AddIndex subcommands.  This lets
    6676             :  * us schedule creation of the index at the appropriate time during ALTER.
    6677             :  *
    6678             :  * Return value is the address of the new index.
    6679             :  */
    6680             : static ObjectAddress
    6681          53 : ATExecAddIndex(AlteredTableInfo *tab, Relation rel,
    6682             :                IndexStmt *stmt, bool is_rebuild, LOCKMODE lockmode)
    6683             : {
    6684             :     bool        check_rights;
    6685             :     bool        skip_build;
    6686             :     bool        quiet;
    6687             :     ObjectAddress address;
    6688             : 
    6689          53 :     Assert(IsA(stmt, IndexStmt));
    6690          53 :     Assert(!stmt->concurrent);
    6691             : 
    6692             :     /* The IndexStmt has already been through transformIndexStmt */
    6693          53 :     Assert(stmt->transformed);
    6694             : 
    6695             :     /* suppress schema rights check when rebuilding existing index */
    6696          53 :     check_rights = !is_rebuild;
    6697             :     /* skip index build if phase 3 will do it or we're reusing an old one */
    6698          53 :     skip_build = tab->rewrite > 0 || OidIsValid(stmt->oldNode);
    6699             :     /* suppress notices when rebuilding existing index */
    6700          53 :     quiet = is_rebuild;
    6701             : 
    6702          53 :     address = DefineIndex(RelationGetRelid(rel),
    6703             :                           stmt,
    6704             :                           InvalidOid,   /* no predefined OID */
    6705             :                           true, /* is_alter_table */
    6706             :                           check_rights,
    6707             :                           false,    /* check_not_in_use - we did it already */
    6708             :                           skip_build,
    6709             :                           quiet);
    6710             : 
    6711             :     /*
    6712             :      * If TryReuseIndex() stashed a relfilenode for us, we used it for the new
    6713             :      * index instead of building from scratch.  The DROP of the old edition of
    6714             :      * this index will have scheduled the storage for deletion at commit, so
    6715             :      * cancel that pending deletion.
    6716             :      */
    6717          40 :     if (OidIsValid(stmt->oldNode))
    6718             :     {
    6719           7 :         Relation    irel = index_open(address.objectId, NoLock);
    6720             : 
    6721           7 :         RelationPreserveStorage(irel->rd_node, true);
    6722           7 :         index_close(irel, NoLock);
    6723             :     }
    6724             : 
    6725          40 :     return address;
    6726             : }
    6727             : 
    6728             : /*
    6729             :  * ALTER TABLE ADD CONSTRAINT USING INDEX
    6730             :  *
    6731             :  * Returns the address of the new constraint.
    6732             :  */
    6733             : static ObjectAddress
    6734           2 : ATExecAddIndexConstraint(AlteredTableInfo *tab, Relation rel,
    6735             :                          IndexStmt *stmt, LOCKMODE lockmode)
    6736             : {
    6737           2 :     Oid         index_oid = stmt->indexOid;
    6738             :     Relation    indexRel;
    6739             :     char       *indexName;
    6740             :     IndexInfo  *indexInfo;
    6741             :     char       *constraintName;
    6742             :     char        constraintType;
    6743             :     ObjectAddress address;
    6744             : 
    6745           2 :     Assert(IsA(stmt, IndexStmt));
    6746           2 :     Assert(OidIsValid(index_oid));
    6747           2 :     Assert(stmt->isconstraint);
    6748             : 
    6749           2 :     indexRel = index_open(index_oid, AccessShareLock);
    6750             : 
    6751           2 :     indexName = pstrdup(RelationGetRelationName(indexRel));
    6752             : 
    6753           2 :     indexInfo = BuildIndexInfo(indexRel);
    6754             : 
    6755             :     /* this should have been checked at parse time */
    6756           2 :     if (!indexInfo->ii_Unique)
    6757           0 :         elog(ERROR, "index \"%s\" is not unique", indexName);
    6758             : 
    6759             :     /*
    6760             :      * Determine name to assign to constraint.  We require a constraint to
    6761             :      * have the same name as the underlying index; therefore, use the index's
    6762             :      * existing name as the default constraint name, and if the user
    6763             :      * explicitly gives some other name for the constraint, rename the index
    6764             :      * to match.
    6765             :      */
    6766           2 :     constraintName = stmt->idxname;
    6767           2 :     if (constraintName == NULL)
    6768           1 :         constraintName = indexName;
    6769           1 :     else if (strcmp(constraintName, indexName) != 0)
    6770             :     {
    6771           1 :         ereport(NOTICE,
    6772             :                 (errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX will rename index \"%s\" to \"%s\"",
    6773             :                         indexName, constraintName)));
    6774           1 :         RenameRelationInternal(index_oid, constraintName, false);
    6775             :     }
    6776             : 
    6777             :     /* Extra checks needed if making primary key */
    6778           2 :     if (stmt->primary)
    6779           2 :         index_check_primary_key(rel, indexInfo, true);
    6780             : 
    6781             :     /* Note we currently don't support EXCLUSION constraints here */
    6782           2 :     if (stmt->primary)
    6783           2 :         constraintType = CONSTRAINT_PRIMARY;
    6784             :     else
    6785           0 :         constraintType = CONSTRAINT_UNIQUE;
    6786             : 
    6787             :     /* Create the catalog entries for the constraint */
    6788           8 :     address = index_constraint_create(rel,
    6789             :                                       index_oid,
    6790             :                                       indexInfo,
    6791             :                                       constraintName,
    6792             :                                       constraintType,
    6793           2 :                                       stmt->deferrable,
    6794           2 :                                       stmt->initdeferred,
    6795           2 :                                       stmt->primary,
    6796             :                                       true, /* update pg_index */
    6797             :                                       true, /* remove old dependencies */
    6798             :                                       allowSystemTableMods,
    6799             :                                       false);   /* is_internal */
    6800             : 
    6801           2 :     index_close(indexRel, NoLock);
    6802             : 
    6803           2 :     return address;
    6804             : }
    6805             : 
    6806             : /*
    6807             :  * ALTER TABLE ADD CONSTRAINT
    6808             :  *
    6809             :  * Return value is the address of the new constraint; if no constraint was
    6810             :  * added, InvalidObjectAddress is returned.
    6811             :  */
    6812             : static ObjectAddress
    6813         217 : ATExecAddConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
    6814             :                     Constraint *newConstraint, bool recurse, bool is_readd,
    6815             :                     LOCKMODE lockmode)
    6816             : {
    6817         217 :     ObjectAddress address = InvalidObjectAddress;
    6818             : 
    6819         217 :     Assert(IsA(newConstraint, Constraint));
    6820             : 
    6821             :     /*
    6822             :      * Currently, we only expect to see CONSTR_CHECK and CONSTR_FOREIGN nodes
    6823             :      * arriving here (see the preprocessing done in parse_utilcmd.c).  Use a
    6824             :      * switch anyway to make it easier to add more code later.
    6825             :      */
    6826         217 :     switch (newConstraint->contype)
    6827             :     {
    6828             :         case CONSTR_CHECK:
    6829          79 :             address =
    6830          79 :                 ATAddCheckConstraint(wqueue, tab, rel,
    6831             :                                      newConstraint, recurse, false, is_readd,
    6832             :                                      lockmode);
    6833          71 :             break;
    6834             : 
    6835             :         case CONSTR_FOREIGN:
    6836             : 
    6837             :             /*
    6838             :              * Note that we currently never recurse for FK constraints, so the
    6839             :              * "recurse" flag is silently ignored.
    6840             :              *
    6841             :              * Assign or validate constraint name
    6842             :              */
    6843         138 :             if (newConstraint->conname)
    6844             :             {
    6845          40 :                 if (ConstraintNameIsUsed(CONSTRAINT_RELATION,
    6846             :                                          RelationGetRelid(rel),
    6847          40 :                                          RelationGetNamespace(rel),
    6848          40 :                                          newConstraint->conname))
    6849           0 :                     ereport(ERROR,
    6850             :                             (errcode(ERRCODE_DUPLICATE_OBJECT),
    6851             :                              errmsg("constraint \"%s\" for relation \"%s\" already exists",
    6852             :                                     newConstraint->conname,
    6853             :                                     RelationGetRelationName(rel))));
    6854             :             }
    6855             :             else
    6856          98 :                 newConstraint->conname =
    6857         196 :                     ChooseConstraintName(RelationGetRelationName(rel),
    6858          98 :                                          strVal(linitial(newConstraint->fk_attrs)),
    6859             :                                          "fkey",
    6860          98 :                                          RelationGetNamespace(rel),
    6861             :                                          NIL);
    6862             : 
    6863         138 :             address = ATAddForeignKeyConstraint(tab, rel, newConstraint,
    6864             :                                                 lockmode);
    6865          93 :             break;
    6866             : 
    6867             :         default:
    6868           0 :             elog(ERROR, "unrecognized constraint type: %d",
    6869             :                  (int) newConstraint->contype);
    6870             :     }
    6871             : 
    6872         164 :     return address;
    6873             : }
    6874             : 
    6875             : /*
    6876             :  * Add a check constraint to a single table and its children.  Returns the
    6877             :  * address of the constraint added to the parent relation, if one gets added,
    6878             :  * or InvalidObjectAddress otherwise.
    6879             :  *
    6880             :  * Subroutine for ATExecAddConstraint.
    6881             :  *
    6882             :  * We must recurse to child tables during execution, rather than using
    6883             :  * ALTER TABLE's normal prep-time recursion.  The reason is that all the
    6884             :  * constraints *must* be given the same name, else they won't be seen as
    6885             :  * related later.  If the user didn't explicitly specify a name, then
    6886             :  * AddRelationNewConstraints would normally assign different names to the
    6887             :  * child constraints.  To fix that, we must capture the name assigned at
    6888             :  * the parent table and pass that down.
    6889             :  */
    6890             : static ObjectAddress
    6891         129 : ATAddCheckConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
    6892             :                      Constraint *constr, bool recurse, bool recursing,
    6893             :                      bool is_readd, LOCKMODE lockmode)
    6894             : {
    6895             :     List       *newcons;
    6896             :     ListCell   *lcon;
    6897             :     List       *children;
    6898             :     ListCell   *child;
    6899         129 :     ObjectAddress address = InvalidObjectAddress;
    6900             : 
    6901             :     /* At top level, permission check was done in ATPrepCmd, else do it */
    6902         129 :     if (recursing)
    6903          50 :         ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);
    6904             : 
    6905             :     /*
    6906             :      * Call AddRelationNewConstraints to do the work, making sure it works on
    6907             :      * a copy of the Constraint so transformExpr can't modify the original. It
    6908             :      * returns a list of cooked constraints.
    6909             :      *
    6910             :      * If the constraint ends up getting merged with a pre-existing one, it's
    6911             :      * omitted from the returned list, which is what we want: we do not need
    6912             :      * to do any validation work.  That can only happen at child tables,
    6913             :      * though, since we disallow merging at the top level.
    6914             :      */
    6915         129 :     newcons = AddRelationNewConstraints(rel, NIL,
    6916             :                                         list_make1(copyObject(constr)),
    6917             :                                         recursing | is_readd,   /* allow_merge */
    6918             :                                         !recursing, /* is_local */
    6919             :                                         is_readd);  /* is_internal */
    6920             : 
    6921             :     /* we don't expect more than one constraint here */
    6922         122 :     Assert(list_length(newcons) <= 1);
    6923             : 
    6924             :     /* Add each to-be-validated constraint to Phase 3's queue */
    6925         237 :     foreach(lcon, newcons)
    6926             :     {
    6927         115 :         CookedConstraint *ccon = (CookedConstraint *) lfirst(lcon);
    6928             : 
    6929         115 :         if (!ccon->skip_validation)
    6930             :         {
    6931             :             NewConstraint *newcon;
    6932             : 
    6933          95 :             newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
    6934          95 :             newcon->name = ccon->name;
    6935          95 :             newcon->contype = ccon->contype;
    6936          95 :             newcon->qual = ccon->expr;
    6937             : 
    6938          95 :             tab->constraints = lappend(tab->constraints, newcon);
    6939             :         }
    6940             : 
    6941             :         /* Save the actually assigned name if it was defaulted */
    6942         115 :         if (constr->conname == NULL)
    6943          13 :             constr->conname = ccon->name;
    6944             : 
    6945         115 :         ObjectAddressSet(address, ConstraintRelationId, ccon->conoid);
    6946             :     }
    6947             : 
    6948             :     /* At this point we must have a locked-down name to use */
    6949         122 :     Assert(constr->conname != NULL);
    6950             : 
    6951             :     /* Advance command counter in case same table is visited multiple times */
    6952         122 :     CommandCounterIncrement();
    6953             : 
    6954             :     /*
    6955             :      * If the constraint got merged with an existing constraint, we're done.
    6956             :      * We mustn't recurse to child tables in this case, because they've
    6957             :      * already got the constraint, and visiting them again would lead to an
    6958             :      * incorrect value for coninhcount.
    6959             :      */
    6960         122 :     if (newcons == NIL)
    6961           7 :         return address;
    6962             : 
    6963             :     /*
    6964             :      * If adding a NO INHERIT constraint, no need to find our children.
    6965             :      */
    6966         115 :     if (constr->is_no_inherit)
    6967           8 :         return address;
    6968             : 
    6969             :     /*
    6970             :      * Propagate to children as appropriate.  Unlike most other ALTER
    6971             :      * routines, we have to do this one level of recursion at a time; we can't
    6972             :      * use find_all_inheritors to do it in one pass.
    6973             :      */
    6974         107 :     children = find_inheritance_children(RelationGetRelid(rel), lockmode);
    6975             : 
    6976             :     /*
    6977             :      * Check if ONLY was specified with ALTER TABLE.  If so, allow the
    6978             :      * constraint creation only if there are no children currently.  Error out
    6979             :      * otherwise.
    6980             :      */
    6981         107 :     if (!recurse && children != NIL)
    6982           1 :         ereport(ERROR,
    6983             :                 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    6984             :                  errmsg("constraint must be added to child tables too")));
    6985             : 
    6986         155 :     foreach(child, children)
    6987             :     {
    6988          50 :         Oid         childrelid = lfirst_oid(child);
    6989             :         Relation    childrel;
    6990             :         AlteredTableInfo *childtab;
    6991             : 
    6992             :         /* find_inheritance_children already got lock */
    6993          50 :         childrel = heap_open(childrelid, NoLock);
    6994          50 :         CheckTableNotInUse(childrel, "ALTER TABLE");
    6995             : 
    6996             :         /* Find or create work queue entry for this table */
    6997          50 :         childtab = ATGetQueueEntry(wqueue, childrel);
    6998             : 
    6999             :         /* Recurse to child */
    7000          50 :         ATAddCheckConstraint(wqueue, childtab, childrel,
    7001             :                              constr, recurse, true, is_readd, lockmode);
    7002             : 
    7003          49 :         heap_close(childrel, NoLock);
    7004             :     }
    7005             : 
    7006         105 :     return address;
    7007             : }
    7008             : 
    7009             : /*
    7010             :  * Add a foreign-key constraint to a single table; return the new constraint's
    7011             :  * address.
    7012             :  *
    7013             :  * Subroutine for ATExecAddConstraint.  Must already hold exclusive
    7014             :  * lock on the rel, and have done appropriate validity checks for it.
    7015             :  * We do permissions checks here, however.
    7016             :  */
    7017             : static ObjectAddress
    7018         138 : ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
    7019             :                           Constraint *fkconstraint, LOCKMODE lockmode)
    7020             : {
    7021             :     Relation    pkrel;
    7022             :     int16       pkattnum[INDEX_MAX_KEYS];
    7023             :     int16       fkattnum[INDEX_MAX_KEYS];
    7024             :     Oid         pktypoid[INDEX_MAX_KEYS];
    7025             :     Oid         fktypoid[INDEX_MAX_KEYS];
    7026             :     Oid         opclasses[INDEX_MAX_KEYS];
    7027             :     Oid         pfeqoperators[INDEX_MAX_KEYS];
    7028             :     Oid         ppeqoperators[INDEX_MAX_KEYS];
    7029             :     Oid         ffeqoperators[INDEX_MAX_KEYS];
    7030             :     int         i;
    7031             :     int         numfks,
    7032             :                 numpks;
    7033             :     Oid         indexOid;
    7034             :     Oid         constrOid;
    7035             :     bool        old_check_ok;
    7036             :     ObjectAddress address;
    7037         138 :     ListCell   *old_pfeqop_item = list_head(fkconstraint->old_conpfeqop);
    7038             : 
    7039             :     /*
    7040             :      * Grab ShareRowExclusiveLock on the pk table, so that someone doesn't
    7041             :      * delete rows out from under us.
    7042             :      */
    7043         138 :     if (OidIsValid(fkconstraint->old_pktable_oid))
    7044           4 :         pkrel = heap_open(fkconstraint->old_pktable_oid, ShareRowExclusiveLock);
    7045             :     else
    7046         134 :         pkrel = heap_openrv(fkconstraint->pktable, ShareRowExclusiveLock);
    7047             : 
    7048             :     /*
    7049             :      * Validity checks (permission checks wait till we have the column
    7050             :      * numbers)
    7051             :      */
    7052         138 :     if (pkrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
    7053           0 :         ereport(ERROR,
    7054             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    7055             :                  errmsg("cannot reference partitioned table \"%s\"",
    7056             :                         RelationGetRelationName(pkrel))));
    7057             : 
    7058         138 :     if (pkrel->rd_rel->relkind != RELKIND_RELATION)
    7059           0 :         ereport(ERROR,
    7060             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    7061             :                  errmsg("referenced relation \"%s\" is not a table",
    7062             :                         RelationGetRelationName(pkrel))));
    7063             : 
    7064         138 :     if (!allowSystemTableMods && IsSystemRelation(pkrel))
    7065           0 :         ereport(ERROR,
    7066             :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
    7067             :                  errmsg("permission denied: \"%s\" is a system catalog",
    7068             :                         RelationGetRelationName(pkrel))));
    7069             : 
    7070             :     /*
    7071             :      * References from permanent or unlogged tables to temp tables, and from
    7072             :      * permanent tables to unlogged tables, are disallowed because the
    7073             :      * referenced data can vanish out from under us.  References from temp
    7074             :      * tables to any other table type are also disallowed, because other
    7075             :      * backends might need to run the RI triggers on the perm table, but they
    7076             :      * can't reliably see tuples in the local buffers of other backends.
    7077             :      */
    7078         138 :     switch (rel->rd_rel->relpersistence)
    7079             :     {
    7080             :         case RELPERSISTENCE_PERMANENT:
    7081          89 :             if (pkrel->rd_rel->relpersistence != RELPERSISTENCE_PERMANENT)
    7082           0 :                 ereport(ERROR,
    7083             :                         (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    7084             :                          errmsg("constraints on permanent tables may reference only permanent tables")));
    7085          89 :             break;
    7086             :         case RELPERSISTENCE_UNLOGGED:
    7087           2 :             if (pkrel->rd_rel->relpersistence != RELPERSISTENCE_PERMANENT
    7088           2 :                 && pkrel->rd_rel->relpersistence != RELPERSISTENCE_UNLOGGED)
    7089           0 :                 ereport(ERROR,
    7090             :                         (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    7091             :                          errmsg("constraints on unlogged tables may reference only permanent or unlogged tables")));
    7092           2 :             break;
    7093             :         case RELPERSISTENCE_TEMP:
    7094          47 :             if (pkrel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
    7095           0 :                 ereport(ERROR,
    7096             :                         (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    7097             :                          errmsg("constraints on temporary tables may reference only temporary tables")));
    7098          47 :             if (!pkrel->rd_islocaltemp || !rel->rd_islocaltemp)
    7099           0 :                 ereport(ERROR,
    7100             :                         (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    7101             :                          errmsg("constraints on temporary tables must involve temporary tables of this session")));
    7102          47 :             break;
    7103             :     }
    7104             : 
    7105             :     /*
    7106             :      * Look up the referencing attributes to make sure they exist, and record
    7107             :      * their attnums and type OIDs.
    7108             :      */
    7109         138 :     MemSet(pkattnum, 0, sizeof(pkattnum));
    7110         138 :     MemSet(fkattnum, 0, sizeof(fkattnum));
    7111         138 :     MemSet(pktypoid, 0, sizeof(pktypoid));
    7112         138 :     MemSet(fktypoid, 0, sizeof(fktypoid));
    7113         138 :     MemSet(opclasses, 0, sizeof(opclasses));
    7114         138 :     MemSet(pfeqoperators, 0, sizeof(pfeqoperators));
    7115         138 :     MemSet(ppeqoperators, 0, sizeof(ppeqoperators));
    7116         138 :     MemSet(ffeqoperators, 0, sizeof(ffeqoperators));
    7117             : 
    7118         138 :     numfks = transformColumnNameList(RelationGetRelid(rel),
    7119             :                                      fkconstraint->fk_attrs,
    7120             :                                      fkattnum, fktypoid);
    7121             : 
    7122             :     /*
    7123             :      * If the attribute list for the referenced table was omitted, lookup the
    7124             :      * definition of the primary key and use it.  Otherwise, validate the
    7125             :      * supplied attribute list.  In either case, discover the index OID and
    7126             :      * index opclasses, and the attnums and type OIDs of the attributes.
    7127             :      */
    7128         134 :     if (fkconstraint->pk_attrs == NIL)
    7129             :     {
    7130          73 :         numpks = transformFkeyGetPrimaryKey(pkrel, &indexOid,
    7131             :                                             &fkconstraint->pk_attrs,
    7132             :                                             pkattnum, pktypoid,
    7133             :                                             opclasses);
    7134             :     }
    7135             :     else
    7136             :     {
    7137          61 :         numpks = transformColumnNameList(RelationGetRelid(pkrel),
    7138             :                                          fkconstraint->pk_attrs,
    7139             :                                          pkattnum, pktypoid);
    7140             :         /* Look for an index matching the column list */
    7141          57 :         indexOid = transformFkeyCheckAttrs(pkrel, numpks, pkattnum,
    7142             :                                            opclasses);
    7143             :     }
    7144             : 
    7145             :     /*
    7146             :      * Now we can check permissions.
    7147             :      */
    7148         128 :     checkFkeyPermissions(pkrel, pkattnum, numpks);
    7149             : 
    7150             :     /*
    7151             :      * Look up the equality operators to use in the constraint.
    7152             :      *
    7153             :      * Note that we have to be careful about the difference between the actual
    7154             :      * PK column type and the opclass' declared input type, which might be
    7155             :      * only binary-compatible with it.  The declared opcintype is the right
    7156             :      * thing to probe pg_amop with.
    7157             :      */
    7158         128 :     if (numfks != numpks)
    7159           0 :         ereport(ERROR,
    7160             :                 (errcode(ERRCODE_INVALID_FOREIGN_KEY),
    7161             :                  errmsg("number of referencing and referenced columns for foreign key disagree")));
    7162             : 
    7163             :     /*
    7164             :      * On the strength of a previous constraint, we might avoid scanning
    7165             :      * tables to validate this one.  See below.
    7166             :      */
    7167         128 :     old_check_ok = (fkconstraint->old_conpfeqop != NIL);
    7168         128 :     Assert(!old_check_ok || numfks == list_length(fkconstraint->old_conpfeqop));
    7169             : 
    7170         249 :     for (i = 0; i < numpks; i++)
    7171             :     {
    7172         156 :         Oid         pktype = pktypoid[i];
    7173         156 :         Oid         fktype = fktypoid[i];
    7174             :         Oid         fktyped;
    7175             :         HeapTuple   cla_ht;
    7176             :         Form_pg_opclass cla_tup;
    7177             :         Oid         amid;
    7178             :         Oid         opfamily;
    7179             :         Oid         opcintype;
    7180             :         Oid         pfeqop;
    7181             :         Oid         ppeqop;
    7182             :         Oid         ffeqop;
    7183             :         int16       eqstrategy;
    7184             :         Oid         pfeqop_right;
    7185             : 
    7186             :         /* We need several fields out of the pg_opclass entry */
    7187         156 :         cla_ht = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclasses[i]));
    7188         156 :         if (!HeapTupleIsValid(cla_ht))
    7189           0 :             elog(ERROR, "cache lookup failed for opclass %u", opclasses[i]);
    7190         156 :         cla_tup = (Form_pg_opclass) GETSTRUCT(cla_ht);
    7191         156 :         amid = cla_tup->opcmethod;
    7192         156 :         opfamily = cla_tup->opcfamily;
    7193         156 :         opcintype = cla_tup->opcintype;
    7194         156 :         ReleaseSysCache(cla_ht);
    7195             : 
    7196             :         /*
    7197             :          * Check it's a btree; currently this can never fail since no other
    7198             :          * index AMs support unique indexes.  If we ever did have other types
    7199             :          * of unique indexes, we'd need a way to determine which operator
    7200             :          * strategy number is equality.  (Is it reasonable to insist that
    7201             :          * every such index AM use btree's number for equality?)
    7202             :          */
    7203         156 :         if (amid != BTREE_AM_OID)
    7204           0 :             elog(ERROR, "only b-tree indexes are supported for foreign keys");
    7205         156 :         eqstrategy = BTEqualStrategyNumber;
    7206             : 
    7207             :         /*
    7208             :          * There had better be a primary equality operator for the index.
    7209             :          * We'll use it for PK = PK comparisons.
    7210             :          */
    7211         156 :         ppeqop = get_opfamily_member(opfamily, opcintype, opcintype,
    7212             :                                      eqstrategy);
    7213             : 
    7214         156 :         if (!OidIsValid(ppeqop))
    7215           0 :             elog(ERROR, "missing operator %d(%u,%u) in opfamily %u",
    7216             :                  eqstrategy, opcintype, opcintype, opfamily);
    7217             : 
    7218             :         /*
    7219             :          * Are there equality operators that take exactly the FK type? Assume
    7220             :          * we should look through any domain here.
    7221             :          */
    7222         156 :         fktyped = getBaseType(fktype);
    7223             : 
    7224         156 :         pfeqop = get_opfamily_member(opfamily, opcintype, fktyped,
    7225             :                                      eqstrategy);
    7226         156 :         if (OidIsValid(pfeqop))
    7227             :         {
    7228         113 :             pfeqop_right = fktyped;
    7229         113 :             ffeqop = get_opfamily_member(opfamily, fktyped, fktyped,
    7230             :                                          eqstrategy);
    7231             :         }
    7232             :         else
    7233             :         {
    7234             :             /* keep compiler quiet */
    7235          43 :             pfeqop_right = InvalidOid;
    7236          43 :             ffeqop = InvalidOid;
    7237             :         }
    7238             : 
    7239         156 :         if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
    7240             :         {
    7241             :             /*
    7242             :              * Otherwise, look for an implicit cast from the FK type to the
    7243             :              * opcintype, and if found, use the primary equality operator.
    7244             :              * This is a bit tricky because opcintype might be a polymorphic
    7245             :              * type such as ANYARRAY or ANYENUM; so what we have to test is
    7246             :              * whether the two actual column types can be concurrently cast to
    7247             :              * that type.  (Otherwise, we'd fail to reject combinations such
    7248             :              * as int[] and point[].)
    7249             :              */
    7250             :             Oid         input_typeids[2];
    7251             :             Oid         target_typeids[2];
    7252             : 
    7253          43 :             input_typeids[0] = pktype;
    7254          43 :             input_typeids[1] = fktype;
    7255          43 :             target_typeids[0] = opcintype;
    7256          43 :             target_typeids[1] = opcintype;
    7257          43 :             if (can_coerce_type(2, input_typeids, target_typeids,
    7258             :                                 COERCION_IMPLICIT))
    7259             :             {
    7260           8 :                 pfeqop = ffeqop = ppeqop;
    7261           8 :                 pfeqop_right = opcintype;
    7262             :             }
    7263             :         }
    7264             : 
    7265         156 :         if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
    7266          35 :             ereport(ERROR,
    7267             :                     (errcode(ERRCODE_DATATYPE_MISMATCH),
    7268             :                      errmsg("foreign key constraint \"%s\" "
    7269             :                             "cannot be implemented",
    7270             :                             fkconstraint->conname),
    7271             :                      errdetail("Key columns \"%s\" and \"%s\" "
    7272             :                                "are of incompatible types: %s and %s.",
    7273             :                                strVal(list_nth(fkconstraint->fk_attrs, i)),
    7274             :                                strVal(list_nth(fkconstraint->pk_attrs, i)),
    7275             :                                format_type_be(fktype),
    7276             :                                format_type_be(pktype))));
    7277             : 
    7278         121 :         if (old_check_ok)
    7279             :         {
    7280             :             /*
    7281             :              * When a pfeqop changes, revalidate the constraint.  We could
    7282             :              * permit intra-opfamily changes, but that adds subtle complexity
    7283             :              * without any concrete benefit for core types.  We need not
    7284             :              * assess ppeqop or ffeqop, which RI_Initial_Check() does not use.
    7285             :              */
    7286           1 :             old_check_ok = (pfeqop == lfirst_oid(old_pfeqop_item));
    7287           1 :             old_pfeqop_item = lnext(old_pfeqop_item);
    7288             :         }
    7289         121 :         if (old_check_ok)
    7290             :         {
    7291             :             Oid         old_fktype;
    7292             :             Oid         new_fktype;
    7293             :             CoercionPathType old_pathtype;
    7294             :             CoercionPathType new_pathtype;
    7295             :             Oid         old_castfunc;
    7296             :             Oid         new_castfunc;
    7297           1 :             Form_pg_attribute attr = TupleDescAttr(tab->oldDesc,
    7298             :                                                    fkattnum[i] - 1);
    7299             : 
    7300             :             /*
    7301             :              * Identify coercion pathways from each of the old and new FK-side
    7302             :              * column types to the right (foreign) operand type of the pfeqop.
    7303             :              * We may assume that pg_constraint.conkey is not changing.
    7304             :              */
    7305           1 :             old_fktype = attr->atttypid;
    7306           1 :             new_fktype = fktype;
    7307           1 :             old_pathtype = findFkeyCast(pfeqop_right, old_fktype,
    7308             :                                         &old_castfunc);
    7309           1 :             new_pathtype = findFkeyCast(pfeqop_right, new_fktype,
    7310             :                                         &new_castfunc);
    7311             : 
    7312             :             /*
    7313             :              * Upon a change to the cast from the FK column to its pfeqop
    7314             :              * operand, revalidate the constraint.  For this evaluation, a
    7315             :              * binary coercion cast is equivalent to no cast at all.  While
    7316             :              * type implementors should design implicit casts with an eye
    7317             :              * toward consistency of operations like equality, we cannot
    7318             :              * assume here that they have done so.
    7319             :              *
    7320             :              * A function with a polymorphic argument could change behavior
    7321             :              * arbitrarily in response to get_fn_expr_argtype().  Therefore,
    7322             :              * when the cast destination is polymorphic, we only avoid
    7323             :              * revalidation if the input type has not changed at all.  Given
    7324             :              * just the core data types and operator classes, this requirement
    7325             :              * prevents no would-be optimizations.
    7326             :              *
    7327             :              * If the cast converts from a base type to a domain thereon, then
    7328             :              * that domain type must be the opcintype of the unique index.
    7329             :              * Necessarily, the primary key column must then be of the domain
    7330             :              * type.  Since the constraint was previously valid, all values on
    7331             :              * the foreign side necessarily exist on the primary side and in
    7332             :              * turn conform to the domain.  Consequently, we need not treat
    7333             :              * domains specially here.
    7334             :              *
    7335             :              * Since we require that all collations share the same notion of
    7336             :              * equality (which they do, because texteq reduces to bitwise
    7337             :              * equality), we don't compare collation here.
    7338             :              *
    7339             :              * We need not directly consider the PK type.  It's necessarily
    7340             :              * binary coercible to the opcintype of the unique index column,
    7341             :              * and ri_triggers.c will only deal with PK datums in terms of
    7342             :              * that opcintype.  Changing the opcintype also changes pfeqop.
    7343             :              */
    7344           2 :             old_check_ok = (new_pathtype == old_pathtype &&
    7345           3 :                             new_castfunc == old_castfunc &&
    7346           1 :                             (!IsPolymorphicType(pfeqop_right) ||
    7347             :                              new_fktype == old_fktype));
    7348             : 
    7349             :         }
    7350             : 
    7351         121 :         pfeqoperators[i] = pfeqop;
    7352         121 :         ppeqoperators[i] = ppeqop;
    7353         121 :         ffeqoperators[i] = ffeqop;
    7354             :     }
    7355             : 
    7356             :     /*
    7357             :      * Record the FK constraint in pg_constraint.
    7358             :      */
    7359         651 :     constrOid = CreateConstraintEntry(fkconstraint->conname,
    7360          93 :                                       RelationGetNamespace(rel),
    7361             :                                       CONSTRAINT_FOREIGN,
    7362          93 :                                       fkconstraint->deferrable,
    7363          93 :                                       fkconstraint->initdeferred,
    7364          93 :                                       fkconstraint->initially_valid,
    7365             :                                       RelationGetRelid(rel),
    7366             :                                       fkattnum,
    7367             :                                       numfks,
    7368             :                                       InvalidOid,   /* not a domain constraint */
    7369             :                                       indexOid,
    7370             :                                       RelationGetRelid(pkrel),
    7371             :                                       pkattnum,
    7372             :                                       pfeqoperators,
    7373             :                                       ppeqoperators,
    7374             :                                       ffeqoperators,
    7375             :                                       numpks,
    7376          93 :                                       fkconstraint->fk_upd_action,
    7377          93 :                                       fkconstraint->fk_del_action,
    7378          93 :                                       fkconstraint->fk_matchtype,
    7379             :                                       NULL, /* no exclusion constraint */
    7380             :                                       NULL, /* no check constraint */
    7381             :                                       NULL,
    7382             :                                       NULL,
    7383             :                                       true, /* islocal */
    7384             :                                       0,    /* inhcount */
    7385             :                                       true, /* isnoinherit */
    7386             :                                       false);   /* is_internal */
    7387          93 :     ObjectAddressSet(address, ConstraintRelationId, constrOid);
    7388             : 
    7389             :     /*
    7390             :      * Create the triggers that will enforce the constraint.
    7391             :      */
    7392          93 :     createForeignKeyTriggers(rel, RelationGetRelid(pkrel), fkconstraint,
    7393             :                              constrOid, indexOid);
    7394             : 
    7395             :     /*
    7396             :      * Tell Phase 3 to check that the constraint is satisfied by existing
    7397             :      * rows. We can skip this during table creation, when requested explicitly
    7398             :      * by specifying NOT VALID in an ADD FOREIGN KEY command, and when we're
    7399             :      * recreating a constraint following a SET DATA TYPE operation that did
    7400             :      * not impugn its validity.
    7401             :      */
    7402          93 :     if (!old_check_ok && !fkconstraint->skip_validation)
    7403             :     {
    7404             :         NewConstraint *newcon;
    7405             : 
    7406          23 :         newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
    7407          23 :         newcon->name = fkconstraint->conname;
    7408          23 :         newcon->contype = CONSTR_FOREIGN;
    7409          23 :         newcon->refrelid = RelationGetRelid(pkrel);
    7410          23 :         newcon->refindid = indexOid;
    7411          23 :         newcon->conid = constrOid;
    7412          23 :         newcon->qual = (Node *) fkconstraint;
    7413             : 
    7414          23 :         tab->constraints = lappend(tab->constraints, newcon);
    7415             :     }
    7416             : 
    7417             :     /*
    7418             :      * Close pk table, but keep lock until we've committed.
    7419             :      */
    7420          93 :     heap_close(pkrel, NoLock);
    7421             : 
    7422          93 :     return address;
    7423             : }
    7424             : 
    7425             : /*
    7426             :  * ALTER TABLE ALTER CONSTRAINT
    7427             :  *
    7428             :  * Update the attributes of a constraint.
    7429             :  *
    7430             :  * Currently only works for Foreign Key constraints.
    7431             :  * Foreign keys do not inherit, so we purposely ignore the
    7432             :  * recursion bit here, but we keep the API the same for when
    7433             :  * other constraint types are supported.
    7434             :  *
    7435             :  * If the constraint is modified, returns its address; otherwise, return
    7436             :  * InvalidObjectAddress.
    7437             :  */
    7438             : static ObjectAddress
    7439           5 : ATExecAlterConstraint(Relation rel, AlterTableCmd *cmd,
    7440             :                       bool recurse, bool recursing, LOCKMODE lockmode)
    7441             : {
    7442             :     Constraint *cmdcon;
    7443             :     Relation    conrel;
    7444             :     SysScanDesc scan;
    7445             :     ScanKeyData key;
    7446             :     HeapTuple   contuple;
    7447           5 :     Form_pg_constraint currcon = NULL;
    7448           5 :     bool        found = false;
    7449             :     ObjectAddress address;
    7450             : 
    7451           5 :     cmdcon = castNode(Constraint, cmd->def);
    7452             : 
    7453           5 :     conrel = heap_open(ConstraintRelationId, RowExclusiveLock);
    7454             : 
    7455             :     /*
    7456             :      * Find and check the target constraint
    7457             :      */
    7458           5 :     ScanKeyInit(&key,
    7459             :                 Anum_pg_constraint_conrelid,
    7460             :                 BTEqualStrategyNumber, F_OIDEQ,
    7461             :                 ObjectIdGetDatum(RelationGetRelid(rel)));
    7462           5 :     scan = systable_beginscan(conrel, ConstraintRelidIndexId,
    7463             :                               true, NULL, 1, &key);
    7464             : 
    7465           5 :     while (HeapTupleIsValid(contuple = systable_getnext(scan)))
    7466             :     {
    7467           5 :         currcon = (Form_pg_constraint) GETSTRUCT(contuple);
    7468           5 :         if (strcmp(NameStr(currcon->conname), cmdcon->conname) == 0)
    7469             :         {
    7470           5 :             found = true;
    7471           5 :             break;
    7472             :         }
    7473             :     }
    7474             : 
    7475           5 :     if (!found)
    7476           0 :         ereport(ERROR,
    7477             :                 (errcode(ERRCODE_UNDEFINED_OBJECT),
    7478             :                  errmsg("constraint \"%s\" of relation \"%s\" does not exist",
    7479             :                         cmdcon->conname, RelationGetRelationName(rel))));
    7480             : 
    7481           5 :     if (currcon->contype != CONSTRAINT_FOREIGN)
    7482           0 :         ereport(ERROR,
    7483             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    7484             :                  errmsg("constraint \"%s\" of relation \"%s\" is not a foreign key constraint",
    7485             :                         cmdcon->conname, RelationGetRelationName(rel))));
    7486             : 
    7487           6 :     if (currcon->condeferrable != cmdcon->deferrable ||
    7488           1 :         currcon->condeferred != cmdcon->initdeferred)
    7489           5 :     {
    7490             :         HeapTuple   copyTuple;
    7491             :         HeapTuple   tgtuple;
    7492             :         Form_pg_constraint copy_con;
    7493           5 :         List       *otherrelids = NIL;
    7494             :         ScanKeyData tgkey;
    7495             :         SysScanDesc tgscan;
    7496             :         Relation    tgrel;
    7497             :         ListCell   *lc;
    7498             : 
    7499             :         /*
    7500             :          * Now update the catalog, while we have the door open.
    7501             :          */
    7502           5 :         copyTuple = heap_copytuple(contuple);
    7503           5 :         copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
    7504           5 :         copy_con->condeferrable = cmdcon->deferrable;
    7505           5 :         copy_con->condeferred = cmdcon->initdeferred;
    7506           5 :         CatalogTupleUpdate(conrel, &copyTuple->t_self, copyTuple);
    7507             : 
    7508           5 :         InvokeObjectPostAlterHook(ConstraintRelationId,
    7509             :                                   HeapTupleGetOid(contuple), 0);
    7510             : 
    7511           5 :         heap_freetuple(copyTuple);
    7512             : 
    7513             :         /*
    7514             :          * Now we need to update the multiple entries in pg_trigger that
    7515             :          * implement the constraint.
    7516             :          */
    7517           5 :         tgrel = heap_open(TriggerRelationId, RowExclusiveLock);
    7518             : 
    7519          10 :         ScanKeyInit(&tgkey,
    7520             :                     Anum_pg_trigger_tgconstraint,
    7521             :                     BTEqualStrategyNumber, F_OIDEQ,
    7522          10 :                     ObjectIdGetDatum(HeapTupleGetOid(contuple)));
    7523             : 
    7524           5 :         tgscan = systable_beginscan(tgrel, TriggerConstraintIndexId, true,
    7525             :                                     NULL, 1, &tgkey);
    7526             : 
    7527          30 :         while (HeapTupleIsValid(tgtuple = systable_getnext(tgscan)))
    7528             :         {
    7529          20 :             Form_pg_trigger tgform = (Form_pg_trigger) GETSTRUCT(tgtuple);
    7530             :             Form_pg_trigger copy_tg;
    7531             : 
    7532             :             /*
    7533             :              * Remember OIDs of other relation(s) involved in FK constraint.
    7534             :              * (Note: it's likely that we could skip forcing a relcache inval
    7535             :              * for other rels that don't have a trigger whose properties
    7536             :              * change, but let's be conservative.)
    7537             :              */
    7538          20 :             if (tgform->tgrelid != RelationGetRelid(rel))
    7539          10 :                 otherrelids = list_append_unique_oid(otherrelids,
    7540             :                                                      tgform->tgrelid);
    7541             : 
    7542             :             /*
    7543             :              * Update deferrability of RI_FKey_noaction_del,
    7544             :              * RI_FKey_noaction_upd, RI_FKey_check_ins and RI_FKey_check_upd
    7545             :              * triggers, but not others; see createForeignKeyTriggers and
    7546             :              * CreateFKCheckTrigger.
    7547             :              */
    7548          38 :             if (tgform->tgfoid != F_RI_FKEY_NOACTION_DEL &&
    7549          31 :                 tgform->tgfoid != F_RI_FKEY_NOACTION_UPD &&
    7550          21 :                 tgform->tgfoid != F_RI_FKEY_CHECK_INS &&
    7551           8 :                 tgform->tgfoid != F_RI_FKEY_CHECK_UPD)
    7552           3 :                 continue;
    7553             : 
    7554          17 :             copyTuple = heap_copytuple(tgtuple);
    7555          17 :             copy_tg = (Form_pg_trigger) GETSTRUCT(copyTuple);
    7556             : 
    7557          17 :             copy_tg->tgdeferrable = cmdcon->deferrable;
    7558          17 :             copy_tg->tginitdeferred = cmdcon->initdeferred;
    7559          17 :             CatalogTupleUpdate(tgrel, &copyTuple->t_self, copyTuple);
    7560             : 
    7561          17 :             InvokeObjectPostAlterHook(TriggerRelationId,
    7562             :                                       HeapTupleGetOid(tgtuple), 0);
    7563             : 
    7564          17 :             heap_freetuple(copyTuple);
    7565             :         }
    7566             : 
    7567           5 :         systable_endscan(tgscan);
    7568             : 
    7569           5 :         heap_close(tgrel, RowExclusiveLock);
    7570             : 
    7571             :         /*
    7572             :          * Invalidate relcache so that others see the new attributes.  We must
    7573             :          * inval both the named rel and any others having relevant triggers.
    7574             :          * (At present there should always be exactly one other rel, but
    7575             :          * there's no need to hard-wire such an assumption here.)
    7576             :          */
    7577           5 :         CacheInvalidateRelcache(rel);
    7578          10 :         foreach(lc, otherrelids)
    7579             :         {
    7580           5 :             CacheInvalidateRelcacheByRelid(lfirst_oid(lc));
    7581             :         }
    7582             : 
    7583           5 :         ObjectAddressSet(address, ConstraintRelationId,
    7584             :                          HeapTupleGetOid(contuple));
    7585             :     }
    7586             :     else
    7587           0 :         address = InvalidObjectAddress;
    7588             : 
    7589           5 :     systable_endscan(scan);
    7590             : 
    7591           5 :     heap_close(conrel, RowExclusiveLock);
    7592             : 
    7593           5 :     return address;
    7594             : }
    7595             : 
    7596             : /*
    7597             :  * ALTER TABLE VALIDATE CONSTRAINT
    7598             :  *
    7599             :  * XXX The reason we handle recursion here rather than at Phase 1 is because
    7600             :  * there's no good way to skip recursing when handling foreign keys: there is
    7601             :  * no need to lock children in that case, yet we wouldn't be able to avoid
    7602             :  * doing so at that level.
    7603             :  *
    7604             :  * Return value is the address of the validated constraint.  If the constraint
    7605             :  * was already validated, InvalidObjectAddress is returned.
    7606             :  */
    7607             : static ObjectAddress
    7608          22 : ATExecValidateConstraint(Relation rel, char *constrName, bool recurse,
    7609             :                          bool recursing, LOCKMODE lockmode)
    7610             : {
    7611             :     Relation    conrel;
    7612             :     SysScanDesc scan;
    7613             :     ScanKeyData key;
    7614             :     HeapTuple   tuple;
    7615          22 :     Form_pg_constraint con = NULL;
    7616          22 :     bool        found = false;
    7617             :     ObjectAddress address;
    7618             : 
    7619          22 :     conrel = heap_open(ConstraintRelationId, RowExclusiveLock);
    7620             : 
    7621             :     /*
    7622             :      * Find and check the target constraint
    7623             :      */
    7624          22 :     ScanKeyInit(&key,
    7625             :                 Anum_pg_constraint_conrelid,
    7626             :                 BTEqualStrategyNumber, F_OIDEQ,
    7627             :                 ObjectIdGetDatum(RelationGetRelid(rel)));
    7628          22 :     scan = systable_beginscan(conrel, ConstraintRelidIndexId,
    7629             :                               true, NULL, 1, &key);
    7630             : 
    7631          22 :     while (HeapTupleIsValid(tuple = systable_getnext(scan)))
    7632             :     {
    7633          22 :         con = (Form_pg_constraint) GETSTRUCT(tuple);
    7634          22 :         if (strcmp(NameStr(con->conname), constrName) == 0)
    7635             :         {
    7636          22 :             found = true;
    7637          22 :             break;
    7638             :         }
    7639             :     }
    7640             : 
    7641          22 :     if (!found)
    7642           0 :         ereport(ERROR,
    7643             :                 (errcode(ERRCODE_UNDEFINED_OBJECT),
    7644             :                  errmsg("constraint \"%s\" of relation \"%s\" does not exist",
    7645             :                         constrName, RelationGetRelationName(rel))));
    7646             : 
    7647          40 :     if (con->contype != CONSTRAINT_FOREIGN &&
    7648          18 :         con->contype != CONSTRAINT_CHECK)
    7649           0 :         ereport(ERROR,
    7650             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    7651             :                  errmsg("constraint \"%s\" of relation \"%s\" is not a foreign key or check constraint",
    7652             :                         constrName, RelationGetRelationName(rel))));
    7653             : 
    7654          22 :     if (!con->convalidated)
    7655             :     {
    7656             :         HeapTuple   copyTuple;
    7657             :         Form_pg_constraint copy_con;
    7658             : 
    7659          19 :         if (con->contype == CONSTRAINT_FOREIGN)
    7660             :         {
    7661             :             Relation    refrel;
    7662             : 
    7663             :             /*
    7664             :              * Triggers are already in place on both tables, so a concurrent
    7665             :              * write that alters the result here is not possible. Normally we
    7666             :              * can run a query here to do the validation, which would only
    7667             :              * require AccessShareLock. In some cases, it is possible that we
    7668             :              * might need to fire triggers to perform the check, so we take a
    7669             :              * lock at RowShareLock level just in case.
    7670             :              */
    7671           3 :             refrel = heap_open(con->confrelid, RowShareLock);
    7672             : 
    7673           6 :             validateForeignKeyConstraint(constrName, rel, refrel,
    7674             :                                          con->conindid,
    7675           6 :                                          HeapTupleGetOid(tuple));
    7676           2 :             heap_close(refrel, NoLock);
    7677             : 
    7678             :             /*
    7679             :              * Foreign keys do not inherit, so we purposely ignore the
    7680             :              * recursion bit here
    7681             :              */
    7682             :         }
    7683          16 :         else if (con->contype == CONSTRAINT_CHECK)
    7684             :         {
    7685          16 :             List       *children = NIL;
    7686             :             ListCell   *child;
    7687             : 
    7688             :             /*
    7689             :              * If we're recursing, the parent has already done this, so skip
    7690             :              * it.  Also, if the constraint is a NO INHERIT constraint, we
    7691             :              * shouldn't try to look for it in the children.
    7692             :              */
    7693          16 :             if (!recursing && !con->connoinherit)
    7694           8 :                 children = find_all_inheritors(RelationGetRelid(rel),
    7695             :                                                lockmode, NULL);
    7696             : 
    7697             :             /*
    7698             :              * For CHECK constraints, we must ensure that we only mark the
    7699             :              * constraint as validated on the parent if it's already validated
    7700             :              * on the children.
    7701             :              *
    7702             :              * We recurse before validating on the parent, to reduce risk of
    7703             :              * deadlocks.
    7704             :              */
    7705          30 :             foreach(child, children)
    7706             :             {
    7707          15 :                 Oid         childoid = lfirst_oid(child);
    7708             :                 Relation    childrel;
    7709             : 
    7710          15 :                 if (childoid == RelationGetRelid(rel))
    7711           8 :                     continue;
    7712             : 
    7713             :                 /*
    7714             :                  * If we are told not to recurse, there had better not be any
    7715             :                  * child tables, because we can't mark the constraint on the
    7716             :                  * parent valid unless it is valid for all child tables.
    7717             :                  */
    7718           7 :                 if (!recurse)
    7719           0 :                     ereport(ERROR,
    7720             :                             (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    7721             :                              errmsg("constraint must be validated on child tables too")));
    7722             : 
    7723             :                 /* find_all_inheritors already got lock */
    7724           7 :                 childrel = heap_open(childoid, NoLock);
    7725             : 
    7726           7 :                 ATExecValidateConstraint(childrel, constrName, false,
    7727             :                                          true, lockmode);
    7728           6 :                 heap_close(childrel, NoLock);
    7729             :             }
    7730             : 
    7731          15 :             validateCheckConstraint(rel, tuple);
    7732             : 
    7733             :             /*
    7734             :              * Invalidate relcache so that others see the new validated
    7735             :              * constraint.
    7736             :              */
    7737          12 :             CacheInvalidateRelcache(rel);
    7738             :         }
    7739             : 
    7740             :         /*
    7741             :          * Now update the catalog, while we have the door open.
    7742             :          */
    7743          14 :         copyTuple = heap_copytuple(tuple);
    7744          14 :         copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
    7745          14 :         copy_con->convalidated = true;
    7746          14 :         CatalogTupleUpdate(conrel, &copyTuple->t_self, copyTuple);
    7747             : 
    7748          14 :         InvokeObjectPostAlterHook(ConstraintRelationId,
    7749             :                                   HeapTupleGetOid(tuple), 0);
    7750             : 
    7751          14 :         heap_freetuple(copyTuple);
    7752             : 
    7753          14 :         ObjectAddressSet(address, ConstraintRelationId,
    7754             :                          HeapTupleGetOid(tuple));
    7755             :     }
    7756             :     else
    7757           3 :         address = InvalidObjectAddress; /* already validated */
    7758             : 
    7759          17 :     systable_endscan(scan);
    7760             : 
    7761          17 :     heap_close(conrel, RowExclusiveLock);
    7762             : 
    7763          17 :     return address;
    7764             : }
    7765             : 
    7766             : 
    7767             : /*
    7768             :  * transformColumnNameList - transform list of column names
    7769             :  *
    7770             :  * Lookup each name and return its attnum and type OID
    7771             :  */
    7772             : static int
    7773         199 : transformColumnNameList(Oid relId, List *colList,
    7774             :                         int16 *attnums, Oid *atttypids)
    7775             : {
    7776             :     ListCell   *l;
    7777             :     int         attnum;
    7778             : 
    7779         199 :     attnum = 0;
    7780         476 :     foreach(l, colList)
    7781             :     {
    7782         285 :         char       *attname = strVal(lfirst(l));
    7783             :         HeapTuple   atttuple;
    7784             : 
    7785         285 :         atttuple = SearchSysCacheAttName(relId, attname);
    7786         285 :         if (!HeapTupleIsValid(atttuple))
    7787           8 :             ereport(ERROR,
    7788             :                     (errcode(ERRCODE_UNDEFINED_COLUMN),
    7789             :                      errmsg("column \"%s\" referenced in foreign key constraint does not exist",
    7790             :                             attname)));
    7791         277 :         if (attnum >= INDEX_MAX_KEYS)
    7792           0 :             ereport(ERROR,
    7793             :                     (errcode(ERRCODE_TOO_MANY_COLUMNS),
    7794             :                      errmsg("cannot have more than %d keys in a foreign key",
    7795             :                             INDEX_MAX_KEYS)));
    7796         277 :         attnums[attnum] = ((Form_pg_attribute) GETSTRUCT(atttuple))->attnum;
    7797         277 :         atttypids[attnum] = ((Form_pg_attribute) GETSTRUCT(atttuple))->atttypid;
    7798         277 :         ReleaseSysCache(atttuple);
    7799         277 :         attnum++;
    7800             :     }
    7801             : 
    7802         191 :     return attnum;
    7803             : }
    7804             : 
    7805             : /*
    7806             :  * transformFkeyGetPrimaryKey -
    7807             :  *
    7808             :  *  Look up the names, attnums, and types of the primary key attributes
    7809             :  *  for the pkrel.  Also return the index OID and index opclasses of the
    7810             :  *  index supporting the primary key.
    7811             :  *
    7812             :  *  All parameters except pkrel are output parameters.  Also, the function
    7813             :  *  return value is the number of attributes in the primary key.
    7814             :  *
    7815             :  *  Used when the column list in the REFERENCES specification is omitted.
    7816             :  */
    7817             : static int
    7818          73 : transformFkeyGetPrimaryKey(Relation pkrel, Oid *indexOid,
    7819             :                            List **attnamelist,
    7820             :                            int16 *attnums, Oid *atttypids,
    7821             :                            Oid *opclasses)
    7822             : {
    7823             :     List       *indexoidlist;
    7824             :     ListCell   *indexoidscan;
    7825          73 :     HeapTuple   indexTuple = NULL;
    7826          73 :     Form_pg_index indexStruct = NULL;
    7827             :     Datum       indclassDatum;
    7828             :     bool        isnull;
    7829             :     oidvector  *indclass;
    7830             :     int         i;
    7831             : 
    7832             :     /*
    7833             :      * Get the list of index OIDs for the table from the relcache, and look up
    7834             :      * each one in the pg_index syscache until we find one marked primary key
    7835             :      * (hopefully there isn't more than one such).  Insist it's valid, too.
    7836             :      */
    7837          73 :     *indexOid = InvalidOid;
    7838             : 
    7839          73 :     indexoidlist = RelationGetIndexList(pkrel);
    7840             : 
    7841          74 :     foreach(indexoidscan, indexoidlist)
    7842             :     {
    7843          74 :         Oid         indexoid = lfirst_oid(indexoidscan);
    7844             : 
    7845          74 :         indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexoid));
    7846          74 :         if (!HeapTupleIsValid(indexTuple))
    7847           0 :             elog(ERROR, "cache lookup failed for index %u", indexoid);
    7848          74 :         indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
    7849          74 :         if (indexStruct->indisprimary && IndexIsValid(indexStruct))
    7850             :         {
    7851             :             /*
    7852             :              * Refuse to use a deferrable primary key.  This is per SQL spec,
    7853             :              * and there would be a lot of interesting semantic problems if we
    7854             :              * tried to allow it.
    7855             :              */
    7856          73 :             if (!indexStruct->indimmediate)
    7857           0 :                 ereport(ERROR,
    7858             :                         (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    7859             :                          errmsg("cannot use a deferrable primary key for referenced table \"%s\"",
    7860             :                                 RelationGetRelationName(pkrel))));
    7861             : 
    7862          73 :             *indexOid = indexoid;
    7863          73 :             break;
    7864             :         }
    7865           1 :         ReleaseSysCache(indexTuple);
    7866             :     }
    7867             : 
    7868          73 :     list_free(indexoidlist);
    7869             : 
    7870             :     /*
    7871             :      * Check that we found it
    7872             :      */
    7873          73 :     if (!OidIsValid(*indexOid))
    7874           0 :         ereport(ERROR,
    7875             :                 (errcode(ERRCODE_UNDEFINED_OBJECT),
    7876             :                  errmsg("there is no primary key for referenced table \"%s\"",
    7877             :                         RelationGetRelationName(pkrel))));
    7878             : 
    7879             :     /* Must get indclass the hard way */
    7880          73 :     indclassDatum = SysCacheGetAttr(INDEXRELID, indexTuple,
    7881             :                                     Anum_pg_index_indclass, &isnull);
    7882          73 :     Assert(!isnull);
    7883          73 :     indclass = (oidvector *) DatumGetPointer(indclassDatum);
    7884             : 
    7885             :     /*
    7886             :      * Now build the list of PK attributes from the indkey definition (we
    7887             :      * assume a primary key cannot have expressional elements)
    7888             :      */
    7889          73 :     *attnamelist = NIL;
    7890         164 :     for (i = 0; i < indexStruct->indnatts; i++)
    7891             :     {
    7892          91 :         int         pkattno = indexStruct->indkey.values[i];
    7893             : 
    7894          91 :         attnums[i] = pkattno;
    7895          91 :         atttypids[i] = attnumTypeId(pkrel, pkattno);
    7896          91 :         opclasses[i] = indclass->values[i];
    7897          91 :         *attnamelist = lappend(*attnamelist,
    7898          91 :                                makeString(pstrdup(NameStr(*attnumAttName(pkrel, pkattno)))));
    7899             :     }
    7900             : 
    7901          73 :     ReleaseSysCache(indexTuple);
    7902             : 
    7903          73 :     return i;
    7904             : }
    7905             : 
    7906             : /*
    7907             :  * transformFkeyCheckAttrs -
    7908             :  *
    7909             :  *  Make sure that the attributes of a referenced table belong to a unique
    7910             :  *  (or primary key) constraint.  Return the OID of the index supporting
    7911             :  *  the constraint, as well as the opclasses associated with the index
    7912             :  *  columns.
    7913             :  */
    7914             : static Oid
    7915          57 : transformFkeyCheckAttrs(Relation pkrel,
    7916             :                         int numattrs, int16 *attnums,
    7917             :                         Oid *opclasses) /* output parameter */
    7918             : {
    7919          57 :     Oid         indexoid = InvalidOid;
    7920          57 :     bool        found = false;
    7921          57 :     bool        found_deferrable = false;
    7922             :     List       *indexoidlist;
    7923             :     ListCell   *indexoidscan;
    7924             :     int         i,
    7925             :                 j;
    7926             : 
    7927             :     /*
    7928             :      * Reject duplicate appearances of columns in the referenced-columns list.
    7929             :      * Such a case is forbidden by the SQL standard, and even if we thought it
    7930             :      * useful to allow it, there would be ambiguity about how to match the
    7931             :      * list to unique indexes (in particular, it'd be unclear which index
    7932             :      * opclass goes with which FK column).
    7933             :      */
    7934         148 :     for (i = 0; i < numattrs; i++)
    7935             :     {
    7936         130 :         for (j = i + 1; j < numattrs; j++)
    7937             :         {
    7938          39 :             if (attnums[i] == attnums[j])
    7939           0 :                 ereport(ERROR,
    7940             :                         (errcode(ERRCODE_INVALID_FOREIGN_KEY),
    7941             :                          errmsg("foreign key referenced-columns list must not contain duplicates")));
    7942             :         }
    7943             :     }
    7944             : 
    7945             :     /*
    7946             :      * Get the list of index OIDs for the table from the relcache, and look up
    7947             :      * each one in the pg_index syscache, and match unique indexes to the list
    7948             :      * of attnums we are given.
    7949             :      */
    7950          57 :     indexoidlist = RelationGetIndexList(pkrel);
    7951             : 
    7952          86 :     foreach(indexoidscan, indexoidlist)
    7953             :     {
    7954             :         HeapTuple   indexTuple;
    7955             :         Form_pg_index indexStruct;
    7956             : 
    7957          84 :         indexoid = lfirst_oid(indexoidscan);
    7958          84 :         indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexoid));
    7959          84 :         if (!HeapTupleIsValid(indexTuple))
    7960           0 :             elog(ERROR, "cache lookup failed for index %u", indexoid);
    7961          84 :         indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
    7962             : 
    7963             :         /*
    7964             :          * Must have the right number of columns; must be unique and not a
    7965             :          * partial index; forget it if there are any expressions, too. Invalid
    7966             :          * indexes are out as well.
    7967             :          */
    7968         150 :         if (indexStruct->indnatts == numattrs &&
    7969         130 :             indexStruct->indisunique &&
    7970         128 :             IndexIsValid(indexStruct) &&
    7971         128 :             heap_attisnull(indexTuple, Anum_pg_index_indpred) &&
    7972          64 :             heap_attisnull(indexTuple, Anum_pg_index_indexprs))
    7973             :         {
    7974             :             Datum       indclassDatum;
    7975             :             bool        isnull;
    7976             :             oidvector  *indclass;
    7977             : 
    7978             :             /* Must get indclass the hard way */
    7979          64 :             indclassDatum = SysCacheGetAttr(INDEXRELID, indexTuple,
    7980             :                                             Anum_pg_index_indclass, &isnull);
    7981          64 :             Assert(!isnull);
    7982          64 :             indclass = (oidvector *) DatumGetPointer(indclassDatum);
    7983             : 
    7984             :             /*
    7985             :              * The given attnum list may match the index columns in any order.
    7986             :              * Check for a match, and extract the appropriate opclasses while
    7987             :              * we're at it.
    7988             :              *
    7989             :              * We know that attnums[] is duplicate-free per the test at the
    7990             :              * start of this function, and we checked above that the number of
    7991             :              * index columns agrees, so if we find a match for each attnums[]
    7992             :              * entry then we must have a one-to-one match in some order.
    7993             :              */
    7994         153 :             for (i = 0; i < numattrs; i++)
    7995             :             {
    7996          98 :                 found = false;
    7997         146 :                 for (j = 0; j < numattrs; j++)
    7998             :                 {
    7999         137 :                     if (attnums[i] == indexStruct->indkey.values[j])
    8000             :                     {
    8001          89 :                         opclasses[i] = indclass->values[j];
    8002          89 :                         found = true;
    8003          89 :                         break;
    8004             :                     }
    8005             :                 }
    8006          98 :                 if (!found)
    8007           9 :                     break;
    8008             :             }
    8009             : 
    8010             :             /*
    8011             :              * Refuse to use a deferrable unique/primary key.  This is per SQL
    8012             :              * spec, and there would be a lot of interesting semantic problems
    8013             :              * if we tried to allow it.
    8014             :              */
    8015          64 :             if (found && !indexStruct->indimmediate)
    8016             :             {
    8017             :                 /*
    8018             :                  * Remember that we found an otherwise matching index, so that
    8019             :                  * we can generate a more appropriate error message.
    8020             :                  */
    8021           0 :                 found_deferrable = true;
    8022           0 :                 found = false;
    8023             :             }
    8024             :         }
    8025          84 :         ReleaseSysCache(indexTuple);
    8026          84 :         if (found)
    8027          55 :             break;
    8028             :     }
    8029             : 
    8030          57 :     if (!found)
    8031             :     {
    8032           2 :         if (found_deferrable)
    8033           0 :             ereport(ERROR,
    8034             :                     (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    8035             :                      errmsg("cannot use a deferrable unique constraint for referenced table \"%s\"",
    8036             :                             RelationGetRelationName(pkrel))));
    8037             :         else
    8038           2 :             ereport(ERROR,
    8039             :                     (errcode(ERRCODE_INVALID_FOREIGN_KEY),
    8040             :                      errmsg("there is no unique constraint matching given keys for referenced table \"%s\"",
    8041             :                             RelationGetRelationName(pkrel))));
    8042             :     }
    8043             : 
    8044          55 :     list_free(indexoidlist);
    8045             : 
    8046          55 :     return indexoid;
    8047             : }
    8048             : 
    8049             : /*
    8050             :  * findFkeyCast -
    8051             :  *
    8052             :  *  Wrapper around find_coercion_pathway() for ATAddForeignKeyConstraint().
    8053             :  *  Caller has equal regard for binary coercibility and for an exact match.
    8054             : */
    8055             : static CoercionPathType
    8056           2 : findFkeyCast(Oid targetTypeId, Oid sourceTypeId, Oid *funcid)
    8057             : {
    8058             :     CoercionPathType ret;
    8059             : 
    8060           2 :     if (targetTypeId == sourceTypeId)
    8061             :     {
    8062           2 :         ret = COERCION_PATH_RELABELTYPE;
    8063           2 :         *funcid = InvalidOid;
    8064             :     }
    8065             :     else
    8066             :     {
    8067           0 :         ret = find_coercion_pathway(targetTypeId, sourceTypeId,
    8068             :                                     COERCION_IMPLICIT, funcid);
    8069           0 :         if (ret == COERCION_PATH_NONE)
    8070             :             /* A previously-relied-upon cast is now gone. */
    8071           0 :             elog(ERROR, "could not find cast from %u to %u",
    8072             :                  sourceTypeId, targetTypeId);
    8073             :     }
    8074             : 
    8075           2 :     return ret;
    8076             : }
    8077             : 
    8078             : /*
    8079             :  * Permissions checks on the referenced table for ADD FOREIGN KEY
    8080             :  *
    8081             :  * Note: we have already checked that the user owns the referencing table,
    8082             :  * else we'd have failed much earlier; no additional checks are needed for it.
    8083             :  */
    8084             : static void
    8085         128 : checkFkeyPermissions(Relation rel, int16 *attnums, int natts)
    8086             : {
    8087         128 :     Oid         roleid = GetUserId();
    8088             :     AclResult   aclresult;
    8089             :     int         i;
    8090             : 
    8091             :     /* Okay if we have relation-level REFERENCES permission */
    8092         128 :     aclresult = pg_class_aclcheck(RelationGetRelid(rel), roleid,
    8093             :                                   ACL_REFERENCES);
    8094         128 :     if (aclresult == ACLCHECK_OK)
    8095         256 :         return;
    8096             :     /* Else we must have REFERENCES on each column */
    8097           0 :     for (i = 0; i < natts; i++)
    8098             :     {
    8099           0 :         aclresult = pg_attribute_aclcheck(RelationGetRelid(rel), attnums[i],
    8100             :                                           roleid, ACL_REFERENCES);
    8101           0 :         if (aclresult != ACLCHECK_OK)
    8102           0 :             aclcheck_error(aclresult, ACL_KIND_CLASS,
    8103           0 :                            RelationGetRelationName(rel));
    8104             :     }
    8105             : }
    8106             : 
    8107             : /*
    8108             :  * Scan the existing rows in a table to verify they meet a proposed
    8109             :  * CHECK constraint.
    8110             :  *
    8111             :  * The caller must have opened and locked the relation appropriately.
    8112             :  */
    8113             : static void
    8114          15 : validateCheckConstraint(Relation rel, HeapTuple constrtup)
    8115             : {
    8116             :     EState     *estate;
    8117             :     Datum       val;
    8118             :     char       *conbin;
    8119             :     Expr       *origexpr;
    8120             :     ExprState  *exprstate;
    8121             :     TupleDesc   tupdesc;
    8122             :     HeapScanDesc scan;
    8123             :     HeapTuple   tuple;
    8124             :     ExprContext *econtext;
    8125             :     MemoryContext oldcxt;
    8126             :     TupleTableSlot *slot;
    8127             :     Form_pg_constraint constrForm;
    8128             :     bool        isnull;
    8129             :     Snapshot    snapshot;
    8130             : 
    8131             :     /*
    8132             :      * VALIDATE CONSTRAINT is a no-op for foreign tables and partitioned
    8133             :      * tables.
    8134             :      */
    8135          29 :     if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE ||
    8136          14 :         rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
    8137          14 :         return;
    8138             : 
    8139          13 :     constrForm = (Form_pg_constraint) GETSTRUCT(constrtup);
    8140             : 
    8141          13 :     estate = CreateExecutorState();
    8142             : 
    8143             :     /*
    8144             :      * XXX this tuple doesn't really come from a syscache, but this doesn't
    8145             :      * matter to SysCacheGetAttr, because it only wants to be able to fetch
    8146             :      * the tupdesc
    8147             :      */
    8148          13 :     val = SysCacheGetAttr(CONSTROID, constrtup, Anum_pg_constraint_conbin,
    8149             :                           &isnull);
    8150          13 :     if (isnull)
    8151           0 :         elog(ERROR, "null conbin for constraint %u",
    8152             :              HeapTupleGetOid(constrtup));
    8153          13 :     conbin = TextDatumGetCString(val);
    8154          13 :     origexpr = (Expr *) stringToNode(conbin);
    8155          13 :     exprstate = ExecPrepareExpr(origexpr, estate);
    8156             : 
    8157          13 :     econtext = GetPerTupleExprContext(estate);
    8158          13 :     tupdesc = RelationGetDescr(rel);
    8159          13 :     slot = MakeSingleTupleTableSlot(tupdesc);
    8160          13 :     econtext->ecxt_scantuple = slot;
    8161             : 
    8162          13 :     snapshot = RegisterSnapshot(GetLatestSnapshot());
    8163          13 :     scan = heap_beginscan(rel, snapshot, 0, NULL);
    8164             : 
    8165             :     /*
    8166             :      * Switch to per-tuple memory context and reset it for each tuple
    8167             :      * produced, so we don't leak memory.
    8168             :      */
    8169          13 :     oldcxt = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
    8170             : 
    8171          32 :     while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
    8172             :     {
    8173           9 :         ExecStoreTuple(tuple, slot, InvalidBuffer, false);
    8174             : 
    8175           9 :         if (!ExecCheck(exprstate, econtext))
    8176           3 :             ereport(ERROR,
    8177             :                     (errcode(ERRCODE_CHECK_VIOLATION),
    8178             :                      errmsg("check constraint \"%s\" is violated by some row",
    8179             :                             NameStr(constrForm->conname)),
    8180             :                      errtableconstraint(rel, NameStr(constrForm->conname))));
    8181             : 
    8182           6 :         ResetExprContext(econtext);
    8183             :     }
    8184             : 
    8185          10 :     MemoryContextSwitchTo(oldcxt);
    8186          10 :     heap_endscan(scan);
    8187          10 :     UnregisterSnapshot(snapshot);
    8188          10 :     ExecDropSingleTupleTableSlot(slot);
    8189          10 :     FreeExecutorState(estate);
    8190             : }
    8191             : 
    8192             : /*
    8193             :  * Scan the existing rows in a table to verify they meet a proposed FK
    8194             :  * constraint.
    8195             :  *
    8196             :  * Caller must have opened and locked both relations appropriately.
    8197             :  */
    8198             : static void
    8199          26 : validateForeignKeyConstraint(char *conname,
    8200             :                              Relation rel,
    8201             :                              Relation pkrel,
    8202             :                              Oid pkindOid,
    8203             :                              Oid constraintOid)
    8204             : {
    8205             :     HeapScanDesc scan;
    8206             :     HeapTuple   tuple;
    8207             :     Trigger     trig;
    8208             :     Snapshot    snapshot;
    8209             : 
    8210          26 :     ereport(DEBUG1,
    8211             :             (errmsg("validating foreign key constraint \"%s\"", conname)));
    8212             : 
    8213             :     /*
    8214             :      * Build a trigger call structure; we'll need it either way.
    8215             :      */
    8216          26 :     MemSet(&trig, 0, sizeof(trig));
    8217          26 :     trig.tgoid = InvalidOid;
    8218          26 :     trig.tgname = conname;
    8219          26 :     trig.tgenabled = TRIGGER_FIRES_ON_ORIGIN;
    8220          26 :     trig.tgisinternal = TRUE;
    8221          26 :     trig.tgconstrrelid = RelationGetRelid(pkrel);
    8222          26 :     trig.tgconstrindid = pkindOid;
    8223          26 :     trig.tgconstraint = constraintOid;
    8224          26 :     trig.tgdeferrable = FALSE;
    8225          26 :     trig.tginitdeferred = FALSE;
    8226             :     /* we needn't fill in remaining fields */
    8227             : 
    8228             :     /*
    8229             :      * See if we can do it with a single LEFT JOIN query.  A FALSE result
    8230             :      * indicates we must proceed with the fire-the-trigger method.
    8231             :      */
    8232          26 :     if (RI_Initial_Check(&trig, rel, pkrel))
    8233          46 :         return;
    8234             : 
    8235             :     /*
    8236             :      * Scan through each tuple, calling RI_FKey_check_ins (insert trigger) as
    8237             :      * if that tuple had just been inserted.  If any of those fail, it should
    8238             :      * ereport(ERROR) and that's that.
    8239             :      */
    8240           0 :     snapshot = RegisterSnapshot(GetLatestSnapshot());
    8241           0 :     scan = heap_beginscan(rel, snapshot, 0, NULL);
    8242             : 
    8243           0 :     while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
    8244             :     {
    8245             :         FunctionCallInfoData fcinfo;
    8246             :         TriggerData trigdata;
    8247             : 
    8248             :         /*
    8249             :          * Make a call to the trigger function
    8250             :          *
    8251             :          * No parameters are passed, but we do set a context
    8252             :          */
    8253           0 :         MemSet(&fcinfo, 0, sizeof(fcinfo));
    8254             : 
    8255             :         /*
    8256             :          * We assume RI_FKey_check_ins won't look at flinfo...
    8257             :          */
    8258           0 :         trigdata.type = T_TriggerData;
    8259           0 :         trigdata.tg_event = TRIGGER_EVENT_INSERT | TRIGGER_EVENT_ROW;
    8260           0 :         trigdata.tg_relation = rel;
    8261           0 :         trigdata.tg_trigtuple = tuple;
    8262           0 :         trigdata.tg_newtuple = NULL;
    8263           0 :         trigdata.tg_trigger = &trig;
    8264           0 :         trigdata.tg_trigtuplebuf = scan->rs_cbuf;
    8265           0 :         trigdata.tg_newtuplebuf = InvalidBuffer;
    8266             : 
    8267           0 :         fcinfo.context = (Node *) &trigdata;
    8268             : 
    8269           0 :         RI_FKey_check_ins(&fcinfo);
    8270             :     }
    8271             : 
    8272           0 :     heap_endscan(scan);
    8273           0 :     UnregisterSnapshot(snapshot);
    8274             : }
    8275             : 
    8276             : static void
    8277         186 : CreateFKCheckTrigger(Oid myRelOid, Oid refRelOid, Constraint *fkconstraint,
    8278             :                      Oid constraintOid, Oid indexOid, bool on_insert)
    8279             : {
    8280             :     CreateTrigStmt *fk_trigger;
    8281             : 
    8282             :     /*
    8283             :      * Note: for a self-referential FK (referencing and referenced tables are
    8284             :      * the same), it is important that the ON UPDATE action fires before the
    8285             :      * CHECK action, since both triggers will fire on the same row during an
    8286             :      * UPDATE event; otherwise the CHECK trigger will be checking a non-final
    8287             :      * state of the row.  Triggers fire in name order, so we ensure this by
    8288             :      * using names like "RI_ConstraintTrigger_a_NNNN" for the action triggers
    8289             :      * and "RI_ConstraintTrigger_c_NNNN" for the check triggers.
    8290             :      */
    8291         186 :     fk_trigger = makeNode(CreateTrigStmt);
    8292         186 :     fk_trigger->trigname = "RI_ConstraintTrigger_c";
    8293         186 :     fk_trigger->relation = NULL;
    8294         186 :     fk_trigger->row = true;
    8295         186 :     fk_trigger->timing = TRIGGER_TYPE_AFTER;
    8296             : 
    8297             :     /* Either ON INSERT or ON UPDATE */
    8298         186 :     if (on_insert)
    8299             :     {
    8300          93 :         fk_trigger->funcname = SystemFuncName("RI_FKey_check_ins");
    8301          93 :         fk_trigger->events = TRIGGER_TYPE_INSERT;
    8302             :     }
    8303             :     else
    8304             :     {
    8305          93 :         fk_trigger->funcname = SystemFuncName("RI_FKey_check_upd");
    8306          93 :         fk_trigger->events = TRIGGER_TYPE_UPDATE;
    8307             :     }
    8308             : 
    8309         186 :     fk_trigger->columns = NIL;
    8310         186 :     fk_trigger->transitionRels = NIL;
    8311         186 :     fk_trigger->whenClause = NULL;
    8312         186 :     fk_trigger->isconstraint = true;
    8313         186 :     fk_trigger->deferrable = fkconstraint->deferrable;
    8314         186 :     fk_trigger->initdeferred = fkconstraint->initdeferred;
    8315         186 :     fk_trigger->constrrel = NULL;
    8316         186 :     fk_trigger->args = NIL;
    8317             : 
    8318         186 :     (void) CreateTrigger(fk_trigger, NULL, myRelOid, refRelOid, constraintOid,
    8319             :                          indexOid, true);
    8320             : 
    8321             :     /* Make changes-so-far visible */
    8322         186 :     CommandCounterIncrement();
    8323         186 : }
    8324             : 
    8325             : /*
    8326             :  * Create the triggers that implement an FK constraint.
    8327             :  *
    8328             :  * NB: if you change any trigger properties here, see also
    8329             :  * ATExecAlterConstraint.
    8330             :  */
    8331             : static void
    8332          93 : createForeignKeyTriggers(Relation rel, Oid refRelOid, Constraint *fkconstraint,
    8333             :                          Oid constraintOid, Oid indexOid)
    8334             : {
    8335             :     Oid         myRelOid;
    8336             :     CreateTrigStmt *fk_trigger;
    8337             : 
    8338          93 :     myRelOid = RelationGetRelid(rel);
    8339             : 
    8340             :     /* Make changes-so-far visible */
    8341          93 :     CommandCounterIncrement();
    8342             : 
    8343             :     /*
    8344             :      * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
    8345             :      * DELETE action on the referenced table.
    8346             :      */
    8347          93 :     fk_trigger = makeNode(CreateTrigStmt);
    8348          93 :     fk_trigger->trigname = "RI_ConstraintTrigger_a";
    8349          93 :     fk_trigger->relation = NULL;
    8350          93 :     fk_trigger->row = true;
    8351          93 :     fk_trigger->timing = TRIGGER_TYPE_AFTER;
    8352          93 :     fk_trigger->events = TRIGGER_TYPE_DELETE;
    8353          93 :     fk_trigger->columns = NIL;
    8354          93 :     fk_trigger->transitionRels = NIL;
    8355          93 :     fk_trigger->whenClause = NULL;
    8356          93 :     fk_trigger->isconstraint = true;
    8357          93 :     fk_trigger->constrrel = NULL;
    8358          93 :     switch (fkconstraint->fk_del_action)
    8359             :     {
    8360             :         case FKCONSTR_ACTION_NOACTION:
    8361          72 :             fk_trigger->deferrable = fkconstraint->deferrable;
    8362          72 :             fk_trigger->initdeferred = fkconstraint->initdeferred;
    8363          72 :             fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_del");
    8364          72 :             break;
    8365             :         case FKCONSTR_ACTION_RESTRICT:
    8366           0 :             fk_trigger->deferrable = false;
    8367           0 :             fk_trigger->initdeferred = false;
    8368           0 :             fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_del");
    8369           0 :             break;
    8370             :         case FKCONSTR_ACTION_CASCADE:
    8371          11 :             fk_trigger->deferrable = false;
    8372          11 :             fk_trigger->initdeferred = false;
    8373          11 :             fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_del");
    8374          11 :             break;
    8375             :         case FKCONSTR_ACTION_SETNULL:
    8376           7 :             fk_trigger->deferrable = false;
    8377           7 :             fk_trigger->initdeferred = false;
    8378           7 :             fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_del");
    8379           7 :             break;
    8380             :         case FKCONSTR_ACTION_SETDEFAULT:
    8381           3 :             fk_trigger->deferrable = false;
    8382           3 :             fk_trigger->initdeferred = false;
    8383           3 :             fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_del");
    8384           3 :             break;
    8385             :         default:
    8386           0 :             elog(ERROR, "unrecognized FK action type: %d",
    8387             :                  (int) fkconstraint->fk_del_action);
    8388             :             break;
    8389             :     }
    8390          93 :     fk_trigger->args = NIL;
    8391             : 
    8392          93 :     (void) CreateTrigger(fk_trigger, NULL, refRelOid, myRelOid, constraintOid,
    8393             :                          indexOid, true);
    8394             : 
    8395             :     /* Make changes-so-far visible */
    8396          93 :     CommandCounterIncrement();
    8397             : 
    8398             :     /*
    8399             :      * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
    8400             :      * UPDATE action on the referenced table.
    8401             :      */
    8402          93 :     fk_trigger = makeNode(CreateTrigStmt);
    8403          93 :     fk_trigger->trigname = "RI_ConstraintTrigger_a";
    8404          93 :     fk_trigger->relation = NULL;
    8405          93 :     fk_trigger->row = true;
    8406          93 :     fk_trigger->timing = TRIGGER_TYPE_AFTER;
    8407          93 :     fk_trigger->events = TRIGGER_TYPE_UPDATE;
    8408          93 :     fk_trigger->columns = NIL;
    8409          93 :     fk_trigger->transitionRels = NIL;
    8410          93 :     fk_trigger->whenClause = NULL;
    8411          93 :     fk_trigger->isconstraint = true;
    8412          93 :     fk_trigger->constrrel = NULL;
    8413          93 :     switch (fkconstraint->fk_upd_action)
    8414             :     {
    8415             :         case FKCONSTR_ACTION_NOACTION:
    8416          79 :             fk_trigger->deferrable = fkconstraint->deferrable;
    8417          79 :             fk_trigger->initdeferred = fkconstraint->initdeferred;
    8418          79 :             fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_upd");
    8419          79 :             break;
    8420             :         case FKCONSTR_ACTION_RESTRICT:
    8421           1 :             fk_trigger->deferrable = false;
    8422           1 :             fk_trigger->initdeferred = false;
    8423           1 :             fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_upd");
    8424           1 :             break;
    8425             :         case FKCONSTR_ACTION_CASCADE:
    8426           7 :             fk_trigger->deferrable = false;
    8427           7 :             fk_trigger->initdeferred = false;
    8428           7 :             fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_upd");
    8429           7 :             break;
    8430             :         case FKCONSTR_ACTION_SETNULL:
    8431           4 :             fk_trigger->deferrable = false;
    8432           4 :             fk_trigger->initdeferred = false;
    8433           4 :             fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_upd");
    8434           4 :             break;
    8435             :         case FKCONSTR_ACTION_SETDEFAULT:
    8436           2 :             fk_trigger->deferrable = false;
    8437           2 :             fk_trigger->initdeferred = false;
    8438           2 :             fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_upd");
    8439           2 :             break;
    8440             :         default:
    8441           0 :             elog(ERROR, "unrecognized FK action type: %d",
    8442             :                  (int) fkconstraint->fk_upd_action);
    8443             :             break;
    8444             :     }
    8445          93 :     fk_trigger->args = NIL;
    8446             : 
    8447          93 :     (void) CreateTrigger(fk_trigger, NULL, refRelOid, myRelOid, constraintOid,
    8448             :                          indexOid, true);
    8449             : 
    8450             :     /* Make changes-so-far visible */
    8451          93 :     CommandCounterIncrement();
    8452             : 
    8453             :     /*
    8454             :      * Build and execute CREATE CONSTRAINT TRIGGER statements for the CHECK
    8455             :      * action for both INSERTs and UPDATEs on the referencing table.
    8456             :      */
    8457          93 :     CreateFKCheckTrigger(myRelOid, refRelOid, fkconstraint, constraintOid,
    8458             :                          indexOid, true);
    8459          93 :     CreateFKCheckTrigger(myRelOid, refRelOid, fkconstraint, constraintOid,
    8460             :                          indexOid, false);
    8461          93 : }
    8462             : 
    8463             : /*
    8464             :  * ALTER TABLE DROP CONSTRAINT
    8465             :  *
    8466             :  * Like DROP COLUMN, we can't use the normal ALTER TABLE recursion mechanism.
    8467             :  */
    8468             : static void
    8469          51 : ATExecDropConstraint(Relation rel, const char *constrName,
    8470             :                      DropBehavior behavior,
    8471             :                      bool recurse, bool recursing,
    8472             :                      bool missing_ok, LOCKMODE lockmode)
    8473             : {
    8474             :     List       *children;
    8475             :     ListCell   *child;
    8476             :     Relation    conrel;
    8477             :     Form_pg_constraint con;
    8478             :     SysScanDesc scan;
    8479             :     ScanKeyData key;
    8480             :     HeapTuple   tuple;
    8481          51 :     bool        found = false;
    8482          51 :     bool        is_no_inherit_constraint = false;
    8483             : 
    8484             :     /* At top level, permission check was done in ATPrepCmd, else do it */
    8485          51 :     if (recursing)
    8486           6 :         ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);
    8487             : 
    8488          51 :     conrel = heap_open(ConstraintRelationId, RowExclusiveLock);
    8489             : 
    8490             :     /*
    8491             :      * Find and drop the target constraint
    8492             :      */
    8493          51 :     ScanKeyInit(&key,
    8494             :                 Anum_pg_constraint_conrelid,
    8495             :                 BTEqualStrategyNumber, F_OIDEQ,
    8496             :                 ObjectIdGetDatum(RelationGetRelid(rel)));
    8497          51 :     scan = systable_beginscan(conrel, ConstraintRelidIndexId,
    8498             :                               true, NULL, 1, &key);
    8499             : 
    8500          51 :     while (HeapTupleIsValid(tuple = systable_getnext(scan)))
    8501             :     {
    8502             :         ObjectAddress conobj;
    8503             : 
    8504          62 :         con = (Form_pg_constraint) GETSTRUCT(tuple);
    8505             : 
    8506          62 :         if (strcmp(NameStr(con->conname), constrName) != 0)
    8507          15 :             continue;
    8508             : 
    8509             :         /* Don't drop inherited constraints */
    8510          47 :         if (con->coninhcount > 0 && !recursing)
    8511           3 :             ereport(ERROR,
    8512             :                     (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    8513             :                      errmsg("cannot drop inherited constraint \"%s\" of relation \"%s\"",
    8514             :                             constrName, RelationGetRelationName(rel))));
    8515             : 
    8516          44 :         is_no_inherit_constraint = con->connoinherit;
    8517             : 
    8518             :         /*
    8519             :          * If it's a foreign-key constraint, we'd better lock the referenced
    8520             :          * table and check that that's not in use, just as we've already done
    8521             :          * for the constrained table (else we might, eg, be dropping a trigger
    8522             :          * that has unfired events).  But we can/must skip that in the
    8523             :          * self-referential case.
    8524             :          */
    8525          48 :         if (con->contype == CONSTRAINT_FOREIGN &&
    8526           4 :             con->confrelid != RelationGetRelid(rel))
    8527             :         {
    8528             :             Relation    frel;
    8529             : 
    8530             :             /* Must match lock taken by RemoveTriggerById: */
    8531           4 :             frel = heap_open(con->confrelid, AccessExclusiveLock);
    8532           4 :             CheckTableNotInUse(frel, "ALTER TABLE");
    8533           3 :             heap_close(frel, NoLock);
    8534             :         }
    8535             : 
    8536             :         /*
    8537             :          * Perform the actual constraint deletion
    8538             :          */
    8539          43 :         conobj.classId = ConstraintRelationId;
    8540          43 :         conobj.objectId = HeapTupleGetOid(tuple);
    8541          43 :         conobj.objectSubId = 0;
    8542             : 
    8543          43 :         performDeletion(&conobj, behavior, 0);
    8544             : 
    8545          38 :         found = true;
    8546             : 
    8547             :         /* constraint found and dropped -- no need to keep looping */
    8548          38 :         break;
    8549             :     }
    8550             : 
    8551          42 :     systable_endscan(scan);
    8552             : 
    8553          42 :     if (!found)
    8554             :     {
    8555           4 :         if (!missing_ok)
    8556             :         {
    8557           2 :             ereport(ERROR,
    8558             :                     (errcode(ERRCODE_UNDEFINED_OBJECT),
    8559             :                      errmsg("constraint \"%s\" of relation \"%s\" does not exist",
    8560             :                             constrName, RelationGetRelationName(rel))));
    8561             :         }
    8562             :         else
    8563             :         {
    8564           2 :             ereport(NOTICE,
    8565             :                     (errmsg("constraint \"%s\" of relation \"%s\" does not exist, skipping",
    8566             :                             constrName, RelationGetRelationName(rel))));
    8567           2 :             heap_close(conrel, RowExclusiveLock);
    8568          41 :             return;
    8569             :         }
    8570             :     }
    8571             : 
    8572             :     /*
    8573             :      * Propagate to children as appropriate.  Unlike most other ALTER
    8574             :      * routines, we have to do this one level of recursion at a time; we can't
    8575             :      * use find_all_inheritors to do it in one pass.
    8576             :      */
    8577          38 :     if (!is_no_inherit_constraint)
    8578          21 :         children = find_inheritance_children(RelationGetRelid(rel), lockmode);
    8579             :     else
    8580          17 :         children = NIL;
    8581             : 
    8582             :     /*
    8583             :      * For a partitioned table, if partitions exist and we are told not to
    8584             :      * recurse, it's a user error.  It doesn't make sense to have a constraint
    8585             :      * be defined only on the parent, especially if it's a partitioned table.
    8586             :      */
    8587          38 :     if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&
    8588           5 :         children != NIL && !recurse)
    8589           1 :         ereport(ERROR,
    8590             :                 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    8591             :                  errmsg("cannot remove constraint from only the partitioned table when partitions exist"),
    8592             :                  errhint("Do not specify the ONLY keyword.")));
    8593             : 
    8594          45 :     foreach(child, children)
    8595             :     {
    8596           8 :         Oid         childrelid = lfirst_oid(child);
    8597             :         Relation    childrel;
    8598             :         HeapTuple   copy_tuple;
    8599             : 
    8600             :         /* find_inheritance_children already got lock */
    8601           8 :         childrel = heap_open(childrelid, NoLock);
    8602           8 :         CheckTableNotInUse(childrel, "ALTER TABLE");
    8603             : 
    8604           8 :         ScanKeyInit(&key,
    8605             :                     Anum_pg_constraint_conrelid,
    8606             :                     BTEqualStrategyNumber, F_OIDEQ,
    8607             :                     ObjectIdGetDatum(childrelid));
    8608           8 :         scan = systable_beginscan(conrel, ConstraintRelidIndexId,
    8609             :                                   true, NULL, 1, &key);
    8610             : 
    8611             :         /* scan for matching tuple - there should only be one */
    8612           8 :         while (HeapTupleIsValid(tuple = systable_getnext(scan)))
    8613             :         {
    8614           8 :             con = (Form_pg_constraint) GETSTRUCT(tuple);
    8615             : 
    8616             :             /* Right now only CHECK constraints can be inherited */
    8617           8 :             if (con->contype != CONSTRAINT_CHECK)
    8618           0 :                 continue;
    8619             : 
    8620           8 :             if (strcmp(NameStr(con->conname), constrName) == 0)
    8621           8 :                 break;
    8622             :         }
    8623             : 
    8624           8 :         if (!HeapTupleIsValid(tuple))
    8625           0 :             ereport(ERROR,
    8626             :                     (errcode(ERRCODE_UNDEFINED_OBJECT),
    8627             :                      errmsg("constraint \"%s\" of relation \"%s\" does not exist",
    8628             :                             constrName,
    8629             :                             RelationGetRelationName(childrel))));
    8630             : 
    8631           8 :         copy_tuple = heap_copytuple(tuple);
    8632             : 
    8633           8 :         systable_endscan(scan);
    8634             : 
    8635           8 :         con = (Form_pg_constraint) GETSTRUCT(copy_tuple);
    8636             : 
    8637           8 :         if (con->coninhcount <= 0)    /* shouldn't happen */
    8638           0 :             elog(ERROR, "relation %u has non-inherited constraint \"%s\"",
    8639             :                  childrelid, constrName);
    8640             : 
    8641           8 :         if (recurse)
    8642             :         {
    8643             :             /*
    8644             :              * If the child constraint has other definition sources, just
    8645             :              * decrement its inheritance count; if not, recurse to delete it.
    8646             :              */
    8647           7 :             if (con->coninhcount == 1 && !con->conislocal)
    8648             :             {
    8649             :                 /* Time to delete this child constraint, too */
    8650           6 :                 ATExecDropConstraint(childrel, constrName, behavior,
    8651             :                                      true, true,
    8652             :                                      false, lockmode);
    8653             :             }
    8654             :             else
    8655             :             {
    8656             :                 /* Child constraint must survive my deletion */
    8657           1 :                 con->coninhcount--;
    8658           1 :                 CatalogTupleUpdate(conrel, &copy_tuple->t_self, copy_tuple);
    8659             : 
    8660             :                 /* Make update visible */
    8661           1 :                 CommandCounterIncrement();
    8662             :             }
    8663             :         }
    8664             :         else
    8665             :         {
    8666             :             /*
    8667             :              * If we were told to drop ONLY in this table (no recursion), we
    8668             :              * need to mark the inheritors' constraints as locally defined
    8669             :              * rather than inherited.
    8670             :              */
    8671           1 :             con->coninhcount--;
    8672           1 :             con->conislocal = true;
    8673             : 
    8674           1 :             CatalogTupleUpdate(conrel, &copy_tuple->t_self, copy_tuple);
    8675             : 
    8676             :             /* Make update visible */
    8677           1 :             CommandCounterIncrement();
    8678             :         }
    8679             : 
    8680           8 :         heap_freetuple(copy_tuple);
    8681             : 
    8682           8 :         heap_close(childrel, NoLock);
    8683             :     }
    8684             : 
    8685          37 :     heap_close(conrel, RowExclusiveLock);
    8686             : }
    8687             : 
    8688             : /*
    8689             :  * ALTER COLUMN TYPE
    8690             :  */
    8691             : static void
    8692          90 : ATPrepAlterColumnType(List **wqueue,
    8693             :                       AlteredTableInfo *tab, Relation rel,
    8694             :                       bool recurse, bool recursing,
    8695             :                       AlterTableCmd *cmd, LOCKMODE lockmode)
    8696             : {
    8697          90 :     char       *colName = cmd->name;
    8698          90 :     ColumnDef  *def = (ColumnDef *) cmd->def;
    8699          90 :     TypeName   *typeName = def->typeName;
    8700          90 :     Node       *transform = def->cooked_default;
    8701             :     HeapTuple   tuple;
    8702             :     Form_pg_attribute attTup;
    8703             :     AttrNumber  attnum;
    8704             :     Oid         targettype;
    8705             :     int32       targettypmod;
    8706             :     Oid         targetcollid;
    8707             :     NewColumnValue *newval;
    8708          90 :     ParseState *pstate = make_parsestate(NULL);
    8709             :     AclResult   aclresult;
    8710             :     bool        is_expr;
    8711             : 
    8712          90 :     if (rel->rd_rel->reloftype && !recursing)
    8713           1 :         ereport(ERROR,
    8714             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    8715             :                  errmsg("cannot alter column type of typed table")));
    8716             : 
    8717             :     /* lookup the attribute so we can check inheritance status */
    8718          89 :     tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
    8719          89 :     if (!HeapTupleIsValid(tuple))
    8720           0 :         ereport(ERROR,
    8721             :                 (errcode(ERRCODE_UNDEFINED_COLUMN),
    8722             :                  errmsg("column \"%s\" of relation \"%s\" does not exist",
    8723             :                         colName, RelationGetRelationName(rel))));
    8724          89 :     attTup = (Form_pg_attribute) GETSTRUCT(tuple);
    8725          89 :     attnum = attTup->attnum;
    8726             : 
    8727             :     /* Can't alter a system attribute */
    8728          89 :     if (attnum <= 0)
    8729           0 :         ereport(ERROR,
    8730             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    8731             :                  errmsg("cannot alter system column \"%s\"",
    8732             :                         colName)));
    8733             : 
    8734             :     /* Don't alter inherited columns */
    8735          89 :     if (attTup->attinhcount > 0 && !recursing)
    8736           1 :         ereport(ERROR,
    8737             :                 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    8738             :                  errmsg("cannot alter inherited column \"%s\"",
    8739             :                         colName)));
    8740             : 
    8741             :     /* Don't alter columns used in the partition key */
    8742          88 :     if (is_partition_attr(rel, attnum, &is_expr))
    8743             :     {
    8744           3 :         if (!is_expr)
    8745           2 :             ereport(ERROR,
    8746             :                     (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    8747             :                      errmsg("cannot alter type of column named in partition key")));
    8748             :         else
    8749           1 :             ereport(ERROR,
    8750             :                     (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    8751             :                      errmsg("cannot alter type of column referenced in partition key expression")));
    8752             :     }
    8753             : 
    8754             :     /* Look up the target type */
    8755          85 :     typenameTypeIdAndMod(NULL, typeName, &targettype, &targettypmod);
    8756             : 
    8757          85 :     aclresult = pg_type_aclcheck(targettype, GetUserId(), ACL_USAGE);
    8758          85 :     if (aclresult != ACLCHECK_OK)
    8759           2 :         aclcheck_error_type(aclresult, targettype);
    8760             : 
    8761             :     /* And the collation */
    8762          83 :     targetcollid = GetColumnDefCollation(NULL, def, targettype);
    8763             : 
    8764             :     /* make sure datatype is legal for a column */
    8765          83 :     CheckAttributeType(colName, targettype, targetcollid,
    8766          83 :                        list_make1_oid(rel->rd_rel->reltype),
    8767             :                        false);
    8768             : 
    8769         100 :     if (tab->relkind == RELKIND_RELATION ||
    8770          18 :         tab->relkind == RELKIND_PARTITIONED_TABLE)
    8771             :     {
    8772             :         /*
    8773             :          * Set up an expression to transform the old data value to the new
    8774             :          * type. If a USING option was given, use the expression as
    8775             :          * transformed by transformAlterTableStmt, else just take the old
    8776             :          * value and try to coerce it.  We do this first so that type
    8777             :          * incompatibility can be detected before we waste effort, and because
    8778             :          * we need the expression to be parsed against the original table row
    8779             :          * type.
    8780             :          */
    8781          65 :         if (!transform)
    8782             :         {
    8783          41 :             transform = (Node *) makeVar(1, attnum,
    8784             :                                          attTup->atttypid, attTup->atttypmod,
    8785             :                                          attTup->attcollation,
    8786             :                                          0);
    8787             :         }
    8788             : 
    8789          65 :         transform = coerce_to_target_type(pstate,
    8790             :                                           transform, exprType(transform),
    8791             :                                           targettype, targettypmod,
    8792             :                                           COERCION_ASSIGNMENT,
    8793             :                                           COERCE_IMPLICIT_CAST,
    8794             :                                           -1);
    8795          65 :         if (transform == NULL)
    8796             :         {
    8797             :             /* error text depends on whether USING was specified or not */
    8798           3 :             if (def->cooked_default != NULL)
    8799           1 :                 ereport(ERROR,
    8800             :                         (errcode(ERRCODE_DATATYPE_MISMATCH),
    8801             :                          errmsg("result of USING clause for column \"%s\""
    8802             :                                 " cannot be cast automatically to type %s",
    8803             :                                 colName, format_type_be(targettype)),
    8804             :                          errhint("You might need to add an explicit cast.")));
    8805             :             else
    8806           2 :                 ereport(ERROR,
    8807             :                         (errcode(ERRCODE_DATATYPE_MISMATCH),
    8808             :                          errmsg("column \"%s\" cannot be cast automatically to type %s",
    8809             :                                 colName, format_type_be(targettype)),
    8810             :                 /* translator: USING is SQL, don't translate it */
    8811             :                          errhint("You might need to specify \"USING %s::%s\".",
    8812             :                                  quote_identifier(colName),
    8813             :                                  format_type_with_typemod(targettype,
    8814             :                                                           targettypmod))));
    8815             :         }
    8816             : 
    8817             :         /* Fix collations after all else */
    8818          62 :         assign_expr_collations(pstate, transform);
    8819             : 
    8820             :         /* Plan the expr now so we can accurately assess the need to rewrite. */
    8821          62 :         transform = (Node *) expression_planner((Expr *) transform);
    8822             : 
    8823             :         /*
    8824             :          * Add a work queue item to make ATRewriteTable update the column
    8825             :          * contents.
    8826             :          */
    8827          62 :         newval = (NewColumnValue *) palloc0(sizeof(NewColumnValue));
    8828          62 :         newval->attnum = attnum;
    8829          62 :         newval->expr = (Expr *) transform;
    8830             : 
    8831          62 :         tab->newvals = lappend(tab->newvals, newval);
    8832         124 :         if (ATColumnChangeRequiresRewrite(transform, attnum))
    8833          51 :             tab->rewrite |= AT_REWRITE_COLUMN_REWRITE;
    8834             :     }
    8835          17 :     else if (transform)
    8836           2 :         ereport(ERROR,
    8837             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    8838             :                  errmsg("\"%s\" is not a table",
    8839             :                         RelationGetRelationName(rel))));
    8840             : 
    8841         146 :     if (tab->relkind == RELKIND_COMPOSITE_TYPE ||
    8842          69 :         tab->relkind == RELKIND_FOREIGN_TABLE)
    8843             :     {
    8844             :         /*
    8845             :          * For composite types, do this check now.  Tables will check it later
    8846             :          * when the table is being rewritten.
    8847             :          */
    8848          15 :         find_composite_type_dependencies(rel->rd_rel->reltype, rel, NULL);
    8849             :     }
    8850             : 
    8851          74 :     ReleaseSysCache(tuple);
    8852             : 
    8853             :     /*
    8854             :      * Recurse manually by queueing a new command for each child, if
    8855             :      * necessary. We cannot apply ATSimpleRecursion here because we need to
    8856             :      * remap attribute numbers in the USING expression, if any.
    8857             :      *
    8858             :      * If we are told not to recurse, there had better not be any child
    8859             :      * tables; else the alter would put them out of step.
    8860             :      */
    8861          74 :     if (recurse)
    8862             :     {
    8863          50 :         Oid         relid = RelationGetRelid(rel);
    8864             :         ListCell   *child;
    8865             :         List       *children;
    8866             : 
    8867          50 :         children = find_all_inheritors(relid, lockmode, NULL);
    8868             : 
    8869             :         /*
    8870             :          * find_all_inheritors does the recursive search of the inheritance
    8871             :          * hierarchy, so all we have to do is process all of the relids in the
    8872             :          * list that it returns.
    8873             :          */
    8874         118 :         foreach(child, children)
    8875             :         {
    8876          71 :             Oid         childrelid = lfirst_oid(child);
    8877             :             Relation    childrel;
    8878             : 
    8879          71 :             if (childrelid == relid)
    8880          50 :                 continue;
    8881             : 
    8882             :             /* find_all_inheritors already got lock */
    8883          21 :             childrel = relation_open(childrelid, NoLock);
    8884          21 :             CheckTableNotInUse(childrel, "ALTER TABLE");
    8885             : 
    8886             :             /*
    8887             :              * Remap the attribute numbers.  If no USING expression was
    8888             :              * specified, there is no need for this step.
    8889             :              */
    8890          21 :             if (def->cooked_default)
    8891             :             {
    8892             :                 AttrNumber *attmap;
    8893             :                 bool        found_whole_row;
    8894             : 
    8895             :                 /* create a copy to scribble on */
    8896          10 :                 cmd = copyObject(cmd);
    8897             : 
    8898          10 :                 attmap = convert_tuples_by_name_map(RelationGetDescr(childrel),
    8899             :                                                     RelationGetDescr(rel),
    8900             :                                                     gettext_noop("could not convert row type"));
    8901          20 :                 ((ColumnDef *) cmd->def)->cooked_default =
    8902          10 :                     map_variable_attnos(def->cooked_default,
    8903             :                                         1, 0,
    8904          10 :                                         attmap, RelationGetDescr(rel)->natts,
    8905             :                                         InvalidOid, &found_whole_row);
    8906          10 :                 if (found_whole_row)
    8907           1 :                     ereport(ERROR,
    8908             :                             (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    8909             :                              errmsg("cannot convert whole-row table reference"),
    8910             :                              errdetail("USING expression contains a whole-row table reference.")));
    8911           9 :                 pfree(attmap);
    8912             :             }
    8913          20 :             ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode);
    8914          18 :             relation_close(childrel, NoLock);
    8915             :         }
    8916             :     }
    8917          30 :     else if (!recursing &&
    8918           6 :              find_inheritance_children(RelationGetRelid(rel), NoLock) != NIL)
    8919           0 :         ereport(ERROR,
    8920             :                 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    8921             :                  errmsg("type of inherited column \"%s\" must be changed in child tables too",
    8922             :                         colName)));
    8923             : 
    8924          71 :     if (tab->relkind == RELKIND_COMPOSITE_TYPE)
    8925           6 :         ATTypedTableRecursion(wqueue, rel, cmd, lockmode);
    8926          70 : }
    8927             : 
    8928             : /*
    8929             :  * When the data type of a column is changed, a rewrite might not be required
    8930             :  * if the new type is sufficiently identical to the old one, and the USING
    8931             :  * clause isn't trying to insert some other value.  It's safe to skip the
    8932             :  * rewrite if the old type is binary coercible to the new type, or if the
    8933             :  * new type is an unconstrained domain over the old type.  In the case of a
    8934             :  * constrained domain, we could get by with scanning the table and checking
    8935             :  * the constraint rather than actually rewriting it, but we don't currently
    8936             :  * try to do that.
    8937             :  */
    8938             : static bool
    8939          62 : ATColumnChangeRequiresRewrite(Node *expr, AttrNumber varattno)
    8940             : {
    8941          62 :     Assert(expr != NULL);
    8942             : 
    8943             :     for (;;)
    8944             :     {
    8945             :         /* only one varno, so no need to check that */
    8946          67 :         if (IsA(expr, Var) &&((Var *) expr)->varattno == varattno)
    8947          11 :             return false;
    8948          56 :         else if (IsA(expr, RelabelType))
    8949           4 :             expr = (Node *) ((RelabelType *) expr)->arg;
    8950          52 :         else if (IsA(expr, CoerceToDomain))
    8951             :         {
    8952           1 :             CoerceToDomain *d = (CoerceToDomain *) expr;
    8953             : 
    8954           1 :             if (DomainHasConstraints(d->resulttype))
    8955           0 :                 return true;
    8956           1 :             expr = (Node *) d->arg;
    8957             :         }
    8958             :         else
    8959          51 :             return true;
    8960           5 :     }
    8961             : }
    8962             : 
    8963             : /*
    8964             :  * ALTER COLUMN .. SET DATA TYPE
    8965             :  *
    8966             :  * Return the address of the modified column.
    8967             :  */
    8968             : static ObjectAddress
    8969          69 : ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
    8970             :                       AlterTableCmd *cmd, LOCKMODE lockmode)
    8971             : {
    8972          69 :     char       *colName = cmd->name;
    8973          69 :     ColumnDef  *def = (ColumnDef *) cmd->def;
    8974          69 :     TypeName   *typeName = def->typeName;
    8975             :     HeapTuple   heapTup;
    8976             :     Form_pg_attribute attTup,
    8977             :                 attOldTup;
    8978             :     AttrNumber  attnum;
    8979             :     HeapTuple   typeTuple;
    8980             :     Form_pg_type tform;
    8981             :     Oid         targettype;
    8982             :     int32       targettypmod;
    8983             :     Oid         targetcollid;
    8984             :     Node       *defaultexpr;
    8985             :     Relation    attrelation;
    8986             :     Relation    depRel;
    8987             :     ScanKeyData key[3];
    8988             :     SysScanDesc scan;
    8989             :     HeapTuple   depTup;
    8990             :     ObjectAddress address;
    8991             : 
    8992          69 :     attrelation = heap_open(AttributeRelationId, RowExclusiveLock);
    8993             : 
    8994             :     /* Look up the target column */
    8995          69 :     heapTup = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
    8996          69 :     if (!HeapTupleIsValid(heapTup)) /* shouldn't happen */
    8997           0 :         ereport(ERROR,
    8998             :                 (errcode(ERRCODE_UNDEFINED_COLUMN),
    8999             :                  errmsg("column \"%s\" of relation \"%s\" does not exist",
    9000             :                         colName, RelationGetRelationName(rel))));
    9001          69 :     attTup = (Form_pg_attribute) GETSTRUCT(heapTup);
    9002          69 :     attnum = attTup->attnum;
    9003          69 :     attOldTup = TupleDescAttr(tab->oldDesc, attnum - 1);
    9004             : 
    9005             :     /* Check for multiple ALTER TYPE on same column --- can't cope */
    9006         138 :     if (attTup->atttypid != attOldTup->atttypid ||
    9007          69 :         attTup->atttypmod != attOldTup->atttypmod)
    9008           0 :         ereport(ERROR,
    9009             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    9010             :                  errmsg("cannot alter type of column \"%s\" twice",
    9011             :                         colName)));
    9012             : 
    9013             :     /* Look up the target type (should not fail, since prep found it) */
    9014          69 :     typeTuple = typenameType(NULL, typeName, &targettypmod);
    9015          69 :     tform = (Form_pg_type) GETSTRUCT(typeTuple);
    9016          69 :     targettype = HeapTupleGetOid(typeTuple);
    9017             :     /* And the collation */
    9018          69 :     targetcollid = GetColumnDefCollation(NULL, def, targettype);
    9019             : 
    9020             :     /*
    9021             :      * If there is a default expression for the column, get it and ensure we
    9022             :      * can coerce it to the new datatype.  (We must do this before changing
    9023             :      * the column type, because build_column_default itself will try to
    9024             :      * coerce, and will not issue the error message we want if it fails.)
    9025             :      *
    9026             :      * We remove any implicit coercion steps at the top level of the old
    9027             :      * default expression; this has been agreed to satisfy the principle of
    9028             :      * least surprise.  (The conversion to the new column type should act like
    9029             :      * it started from what the user sees as the stored expression, and the
    9030             :      * implicit coercions aren't going to be shown.)
    9031             :      */
    9032          69 :     if (attTup->atthasdef)
    9033             :     {
    9034           2 :         defaultexpr = build_column_default(rel, attnum);
    9035           2 :         Assert(defaultexpr);
    9036           2 :         defaultexpr = strip_implicit_coercions(defaultexpr);
    9037           2 :         defaultexpr = coerce_to_target_type(NULL,   /* no UNKNOWN params */
    9038             :                                             defaultexpr, exprType(defaultexpr),
    9039             :                                             targettype, targettypmod,
    9040             :                                             COERCION_ASSIGNMENT,
    9041             :                                             COERCE_IMPLICIT_CAST,
    9042             :                                             -1);
    9043           2 :         if (defaultexpr == NULL)
    9044           1 :             ereport(ERROR,
    9045             :                     (errcode(ERRCODE_DATATYPE_MISMATCH),
    9046             :                      errmsg("default for column \"%s\" cannot be cast automatically to type %s",
    9047             :                             colName, format_type_be(targettype))));
    9048             :     }
    9049             :     else
    9050          67 :         defaultexpr = NULL;
    9051             : 
    9052             :     /*
    9053             :      * Find everything that depends on the column (constraints, indexes, etc),
    9054             :      * and record enough information to let us recreate the objects.
    9055             :      *
    9056             :      * The actual recreation does not happen here, but only after we have
    9057             :      * performed all the individual ALTER TYPE operations.  We have to save
    9058             :      * the info before executing ALTER TYPE, though, else the deparser will
    9059             :      * get confused.
    9060             :      *
    9061             :      * There could be multiple entries for the same object, so we must check
    9062             :      * to ensure we process each one only once.  Note: we assume that an index
    9063             :      * that implements a constraint will not show a direct dependency on the
    9064             :      * column.
    9065             :      */
    9066          68 :     depRel = heap_open(DependRelationId, RowExclusiveLock);
    9067             : 
    9068          68 :     ScanKeyInit(&key[0],
    9069             :                 Anum_pg_depend_refclassid,
    9070             :                 BTEqualStrategyNumber, F_OIDEQ,
    9071             :                 ObjectIdGetDatum(RelationRelationId));
    9072          68 :     ScanKeyInit(&key[1],
    9073             :                 Anum_pg_depend_refobjid,
    9074             :                 BTEqualStrategyNumber, F_OIDEQ,
    9075             :                 ObjectIdGetDatum(RelationGetRelid(rel)));
    9076          68 :     ScanKeyInit(&key[2],
    9077             :                 Anum_pg_depend_refobjsubid,
    9078             :                 BTEqualStrategyNumber, F_INT4EQ,
    9079             :                 Int32GetDatum((int32) attnum));
    9080             : 
    9081          68 :     scan = systable_beginscan(depRel, DependReferenceIndexId, true,
    9082             :                               NULL, 3, key);
    9083             : 
    9084         185 :     while (HeapTupleIsValid(depTup = systable_getnext(scan)))
    9085             :     {
    9086          49 :         Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(depTup);
    9087             :         ObjectAddress foundObject;
    9088             : 
    9089             :         /* We don't expect any PIN dependencies on columns */
    9090          49 :         if (foundDep->deptype == DEPENDENCY_PIN)
    9091           0 :             elog(ERROR, "cannot alter type of a pinned column");
    9092             : 
    9093          49 :         foundObject.classId = foundDep->classid;
    9094          49 :         foundObject.objectId = foundDep->objid;
    9095          49 :         foundObject.objectSubId = foundDep->objsubid;
    9096             : 
    9097          49 :         switch (getObjectClass(&foundObject))
    9098             :         {
    9099             :             case OCLASS_CLASS:
    9100             :                 {
    9101          16 :                     char        relKind = get_rel_relkind(foundObject.objectId);
    9102             : 
    9103          16 :                     if (relKind == RELKIND_INDEX)
    9104             :                     {
    9105          12 :                         Assert(foundObject.objectSubId == 0);
    9106          12 :                         if (!list_member_oid(tab->changedIndexOids, foundObject.objectId))
    9107             :                         {
    9108          12 :                             tab->changedIndexOids = lappend_oid(tab->changedIndexOids,
    9109             :                                                                 foundObject.objectId);
    9110          12 :                             tab->changedIndexDefs = lappend(tab->changedIndexDefs,
    9111          12 :                                                             pg_get_indexdef_string(foundObject.objectId));
    9112             :                         }
    9113             :                     }
    9114           4 :                     else if (relKind == RELKIND_SEQUENCE)
    9115             :                     {
    9116             :                         /*
    9117             :                          * This must be a SERIAL column's sequence.  We need
    9118             :                          * not do anything to it.
    9119             :                          */
    9120           4 :                         Assert(foundObject.objectSubId == 0);
    9121             :                     }
    9122             :                     else
    9123             :                     {
    9124             :                         /* Not expecting any other direct dependencies... */
    9125           0 :                         elog(ERROR, "unexpected object depending on column: %s",
    9126             :                              getObjectDescription(&foundObject));
    9127             :                     }
    9128          16 :                     break;
    9129             :                 }
    9130             : 
    9131             :             case OCLASS_CONSTRAINT:
    9132          31 :                 Assert(foundObject.objectSubId == 0);
    9133          31 :                 if (!list_member_oid(tab->changedConstraintOids,
    9134             :                                      foundObject.objectId))
    9135             :                 {
    9136          21 :                     char       *defstring = pg_get_constraintdef_command(foundObject.objectId);
    9137             : 
    9138             :                     /*
    9139             :                      * Put NORMAL dependencies at the front of the list and
    9140             :                      * AUTO dependencies at the back.  This makes sure that
    9141             :                      * foreign-key constraints depending on this column will
    9142             :                      * be dropped before unique or primary-key constraints of
    9143             :                      * the column; which we must have because the FK
    9144             :                      * constraints depend on the indexes belonging to the
    9145             :                      * unique constraints.
    9146             :                      */
    9147          21 :                     if (foundDep->deptype == DEPENDENCY_NORMAL)
    9148             :                     {
    9149          13 :                         tab->changedConstraintOids =
    9150          13 :                             lcons_oid(foundObject.objectId,
    9151             :                                       tab->changedConstraintOids);
    9152          13 :                         tab->changedConstraintDefs =
    9153          13 :                             lcons(defstring,
    9154             :                                   tab->changedConstraintDefs);
    9155             :                     }
    9156             :                     else
    9157             :                     {
    9158           8 :                         tab->changedConstraintOids =
    9159           8 :                             lappend_oid(tab->changedConstraintOids,
    9160             :                                         foundObject.objectId);
    9161           8 :                         tab->changedConstraintDefs =
    9162           8 :                             lappend(tab->changedConstraintDefs,
    9163             :                                     defstring);
    9164             :                     }
    9165             :                 }
    9166          31 :                 break;
    9167             : 
    9168             :             case OCLASS_REWRITE:
    9169             :                 /* XXX someday see if we can cope with revising views */
    9170           0 :                 ereport(ERROR,
    9171             :                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    9172             :                          errmsg("cannot alter type of a column used by a view or rule"),
    9173             :                          errdetail("%s depends on column \"%s\"",
    9174             :                                    getObjectDescription(&foundObject),
    9175             :                                    colName)));
    9176             :                 break;
    9177             : 
    9178             :             case OCLASS_TRIGGER:
    9179             : 
    9180             :                 /*
    9181             :                  * A trigger can depend on a column because the column is
    9182             :                  * specified as an update target, or because the column is
    9183             :                  * used in the trigger's WHEN condition.  The first case would
    9184             :                  * not require any extra work, but the second case would
    9185             :                  * require updating the WHEN expression, which will take a
    9186             :                  * significant amount of new code.  Since we can't easily tell
    9187             :                  * which case applies, we punt for both.  FIXME someday.
    9188             :                  */
    9189           0 :                 ereport(ERROR,
    9190             :                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    9191             :                          errmsg("cannot alter type of a column used in a trigger definition"),
    9192             :                          errdetail("%s depends on column \"%s\"",
    9193             :                                    getObjectDescription(&foundObject),
    9194             :                                    colName)));
    9195             :                 break;
    9196             : 
    9197             :             case OCLASS_POLICY:
    9198             : 
    9199             :                 /*
    9200             :                  * A policy can depend on a column because the column is
    9201             :                  * specified in the policy's USING or WITH CHECK qual
    9202             :                  * expressions.  It might be possible to rewrite and recheck
    9203             :                  * the policy expression, but punt for now.  It's certainly
    9204             :                  * easy enough to remove and recreate the policy; still, FIXME
    9205             :                  * someday.
    9206             :                  */
    9207           0 :                 ereport(ERROR,
    9208             :                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    9209             :                          errmsg("cannot alter type of a column used in a policy definition"),
    9210             :                          errdetail("%s depends on column \"%s\"",
    9211             :                                    getObjectDescription(&foundObject),
    9212             :                                    colName)));
    9213             :                 break;
    9214             : 
    9215             :             case OCLASS_DEFAULT:
    9216             : 
    9217             :                 /*
    9218             :                  * Ignore the column's default expression, since we will fix
    9219             :                  * it below.
    9220             :                  */
    9221           1 :                 Assert(defaultexpr);
    9222           1 :                 break;
    9223             : 
    9224             :             case OCLASS_STATISTIC_EXT:
    9225             : 
    9226             :                 /*
    9227             :                  * Give the extended-stats machinery a chance to fix anything
    9228             :                  * that this column type change would break.
    9229             :                  */
    9230           1 :                 UpdateStatisticsForTypeChange(foundObject.objectId,
    9231             :                                               RelationGetRelid(rel), attnum,
    9232             :                                               attTup->atttypid, targettype);
    9233           1 :                 break;
    9234             : 
    9235             :             case OCLASS_PROC:
    9236             :             case OCLASS_TYPE:
    9237             :             case OCLASS_CAST:
    9238             :             case OCLASS_COLLATION:
    9239             :             case OCLASS_CONVERSION:
    9240             :             case OCLASS_LANGUAGE:
    9241             :             case OCLASS_LARGEOBJECT:
    9242             :             case OCLASS_OPERATOR:
    9243             :             case OCLASS_OPCLASS:
    9244             :             case OCLASS_OPFAMILY:
    9245             :             case OCLASS_AM:
    9246             :             case OCLASS_AMOP:
    9247             :             case OCLASS_AMPROC:
    9248             :             case OCLASS_SCHEMA:
    9249             :             case OCLASS_TSPARSER:
    9250             :             case OCLASS_TSDICT:
    9251             :             case OCLASS_TSTEMPLATE:
    9252             :             case OCLASS_TSCONFIG:
    9253             :             case OCLASS_ROLE:
    9254             :             case OCLASS_DATABASE:
    9255             :             case OCLASS_TBLSPACE:
    9256             :             case OCLASS_FDW:
    9257             :             case OCLASS_FOREIGN_SERVER:
    9258             :             case OCLASS_USER_MAPPING:
    9259             :             case OCLASS_DEFACL:
    9260             :             case OCLASS_EXTENSION:
    9261             :             case OCLASS_EVENT_TRIGGER:
    9262             :             case OCLASS_PUBLICATION:
    9263             :             case OCLASS_PUBLICATION_REL:
    9264             :             case OCLASS_SUBSCRIPTION:
    9265             :             case OCLASS_TRANSFORM:
    9266             : 
    9267             :                 /*
    9268             :                  * We don't expect any of these sorts of objects to depend on
    9269             :                  * a column.
    9270             :                  */
    9271           0 :                 elog(ERROR, "unexpected object depending on column: %s",
    9272             :                      getObjectDescription(&foundObject));
    9273             :                 break;
    9274             : 
    9275             :                 /*
    9276             :                  * There's intentionally no default: case here; we want the
    9277             :                  * compiler to warn if a new OCLASS hasn't been handled above.
    9278             :                  */
    9279             :         }
    9280             :     }
    9281             : 
    9282          68 :     systable_endscan(scan);
    9283             : 
    9284             :     /*
    9285             :      * Now scan for dependencies of this column on other things.  The only
    9286             :      * thing we should find is the dependency on the column datatype, which we
    9287             :      * want to remove, and possibly a collation dependency.
    9288             :      */
    9289          68 :     ScanKeyInit(&key[0],
    9290             :                 Anum_pg_depend_classid,
    9291             :                 BTEqualStrategyNumber, F_OIDEQ,
    9292             :                 ObjectIdGetDatum(RelationRelationId));
    9293          68 :     ScanKeyInit(&key[1],
    9294             :                 Anum_pg_depend_objid,
    9295             :                 BTEqualStrategyNumber, F_OIDEQ,
    9296             :                 ObjectIdGetDatum(RelationGetRelid(rel)));
    9297          68 :     ScanKeyInit(&key[2],
    9298             :                 Anum_pg_depend_objsubid,
    9299             :                 BTEqualStrategyNumber, F_INT4EQ,
    9300             :                 Int32GetDatum((int32) attnum));
    9301             : 
    9302          68 :     scan = systable_beginscan(depRel, DependDependerIndexId, true,
    9303             :                               NULL, 3, key);
    9304             : 
    9305         136 :     while (HeapTupleIsValid(depTup = systable_getnext(scan)))
    9306             :     {
    9307           0 :         Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(depTup);
    9308             : 
    9309           0 :         if (foundDep->deptype != DEPENDENCY_NORMAL)
    9310           0 :             elog(ERROR, "found unexpected dependency type '%c'",
    9311             :                  foundDep->deptype);
    9312           0 :         if (!(foundDep->refclassid == TypeRelationId &&
    9313           0 :               foundDep->refobjid == attTup->atttypid) &&
    9314           0 :             !(foundDep->refclassid == CollationRelationId &&
    9315           0 :               foundDep->refobjid == attTup->attcollation))
    9316           0 :             elog(ERROR, "found unexpected dependency for column");
    9317             : 
    9318           0 :         CatalogTupleDelete(depRel, &depTup->t_self);
    9319             :     }
    9320             : 
    9321          68 :     systable_endscan(scan);
    9322             : 
    9323          68 :     heap_close(depRel, RowExclusiveLock);
    9324             : 
    9325             :     /*
    9326             :      * Here we go --- change the recorded column type and collation.  (Note
    9327             :      * heapTup is a copy of the syscache entry, so okay to scribble on.)
    9328             :      */
    9329          68 :     attTup->atttypid = targettype;
    9330          68 :     attTup->atttypmod = targettypmod;
    9331          68 :     attTup->attcollation = targetcollid;
    9332          68 :     attTup->attndims = list_length(typeName->arrayBounds);
    9333          68 :     attTup->attlen = tform->typlen;
    9334          68 :     attTup->attbyval = tform->typbyval;
    9335          68 :     attTup->attalign = tform->typalign;
    9336          68 :     attTup->attstorage = tform->typstorage;
    9337             : 
    9338          68 :     ReleaseSysCache(typeTuple);
    9339             : 
    9340          68 :     CatalogTupleUpdate(attrelation, &heapTup->t_self, heapTup);
    9341             : 
    9342          68 :     heap_close(attrelation, RowExclusiveLock);
    9343             : 
    9344             :     /* Install dependencies on new datatype and collation */
    9345          68 :     add_column_datatype_dependency(RelationGetRelid(rel), attnum, targettype);
    9346          68 :     add_column_collation_dependency(RelationGetRelid(rel), attnum, targetcollid);
    9347             : 
    9348             :     /*
    9349             :      * Drop any pg_statistic entry for the column, since it's now wrong type
    9350             :      */
    9351          68 :     RemoveStatistics(RelationGetRelid(rel), attnum);
    9352             : 
    9353          68 :     InvokeObjectPostAlterHook(RelationRelationId,
    9354             :                               RelationGetRelid(rel), attnum);
    9355             : 
    9356             :     /*
    9357             :      * Update the default, if present, by brute force --- remove and re-add
    9358             :      * the default.  Probably unsafe to take shortcuts, since the new version
    9359             :      * may well have additional dependencies.  (It's okay to do this now,
    9360             :      * rather than after other ALTER TYPE commands, since the default won't
    9361             :      * depend on other column types.)
    9362             :      */
    9363          68 :     if (defaultexpr)
    9364             :     {
    9365             :         /* Must make new row visible since it will be updated again */
    9366           1 :         CommandCounterIncrement();
    9367             : 
    9368             :         /*
    9369             :          * We use RESTRICT here for safety, but at present we do not expect
    9370             :          * anything to depend on the default.
    9371             :          */
    9372           1 :         RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT, true,
    9373             :                           true);
    9374             : 
    9375           1 :         StoreAttrDefault(rel, attnum, defaultexpr, true);
    9376             :     }
    9377             : 
    9378          68 :     ObjectAddressSubSet(address, RelationRelationId,
    9379             :                         RelationGetRelid(rel), attnum);
    9380             : 
    9381             :     /* Cleanup */
    9382          68 :     heap_freetuple(heapTup);
    9383             : 
    9384          68 :     return address;
    9385             : }
    9386             : 
    9387             : /*
    9388             :  * Returns the address of the modified column
    9389             :  */
    9390             : static ObjectAddress
    9391           7 : ATExecAlterColumnGenericOptions(Relation rel,
    9392             :                                 const char *colName,
    9393             :                                 List *options,
    9394             :                                 LOCKMODE lockmode)
    9395             : {
    9396             :     Relation    ftrel;
    9397             :     Relation    attrel;
    9398             :     ForeignServer *server;
    9399             :     ForeignDataWrapper *fdw;
    9400             :     HeapTuple   tuple;
    9401             :     HeapTuple   newtuple;
    9402             :     bool        isnull;
    9403             :     Datum       repl_val[Natts_pg_attribute];
    9404             :     bool        repl_null[Natts_pg_attribute];
    9405             :     bool        repl_repl[Natts_pg_attribute];
    9406             :     Datum       datum;
    9407             :     Form_pg_foreign_table fttableform;
    9408             :     Form_pg_attribute atttableform;
    9409             :     AttrNumber  attnum;
    9410             :     ObjectAddress address;
    9411             : 
    9412           7 :     if (options == NIL)
    9413           0 :         return InvalidObjectAddress;
    9414             : 
    9415             :     /* First, determine FDW validator associated to the foreign table. */
    9416           7 :     ftrel = heap_open(ForeignTableRelationId, AccessShareLock);
    9417           7 :     tuple = SearchSysCache1(FOREIGNTABLEREL, rel->rd_id);
    9418           7 :     if (!HeapTupleIsValid(tuple))
    9419           0 :         ereport(ERROR,
    9420             :                 (errcode(ERRCODE_UNDEFINED_OBJECT),
    9421             :                  errmsg("foreign table \"%s\" does not exist",
    9422             :                         RelationGetRelationName(rel))));
    9423           7 :     fttableform = (Form_pg_foreign_table) GETSTRUCT(tuple);
    9424           7 :     server = GetForeignServer(fttableform->ftserver);
    9425           7 :     fdw = GetForeignDataWrapper(server->fdwid);
    9426             : 
    9427           7 :     heap_close(ftrel, AccessShareLock);
    9428           7 :     ReleaseSysCache(tuple);
    9429             : 
    9430           7 :     attrel = heap_open(AttributeRelationId, RowExclusiveLock);
    9431           7 :     tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
    9432           7 :     if (!HeapTupleIsValid(tuple))
    9433           0 :         ereport(ERROR,
    9434             :                 (errcode(ERRCODE_UNDEFINED_COLUMN),
    9435             :                  errmsg("column \"%s\" of relation \"%s\" does not exist",
    9436             :                         colName, RelationGetRelationName(rel))));
    9437             : 
    9438             :     /* Prevent them from altering a system attribute */
    9439           7 :     atttableform = (Form_pg_attribute) GETSTRUCT(tuple);
    9440           7 :     attnum = atttableform->attnum;
    9441           7 :     if (attnum <= 0)
    9442           1 :         ereport(ERROR,
    9443             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    9444             :                  errmsg("cannot alter system column \"%s\"", colName)));
    9445             : 
    9446             : 
    9447             :     /* Initialize buffers for new tuple values */
    9448           6 :     memset(repl_val, 0, sizeof(repl_val));
    9449           6 :     memset(repl_null, false, sizeof(repl_null));
    9450           6 :     memset(repl_repl, false, sizeof(repl_repl));
    9451             : 
    9452             :     /* Extract the current options */
    9453           6 :     datum = SysCacheGetAttr(ATTNAME,
    9454             :                             tuple,
    9455             :                             Anum_pg_attribute_attfdwoptions,
    9456             :                             &isnull);
    9457           6 :     if (isnull)
    9458           5 :         datum = PointerGetDatum(NULL);
    9459             : 
    9460             :     /* Transform the options */
    9461           6 :     datum = transformGenericOptions(AttributeRelationId,
    9462             :                                     datum,
    9463             :                                     options,
    9464             :                                     fdw->fdwvalidator);
    9465             : 
    9466           6 :     if (PointerIsValid(DatumGetPointer(datum)))
    9467           6 :         repl_val[Anum_pg_attribute_attfdwoptions - 1] = datum;
    9468             :     else
    9469           0 :         repl_null[Anum_pg_attribute_attfdwoptions - 1] = true;
    9470             : 
    9471           6 :     repl_repl[Anum_pg_attribute_attfdwoptions - 1] = true;
    9472             : 
    9473             :     /* Everything looks good - update the tuple */
    9474             : 
    9475           6 :     newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrel),
    9476             :                                  repl_val, repl_null, repl_repl);
    9477             : 
    9478           6 :     CatalogTupleUpdate(attrel, &newtuple->t_self, newtuple);
    9479             : 
    9480           6 :     InvokeObjectPostAlterHook(RelationRelationId,
    9481             :                               RelationGetRelid(rel),
    9482             :                               atttableform->attnum);
    9483           6 :     ObjectAddressSubSet(address, RelationRelationId,
    9484             :                         RelationGetRelid(rel), attnum);
    9485             : 
    9486           6 :     ReleaseSysCache(tuple);
    9487             : 
    9488           6 :     heap_close(attrel, RowExclusiveLock);
    9489             : 
    9490           6 :     heap_freetuple(newtuple);
    9491             : 
    9492           6 :     return address;
    9493             : }
    9494             : 
    9495             : /*
    9496             :  * Cleanup after we've finished all the ALTER TYPE operations for a
    9497             :  * particular relation.  We have to drop and recreate all the indexes
    9498             :  * and constraints that depend on the altered columns.
    9499             :  */
    9500             : static void
    9501          67 : ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab, LOCKMODE lockmode)
    9502             : {
    9503             :     ObjectAddress obj;
    9504             :     ListCell   *def_item;
    9505             :     ListCell   *oid_item;
    9506             : 
    9507             :     /*
    9508             :      * Re-parse the index and constraint definitions, and attach them to the
    9509             :      * appropriate work queue entries.  We do this before dropping because in
    9510             :      * the case of a FOREIGN KEY constraint, we might not yet have exclusive
    9511             :      * lock on the table the constraint is attached to, and we need to get
    9512             :      * that before dropping.  It's safe because the parser won't actually look
    9513             :      * at the catalogs to detect the existing entry.
    9514             :      *
    9515             :      * We can't rely on the output of deparsing to tell us which relation to
    9516             :      * operate on, because concurrent activity might have made the name
    9517             :      * resolve differently.  Instead, we've got to use the OID of the
    9518             :      * constraint or index we're processing to figure out which relation to
    9519             :      * operate on.
    9520             :      */
    9521          88 :     forboth(oid_item, tab->changedConstraintOids,
    9522             :             def_item, tab->changedConstraintDefs)
    9523             :     {
    9524          21 :         Oid         oldId = lfirst_oid(oid_item);
    9525             :         HeapTuple   tup;
    9526             :         Form_pg_constraint con;
    9527             :         Oid         relid;
    9528             :         Oid         confrelid;
    9529             :         bool        conislocal;
    9530             : 
    9531          21 :         tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(oldId));
    9532          21 :         if (!HeapTupleIsValid(tup)) /* should not happen */
    9533           0 :             elog(ERROR, "cache lookup failed for constraint %u", oldId);
    9534          21 :         con = (Form_pg_constraint) GETSTRUCT(tup);
    9535          21 :         relid = con->conrelid;
    9536          21 :         confrelid = con->confrelid;
    9537          21 :         conislocal = con->conislocal;
    9538          21 :         ReleaseSysCache(tup);
    9539             : 
    9540             :         /*
    9541             :          * If the constraint is inherited (only), we don't want to inject a
    9542             :          * new definition here; it'll get recreated when ATAddCheckConstraint
    9543             :          * recurses from adding the parent table's constraint.  But we had to
    9544             :          * carry the info this far so that we can drop the constraint below.
    9545             :          */
    9546          21 :         if (!conislocal)
    9547           1 :             continue;
    9548             : 
    9549          40 :         ATPostAlterTypeParse(oldId, relid, confrelid,
    9550          20 :                              (char *) lfirst(def_item),
    9551          20 :                              wqueue, lockmode, tab->rewrite);
    9552             :     }
    9553          79 :     forboth(oid_item, tab->changedIndexOids,
    9554             :             def_item, tab->changedIndexDefs)
    9555             :     {
    9556          12 :         Oid         oldId = lfirst_oid(oid_item);
    9557             :         Oid         relid;
    9558             : 
    9559          12 :         relid = IndexGetRelation(oldId, false);
    9560          24 :         ATPostAlterTypeParse(oldId, relid, InvalidOid,
    9561          12 :                              (char *) lfirst(def_item),
    9562          12 :                              wqueue, lockmode, tab->rewrite);
    9563             :     }
    9564             : 
    9565             :     /*
    9566             :      * Now we can drop the existing constraints and indexes --- constraints
    9567             :      * first, since some of them might depend on the indexes.  In fact, we
    9568             :      * have to delete FOREIGN KEY constraints before UNIQUE constraints, but
    9569             :      * we already ordered the constraint list to ensure that would happen. It
    9570             :      * should be okay to use DROP_RESTRICT here, since nothing else should be
    9571             :      * depending on these objects.
    9572             :      */
    9573          88 :     foreach(oid_item, tab->changedConstraintOids)
    9574             :     {
    9575          21 :         obj.classId = ConstraintRelationId;
    9576          21 :         obj.objectId = lfirst_oid(oid_item);
    9577          21 :         obj.objectSubId = 0;
    9578          21 :         performDeletion(&obj, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
    9579             :     }
    9580             : 
    9581          79 :     foreach(oid_item, tab->changedIndexOids)
    9582             :     {
    9583          12 :         obj.classId = RelationRelationId;
    9584          12 :         obj.objectId = lfirst_oid(oid_item);
    9585          12 :         obj.objectSubId = 0;
    9586          12 :         performDeletion(&obj, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
    9587             :     }
    9588             : 
    9589             :     /*
    9590             :      * The objects will get recreated during subsequent passes over the work
    9591             :      * queue.
    9592             :      */
    9593          67 : }
    9594             : 
    9595             : static void
    9596          32 : ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
    9597             :                      List **wqueue, LOCKMODE lockmode, bool rewrite)
    9598             : {
    9599             :     List       *raw_parsetree_list;
    9600             :     List       *querytree_list;
    9601             :     ListCell   *list_item;
    9602             :     Relation    rel;
    9603             : 
    9604             :     /*
    9605             :      * We expect that we will get only ALTER TABLE and CREATE INDEX
    9606             :      * statements. Hence, there is no need to pass them through
    9607             :      * parse_analyze() or the rewriter, but instead we need to pass them
    9608             :      * through parse_utilcmd.c to make them ready for execution.
    9609             :      */
    9610          32 :     raw_parsetree_list = raw_parser(cmd);
    9611          32 :     querytree_list = NIL;
    9612          64 :     foreach(list_item, raw_parsetree_list)
    9613             :     {
    9614          32 :         RawStmt    *rs = lfirst_node(RawStmt, list_item);
    9615          32 :         Node       *stmt = rs->stmt;
    9616             : 
    9617          32 :         if (IsA(stmt, IndexStmt))
    9618          12 :             querytree_list = lappend(querytree_list,
    9619          12 :                                      transformIndexStmt(oldRelId,
    9620             :                                                         (IndexStmt *) stmt,
    9621             :                                                         cmd));
    9622          20 :         else if (IsA(stmt, AlterTableStmt))
    9623          20 :             querytree_list = list_concat(querytree_list,
    9624             :                                          transformAlterTableStmt(oldRelId,
    9625             :                                                                  (AlterTableStmt *) stmt,
    9626             :                                                                  cmd));
    9627             :         else
    9628           0 :             querytree_list = lappend(querytree_list, stmt);
    9629             :     }
    9630             : 
    9631             :     /* Caller should already have acquired whatever lock we need. */
    9632          32 :     rel = relation_open(oldRelId, NoLock);
    9633             : 
    9634             :     /*
    9635             :      * Attach each generated command to the proper place in the work queue.
    9636             :      * Note this could result in creation of entirely new work-queue entries.
    9637             :      *
    9638             :      * Also note that we have to tweak the command subtypes, because it turns
    9639             :      * out that re-creation of indexes and constraints has to act a bit
    9640             :      * differently from initial creation.
    9641             :      */
    9642          64 :     foreach(list_item, querytree_list)
    9643             :     {
    9644          32 :         Node       *stm = (Node *) lfirst(list_item);
    9645             :         AlteredTableInfo *tab;
    9646             : 
    9647          32 :         tab = ATGetQueueEntry(wqueue, rel);
    9648             : 
    9649          32 :         if (IsA(stm, IndexStmt))
    9650             :         {
    9651          12 :             IndexStmt  *stmt = (IndexStmt *) stm;
    9652             :             AlterTableCmd *newcmd;
    9653             : 
    9654          12 :             if (!rewrite)
    9655           5 :                 TryReuseIndex(oldId, stmt);
    9656             :             /* keep the index's comment */
    9657          12 :             stmt->idxcomment = GetComment(oldId, RelationRelationId, 0);
    9658             : 
    9659          12 :             newcmd = makeNode(AlterTableCmd);
    9660          12 :             newcmd->subtype = AT_ReAddIndex;
    9661          12 :             newcmd->def = (Node *) stmt;
    9662          12 :             tab->subcmds[AT_PASS_OLD_INDEX] =
    9663          12 :                 lappend(tab->subcmds[AT_PASS_OLD_INDEX], newcmd);
    9664             :         }
    9665          20 :         else if (IsA(stm, AlterTableStmt))
    9666             :         {
    9667          20 :             AlterTableStmt *stmt = (AlterTableStmt *) stm;
    9668             :             ListCell   *lcmd;
    9669             : 
    9670          40 :             foreach(lcmd, stmt->cmds)
    9671             :             {
    9672          20 :                 AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);
    9673             : 
    9674          20 :                 if (cmd->subtype == AT_AddIndex)
    9675             :                 {
    9676             :                     IndexStmt  *indstmt;
    9677             :                     Oid         indoid;
    9678             : 
    9679           7 :                     indstmt = castNode(IndexStmt, cmd->def);
    9680           7 :                     indoid = get_constraint_index(oldId);
    9681             : 
    9682           7 :                     if (!rewrite)
    9683           2 :                         TryReuseIndex(indoid, indstmt);
    9684             :                     /* keep any comment on the index */
    9685           7 :                     indstmt->idxcomment = GetComment(indoid,
    9686             :                                                      RelationRelationId, 0);
    9687             : 
    9688           7 :                     cmd->subtype = AT_ReAddIndex;
    9689           7 :                     tab->subcmds[AT_PASS_OLD_INDEX] =
    9690           7 :                         lappend(tab->subcmds[AT_PASS_OLD_INDEX], cmd);
    9691             : 
    9692             :                     /* recreate any comment on the constraint */
    9693           7 :                     RebuildConstraintComment(tab,
    9694             :                                              AT_PASS_OLD_INDEX,
    9695             :                                              oldId,
    9696             :                                              rel, indstmt->idxname);
    9697             :                 }
    9698          13 :                 else if (cmd->subtype == AT_AddConstraint)
    9699             :                 {
    9700             :                     Constraint *con;
    9701             : 
    9702          13 :                     con = castNode(Constraint, cmd->def);
    9703          13 :                     con->old_pktable_oid = refRelId;
    9704             :                     /* rewriting neither side of a FK */
    9705          13 :                     if (con->contype == CONSTR_FOREIGN &&
    9706           1 :                         !rewrite && tab->rewrite == 0)
    9707           1 :                         TryReuseForeignKey(oldId, con);
    9708          13 :                     cmd->subtype = AT_ReAddConstraint;
    9709          13 :                     tab->subcmds[AT_PASS_OLD_CONSTR] =
    9710          13 :                         lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
    9711             : 
    9712             :                     /* recreate any comment on the constraint */
    9713          13 :                     RebuildConstraintComment(tab,
    9714             :                                              AT_PASS_OLD_CONSTR,
    9715             :                                              oldId,
    9716             :                                              rel, con->conname);
    9717             :                 }
    9718             :                 else
    9719           0 :                     elog(ERROR, "unexpected statement subtype: %d",
    9720             :                          (int) cmd->subtype);
    9721             :             }
    9722             :         }
    9723             :         else
    9724           0 :             elog(ERROR, "unexpected statement type: %d",
    9725             :                  (int) nodeTag(stm));
    9726             :     }
    9727             : 
    9728          32 :     relation_close(rel, NoLock);
    9729          32 : }
    9730             : 
    9731             : /*
    9732             :  * Subroutine for ATPostAlterTypeParse() to recreate a comment entry for
    9733             :  * a constraint that is being re-added.
    9734             :  */
    9735             : static void
    9736          20 : RebuildConstraintComment(AlteredTableInfo *tab, int pass, Oid objid,
    9737             :                          Relation rel, char *conname)
    9738             : {
    9739             :     CommentStmt *cmd;
    9740             :     char       *comment_str;
    9741             :     AlterTableCmd *newcmd;
    9742             : 
    9743             :     /* Look for comment for object wanted, and leave if none */
    9744          20 :     comment_str = GetComment(objid, ConstraintRelationId, 0);
    9745          20 :     if (comment_str == NULL)
    9746          32 :         return;
    9747             : 
    9748             :     /* Build node CommentStmt */
    9749           8 :     cmd = makeNode(CommentStmt);
    9750           8 :     cmd->objtype = OBJECT_TABCONSTRAINT;
    9751           8 :     cmd->object = (Node *) list_make3(makeString(get_namespace_name(RelationGetNamespace(rel))),
    9752             :                                       makeString(pstrdup(RelationGetRelationName(rel))),
    9753             :                                       makeString(pstrdup(conname)));
    9754           8 :     cmd->comment = comment_str;
    9755             : 
    9756             :     /* Append it to list of commands */
    9757           8 :     newcmd = makeNode(AlterTableCmd);
    9758           8 :     newcmd->subtype = AT_ReAddComment;
    9759           8 :     newcmd->def = (Node *) cmd;
    9760           8 :     tab->subcmds[pass] = lappend(tab->subcmds[pass], newcmd);
    9761             : }
    9762             : 
    9763             : /*
    9764             :  * Subroutine for ATPostAlterTypeParse().  Calls out to CheckIndexCompatible()
    9765             :  * for the real analysis, then mutates the IndexStmt based on that verdict.
    9766             :  */
    9767             : static void
    9768           7 : TryReuseIndex(Oid oldId, IndexStmt *stmt)
    9769             : {
    9770           7 :     if (CheckIndexCompatible(oldId,
    9771             :                              stmt->accessMethod,
    9772             :                              stmt->indexParams,
    9773             :                              stmt->excludeOpNames))
    9774             :     {
    9775           7 :         Relation    irel = index_open(oldId, NoLock);
    9776             : 
    9777           7 :         stmt->oldNode = irel->rd_node.relNode;
    9778           7 :         index_close(irel, NoLock);
    9779             :     }
    9780           7 : }
    9781             : 
    9782             : /*
    9783             :  * Subroutine for ATPostAlterTypeParse().
    9784             :  *
    9785             :  * Stash the old P-F equality operator into the Constraint node, for possible
    9786             :  * use by ATAddForeignKeyConstraint() in determining whether revalidation of
    9787             :  * this constraint can be skipped.
    9788             :  */
    9789             : static void
    9790           1 : TryReuseForeignKey(Oid oldId, Constraint *con)
    9791             : {
    9792             :     HeapTuple   tup;
    9793             :     Datum       adatum;
    9794             :     bool        isNull;
    9795             :     ArrayType  *arr;
    9796             :     Oid        *rawarr;
    9797             :     int         numkeys;
    9798             :     int         i;
    9799             : 
    9800           1 :     Assert(con->contype == CONSTR_FOREIGN);
    9801           1 :     Assert(con->old_conpfeqop == NIL);   /* already prepared this node */
    9802             : 
    9803           1 :     tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(oldId));
    9804           1 :     if (!HeapTupleIsValid(tup)) /* should not happen */
    9805           0 :         elog(ERROR, "cache lookup failed for constraint %u", oldId);
    9806             : 
    9807           1 :     adatum = SysCacheGetAttr(CONSTROID, tup,
    9808             :                              Anum_pg_constraint_conpfeqop, &isNull);
    9809           1 :     if (isNull)
    9810           0 :         elog(ERROR, "null conpfeqop for constraint %u", oldId);
    9811           1 :     arr = DatumGetArrayTypeP(adatum);   /* ensure not toasted */
    9812           1 :     numkeys = ARR_DIMS(arr)[0];
    9813             :     /* test follows the one in ri_FetchConstraintInfo() */
    9814           2 :     if (ARR_NDIM(arr) != 1 ||
    9815           2 :         ARR_HASNULL(arr) ||
    9816           1 :         ARR_ELEMTYPE(arr) != OIDOID)
    9817           0 :         elog(ERROR, "conpfeqop is not a 1-D Oid array");
    9818           1 :     rawarr = (Oid *) ARR_DATA_PTR(arr);
    9819             : 
    9820             :     /* stash a List of the operator Oids in our Constraint node */
    9821           2 :     for (i = 0; i < numkeys; i++)
    9822           1 :         con->old_conpfeqop = lcons_oid(rawarr[i], con->old_conpfeqop);
    9823             : 
    9824           1 :     ReleaseSysCache(tup);
    9825           1 : }
    9826             : 
    9827             : /*
    9828             :  * ALTER TABLE OWNER
    9829             :  *
    9830             :  * recursing is true if we are recursing from a table to its indexes,
    9831             :  * sequences, or toast table.  We don't allow the ownership of those things to
    9832             :  * be changed separately from the parent table.  Also, we can skip permission
    9833             :  * checks (this is necessary not just an optimization, else we'd fail to
    9834             :  * handle toast tables properly).
    9835             :  *
    9836             :  * recursing is also true if ALTER TYPE OWNER is calling us to fix up a
    9837             :  * free-standing composite type.
    9838             :  */
    9839             : void
    9840          37 : ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing, LOCKMODE lockmode)
    9841             : {
    9842             :     Relation    target_rel;
    9843             :     Relation    class_rel;
    9844             :     HeapTuple   tuple;
    9845             :     Form_pg_class tuple_class;
    9846             : 
    9847             :     /*
    9848             :      * Get exclusive lock till end of transaction on the target table. Use
    9849             :      * relation_open so that we can work on indexes and sequences.
    9850             :      */
    9851          37 :     target_rel = relation_open(relationOid, lockmode);
    9852             : 
    9853             :     /* Get its pg_class tuple, too */
    9854          37 :     class_rel = heap_open(RelationRelationId, RowExclusiveLock);
    9855             : 
    9856          37 :     tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relationOid));
    9857          37 :     if (!HeapTupleIsValid(tuple))
    9858           0 :         elog(ERROR, "cache lookup failed for relation %u", relationOid);
    9859          37 :     tuple_class = (Form_pg_class) GETSTRUCT(tuple);
    9860             : 
    9861             :     /* Can we change the ownership of this tuple? */
    9862          37 :     switch (tuple_class->relkind)
    9863             :     {
    9864             :         case RELKIND_RELATION:
    9865             :         case RELKIND_VIEW:
    9866             :         case RELKIND_MATVIEW:
    9867             :         case RELKIND_FOREIGN_TABLE:
    9868             :         case RELKIND_PARTITIONED_TABLE:
    9869             :             /* ok to change owner */
    9870          16 :             break;
    9871             :         case RELKIND_INDEX:
    9872          10 :             if (!recursing)
    9873             :             {
    9874             :                 /*
    9875             :                  * Because ALTER INDEX OWNER used to be allowed, and in fact
    9876             :                  * is generated by old versions of pg_dump, we give a warning
    9877             :                  * and do nothing rather than erroring out.  Also, to avoid
    9878             :                  * unnecessary chatter while restoring those old dumps, say
    9879             :                  * nothing at all if the command would be a no-op anyway.
    9880             :                  */
    9881           0 :                 if (tuple_class->relowner != newOwnerId)
    9882           0 :                     ereport(WARNING,
    9883             :                             (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    9884             :                              errmsg("cannot change owner of index \"%s\"",
    9885             :                                     NameStr(tuple_class->relname)),
    9886             :                              errhint("Change the ownership of the index's table, instead.")));
    9887             :                 /* quick hack to exit via the no-op path */
    9888           0 :                 newOwnerId = tuple_class->relowner;
    9889             :             }
    9890          10 :             break;
    9891             :         case RELKIND_SEQUENCE:
    9892           5 :             if (!recursing &&
    9893           0 :                 tuple_class->relowner != newOwnerId)
    9894             :             {
    9895             :                 /* if it's an owned sequence, disallow changing it by itself */
    9896             :                 Oid         tableId;
    9897             :                 int32       colId;
    9898             : 
    9899           0 :                 if (sequenceIsOwned(relationOid, DEPENDENCY_AUTO, &tableId, &colId) ||
    9900           0 :                     sequenceIsOwned(relationOid, DEPENDENCY_INTERNAL, &tableId, &colId))
    9901           0 :                     ereport(ERROR,
    9902             :                             (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    9903             :                              errmsg("cannot change owner of sequence \"%s\"",
    9904             :                                     NameStr(tuple_class->relname)),
    9905             :                              errdetail("Sequence \"%s\" is linked to table \"%s\".",
    9906             :                                        NameStr(tuple_class->relname),
    9907             :                                        get_rel_name(tableId))));
    9908             :             }
    9909           5 :             break;
    9910             :         case RELKIND_COMPOSITE_TYPE:
    9911           1 :             if (recursing)
    9912           1 :                 break;
    9913           0 :             ereport(ERROR,
    9914             :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    9915             :                      errmsg("\"%s\" is a composite type",
    9916             :                             NameStr(tuple_class->relname)),
    9917             :                      errhint("Use ALTER TYPE instead.")));
    9918             :             break;
    9919             :         case RELKIND_TOASTVALUE:
    9920           5 :             if (recursing)
    9921           5 :                 break;
    9922             :             /* FALL THRU */
    9923             :         default:
    9924           0 :             ereport(ERROR,
    9925             :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    9926             :                      errmsg("\"%s\" is not a table, view, sequence, or foreign table",
    9927             :                             NameStr(tuple_class->relname))));
    9928             :     }
    9929             : 
    9930             :     /*
    9931             :      * If the new owner is the same as the existing owner, consider the
    9932             :      * command to have succeeded.  This is for dump restoration purposes.
    9933             :      */
    9934          37 :     if (tuple_class->relowner != newOwnerId)
    9935             :     {
    9936             :         Datum       repl_val[Natts_pg_class];
    9937             :         bool        repl_null[Natts_pg_class];
    9938             :         bool        repl_repl[Natts_pg_class];
    9939             :         Acl        *newAcl;
    9940             :         Datum       aclDatum;
    9941             :         bool        isNull;
    9942             :         HeapTuple   newtuple;
    9943             : 
    9944             :         /* skip permission checks when recursing to index or toast table */
    9945          35 :         if (!recursing)
    9946             :         {
    9947             :             /* Superusers can always do it */
    9948          14 :             if (!superuser())
    9949             :             {
    9950           0 :                 Oid         namespaceOid = tuple_class->relnamespace;
    9951             :                 AclResult   aclresult;
    9952             : 
    9953             :                 /* Otherwise, must be owner of the existing object */
    9954           0 :                 if (!pg_class_ownercheck(relationOid, GetUserId()))
    9955           0 :                     aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
    9956           0 :                                    RelationGetRelationName(target_rel));
    9957             : 
    9958             :                 /* Must be able to become new owner */
    9959           0 :                 check_is_member_of_role(GetUserId(), newOwnerId);
    9960             : 
    9961             :                 /* New owner must have CREATE privilege on namespace */
    9962           0 :                 aclresult = pg_namespace_aclcheck(namespaceOid, newOwnerId,
    9963             :                                                   ACL_CREATE);
    9964           0 :                 if (aclresult != ACLCHECK_OK)
    9965           0 :                     aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
    9966           0 :                                    get_namespace_name(namespaceOid));
    9967             :             }
    9968             :         }
    9969             : 
    9970          35 :         memset(repl_null, false, sizeof(repl_null));
    9971          35 :         memset(repl_repl, false, sizeof(repl_repl));
    9972             : 
    9973          35 :         repl_repl[Anum_pg_class_relowner - 1] = true;
    9974          35 :         repl_val[Anum_pg_class_relowner - 1] = ObjectIdGetDatum(newOwnerId);
    9975             : 
    9976             :         /*
    9977             :          * Determine the modified ACL for the new owner.  This is only
    9978             :          * necessary when the ACL is non-null.
    9979             :          */
    9980          35 :         aclDatum = SysCacheGetAttr(RELOID, tuple,
    9981             :                                    Anum_pg_class_relacl,
    9982             :                                    &isNull);
    9983          35 :         if (!isNull)
    9984             :         {
    9985           2 :             newAcl = aclnewowner(DatumGetAclP(aclDatum),
    9986             :                                  tuple_class->relowner, newOwnerId);
    9987           2 :             repl_repl[Anum_pg_class_relacl - 1] = true;
    9988           2 :             repl_val[Anum_pg_class_relacl - 1] = PointerGetDatum(newAcl);
    9989             :         }
    9990             : 
    9991          35 :         newtuple = heap_modify_tuple(tuple, RelationGetDescr(class_rel), repl_val, repl_null, repl_repl);
    9992             : 
    9993          35 :         CatalogTupleUpdate(class_rel, &newtuple->t_self, newtuple);
    9994             : 
    9995          35 :         heap_freetuple(newtuple);
    9996             : 
    9997             :         /*
    9998             :          * We must similarly update any per-column ACLs to reflect the new
    9999             :          * owner; for neatness reasons that's split out as a subroutine.
   10000             :          */
   10001          35 :         change_owner_fix_column_acls(relationOid,
   10002             :                                      tuple_class->relowner,
   10003             :                                      newOwnerId);
   10004             : 
   10005             :         /*
   10006             :          * Update owner dependency reference, if any.  A composite type has
   10007             :          * none, because it's tracked for the pg_type entry instead of here;
   10008             :          * indexes and TOAST tables don't have their own entries either.
   10009             :          */
   10010          69 :         if (tuple_class->relkind != RELKIND_COMPOSITE_TYPE &&
   10011          58 :             tuple_class->relkind != RELKIND_INDEX &&
   10012          24 :             tuple_class->relkind != RELKIND_TOASTVALUE)
   10013          19 :             changeDependencyOnOwner(RelationRelationId, relationOid,
   10014             :                                     newOwnerId);
   10015             : 
   10016             :         /*
   10017             :          * Also change the ownership of the table's row type, if it has one
   10018             :          */
   10019          35 :         if (tuple_class->relkind != RELKIND_INDEX)
   10020          25 :             AlterTypeOwnerInternal(tuple_class->reltype, newOwnerId);
   10021             : 
   10022             :         /*
   10023             :          * If we are operating on a table or materialized view, also change
   10024             :          * the ownership of any indexes and sequences that belong to the
   10025             :          * relation, as well as its toast table (if it has one).
   10026             :          */
   10027          55 :         if (tuple_class->relkind == RELKIND_RELATION ||
   10028          40 :             tuple_class->relkind == RELKIND_MATVIEW ||
   10029          20 :             tuple_class->relkind == RELKIND_TOASTVALUE)
   10030             :         {
   10031             :             List       *index_oid_list;
   10032             :             ListCell   *i;
   10033             : 
   10034             :             /* Find all the indexes belonging to this relation */
   10035          20 :             index_oid_list = RelationGetIndexList(target_rel);
   10036             : 
   10037             :             /* For each index, recursively change its ownership */
   10038          30 :             foreach(i, index_oid_list)
   10039          10 :                 ATExecChangeOwner(lfirst_oid(i), newOwnerId, true, lockmode);
   10040             : 
   10041          20 :             list_free(index_oid_list);
   10042             :         }
   10043             : 
   10044          55 :         if (tuple_class->relkind == RELKIND_RELATION ||
   10045          20 :             tuple_class->relkind == RELKIND_MATVIEW)
   10046             :         {
   10047             :             /* If it has a toast table, recurse to change its ownership */
   10048          15 :             if (tuple_class->reltoastrelid != InvalidOid)
   10049           5 :                 ATExecChangeOwner(tuple_class->reltoastrelid, newOwnerId,
   10050             :                                   true, lockmode);
   10051             : 
   10052             :             /* If it has dependent sequences, recurse to change them too */
   10053          15 :             change_owner_recurse_to_sequences(relationOid, newOwnerId, lockmode);
   10054             :         }
   10055             :     }
   10056             : 
   10057          37 :     InvokeObjectPostAlterHook(RelationRelationId, relationOid, 0);
   10058             : 
   10059          37 :     ReleaseSysCache(tuple);
   10060          37 :     heap_close(class_rel, RowExclusiveLock);
   10061          37 :     relation_close(target_rel, NoLock);
   10062          37 : }
   10063             : 
   10064             : /*
   10065             :  * change_owner_fix_column_acls
   10066             :  *
   10067             :  * Helper function for ATExecChangeOwner.  Scan the columns of the table
   10068             :  * and fix any non-null column ACLs to reflect the new owner.
   10069             :  */
   10070             : static void
   10071          35 : change_owner_fix_column_acls(Oid relationOid, Oid oldOwnerId, Oid newOwnerId)
   10072             : {
   10073             :     Relation    attRelation;
   10074             :     SysScanDesc scan;
   10075             :     ScanKeyData key[1];
   10076             :     HeapTuple   attributeTuple;
   10077             : 
   10078          35 :     attRelation = heap_open(AttributeRelationId, RowExclusiveLock);
   10079          35 :     ScanKeyInit(&key[0],
   10080             :                 Anum_pg_attribute_attrelid,
   10081             :                 BTEqualStrategyNumber, F_OIDEQ,
   10082             :                 ObjectIdGetDatum(relationOid));
   10083          35 :     scan = systable_beginscan(attRelation, AttributeRelidNumIndexId,
   10084             :                               true, NULL, 1, key);
   10085         286 :     while (HeapTupleIsValid(attributeTuple = systable_getnext(scan)))
   10086             :     {
   10087         216 :         Form_pg_attribute att = (Form_pg_attribute) GETSTRUCT(attributeTuple);
   10088             :         Datum       repl_val[Natts_pg_attribute];
   10089             :         bool        repl_null[Natts_pg_attribute];
   10090             :         bool        repl_repl[Natts_pg_attribute];
   10091             :         Acl        *newAcl;
   10092             :         Datum       aclDatum;
   10093             :         bool        isNull;
   10094             :         HeapTuple   newtuple;
   10095             : 
   10096             :         /* Ignore dropped columns */
   10097         216 :         if (att->attisdropped)
   10098         216 :             continue;
   10099             : 
   10100         216 :         aclDatum = heap_getattr(attributeTuple,
   10101             :                                 Anum_pg_attribute_attacl,
   10102             :                                 RelationGetDescr(attRelation),
   10103             :                                 &isNull);
   10104             :         /* Null ACLs do not require changes */
   10105         216 :         if (isNull)
   10106         216 :             continue;
   10107             : 
   10108           0 :         memset(repl_null, false, sizeof(repl_null));
   10109           0 :         memset(repl_repl, false, sizeof(repl_repl));
   10110             : 
   10111           0 :         newAcl = aclnewowner(DatumGetAclP(aclDatum),
   10112             :                              oldOwnerId, newOwnerId);
   10113           0 :         repl_repl[Anum_pg_attribute_attacl - 1] = true;
   10114           0 :         repl_val[Anum_pg_attribute_attacl - 1] = PointerGetDatum(newAcl);
   10115             : 
   10116           0 :         newtuple = heap_modify_tuple(attributeTuple,
   10117             :                                      RelationGetDescr(attRelation),
   10118             :                                      repl_val, repl_null, repl_repl);
   10119             : 
   10120           0 :         CatalogTupleUpdate(attRelation, &newtuple->t_self, newtuple);
   10121             : 
   10122           0 :         heap_freetuple(newtuple);
   10123             :     }
   10124          35 :     systable_endscan(scan);
   10125          35 :     heap_close(attRelation, RowExclusiveLock);
   10126          35 : }
   10127             : 
   10128             : /*
   10129             :  * change_owner_recurse_to_sequences
   10130             :  *
   10131             :  * Helper function for ATExecChangeOwner.  Examines pg_depend searching
   10132             :  * for sequences that are dependent on serial columns, and changes their
   10133             :  * ownership.
   10134             :  */
   10135             : static void
   10136          15 : change_owner_recurse_to_sequences(Oid relationOid, Oid newOwnerId, LOCKMODE lockmode)
   10137             : {
   10138             :     Relation    depRel;
   10139             :     SysScanDesc scan;
   10140             :     ScanKeyData key[2];
   10141             :     HeapTuple   tup;
   10142             : 
   10143             :     /*
   10144             :      * SERIAL sequences are those having an auto dependency on one of the
   10145             :      * table's columns (we don't care *which* column, exactly).
   10146             :      */
   10147          15 :     depRel = heap_open(DependRelationId, AccessShareLock);
   10148             : 
   10149          15 :     ScanKeyInit(&key[0],
   10150             :                 Anum_pg_depend_refclassid,
   10151             :                 BTEqualStrategyNumber, F_OIDEQ,
   10152             :                 ObjectIdGetDatum(RelationRelationId));
   10153          15 :     ScanKeyInit(&key[1],
   10154             :                 Anum_pg_depend_refobjid,
   10155             :                 BTEqualStrategyNumber, F_OIDEQ,
   10156             :                 ObjectIdGetDatum(relationOid));
   10157             :     /* we leave refobjsubid unspecified */
   10158             : 
   10159          15 :     scan = systable_beginscan(depRel, DependReferenceIndexId, true,
   10160             :                               NULL, 2, key);
   10161             : 
   10162          63 :     while (HeapTupleIsValid(tup = systable_getnext(scan)))
   10163             :     {
   10164          33 :         Form_pg_depend depForm = (Form_pg_depend) GETSTRUCT(tup);
   10165             :         Relation    seqRel;
   10166             : 
   10167             :         /* skip dependencies other than auto dependencies on columns */
   10168          46 :         if (depForm->refobjsubid == 0 ||
   10169          17 :             depForm->classid != RelationRelationId ||
   10170           8 :             depForm->objsubid != 0 ||
   10171           4 :             !(depForm->deptype == DEPENDENCY_AUTO || depForm->deptype == DEPENDENCY_INTERNAL))
   10172          29 :             continue;
   10173             : 
   10174             :         /* Use relation_open just in case it's an index */
   10175           4 :         seqRel = relation_open(depForm->objid, lockmode);
   10176             : 
   10177             :         /* skip non-sequence relations */
   10178           4 :         if (RelationGetForm(seqRel)->relkind != RELKIND_SEQUENCE)
   10179             :         {
   10180             :             /* No need to keep the lock */
   10181           1 :             relation_close(seqRel, lockmode);
   10182           1 :             continue;
   10183             :         }
   10184             : 
   10185             :         /* We don't need to close the sequence while we alter it. */
   10186           3 :         ATExecChangeOwner(depForm->objid, newOwnerId, true, lockmode);
   10187             : 
   10188             :         /* Now we can close it.  Keep the lock till end of transaction. */
   10189           3 :         relation_close(seqRel, NoLock);
   10190             :     }
   10191             : 
   10192          15 :     systable_endscan(scan);
   10193             : 
   10194          15 :     relation_close(depRel, AccessShareLock);
   10195          15 : }
   10196             : 
   10197             : /*
   10198             :  * ALTER TABLE CLUSTER ON
   10199             :  *
   10200             :  * The only thing we have to do is to change the indisclustered bits.
   10201             :  *
   10202             :  * Return the address of the new clustering index.
   10203             :  */
   10204             : static ObjectAddress
   10205           3 : ATExecClusterOn(Relation rel, const char *indexName, LOCKMODE lockmode)
   10206             : {
   10207             :     Oid         indexOid;
   10208             :     ObjectAddress address;
   10209             : 
   10210           3 :     indexOid = get_relname_relid(indexName, rel->rd_rel->relnamespace);
   10211             : 
   10212           3 :     if (!OidIsValid(indexOid))
   10213           0 :         ereport(ERROR,
   10214             :                 (errcode(ERRCODE_UNDEFINED_OBJECT),
   10215             :                  errmsg("index \"%s\" for table \"%s\" does not exist",
   10216             :                         indexName, RelationGetRelationName(rel))));
   10217             : 
   10218             :     /* Check index is valid to cluster on */
   10219           3 :     check_index_is_clusterable(rel, indexOid, false, lockmode);
   10220             : 
   10221             :     /* And do the work */
   10222           3 :     mark_index_clustered(rel, indexOid, false);
   10223             : 
   10224           3 :     ObjectAddressSet(address,
   10225             :                      RelationRelationId, indexOid);
   10226             : 
   10227           3 :     return address;
   10228             : }
   10229             : 
   10230             : /*
   10231             :  * ALTER TABLE SET WITHOUT CLUSTER
   10232             :  *
   10233             :  * We have to find any indexes on the table that have indisclustered bit
   10234             :  * set and turn it off.
   10235             :  */
   10236             : static void
   10237           2 : ATExecDropCluster(Relation rel, LOCKMODE lockmode)
   10238             : {
   10239           2 :     mark_index_clustered(rel, InvalidOid, false);
   10240           2 : }
   10241             : 
   10242             : /*
   10243             :  * ALTER TABLE SET TABLESPACE
   10244             :  */
   10245             : static void
   10246           9 : ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel, char *tablespacename, LOCKMODE lockmode)
   10247             : {
   10248             :     Oid         tablespaceId;
   10249             : 
   10250             :     /* Check that the tablespace exists */
   10251           9 :     tablespaceId = get_tablespace_oid(tablespacename, false);
   10252             : 
   10253             :     /* Check permissions except when moving to database's default */
   10254           9 :     if (OidIsValid(tablespaceId) && tablespaceId != MyDatabaseTableSpace)
   10255             :     {
   10256             :         AclResult   aclresult;
   10257             : 
   10258           2 :         aclresult = pg_tablespace_aclcheck(tablespaceId, GetUserId(), ACL_CREATE);
   10259           2 :         if (aclresult != ACLCHECK_OK)
   10260           0 :             aclcheck_error(aclresult, ACL_KIND_TABLESPACE, tablespacename);
   10261             :     }
   10262             : 
   10263             :     /* Save info for Phase 3 to do the real work */
   10264           9 :     if (OidIsValid(tab->newTableSpace))
   10265           0 :         ereport(ERROR,
   10266             :                 (errcode(ERRCODE_SYNTAX_ERROR),
   10267             :                  errmsg("cannot have multiple SET TABLESPACE subcommands")));
   10268             : 
   10269           9 :     tab->newTableSpace = tablespaceId;
   10270           9 : }
   10271             : 
   10272             : /*
   10273             :  * Set, reset, or replace reloptions.
   10274             :  */
   10275             : static void
   10276          54 : ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation,
   10277             :                     LOCKMODE lockmode)
   10278             : {
   10279             :     Oid         relid;
   10280             :     Relation    pgclass;
   10281             :     HeapTuple   tuple;
   10282             :     HeapTuple   newtuple;
   10283             :     Datum       datum;
   10284             :     bool        isnull;
   10285             :     Datum       newOptions;
   10286             :     Datum       repl_val[Natts_pg_class];
   10287             :     bool        repl_null[Natts_pg_class];
   10288             :     bool        repl_repl[Natts_pg_class];
   10289             :     static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
   10290             : 
   10291          54 :     if (defList == NIL && operation != AT_ReplaceRelOptions)
   10292          50 :         return;                 /* nothing to do */
   10293             : 
   10294          54 :     pgclass = heap_open(RelationRelationId, RowExclusiveLock);
   10295             : 
   10296             :     /* Fetch heap tuple */
   10297          54 :     relid = RelationGetRelid(rel);
   10298          54 :     tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
   10299          54 :     if (!HeapTupleIsValid(tuple))
   10300           0 :         elog(ERROR, "cache lookup failed for relation %u", relid);
   10301             : 
   10302          54 :     if (operation == AT_ReplaceRelOptions)
   10303             :     {
   10304             :         /*
   10305             :          * If we're supposed to replace the reloptions list, we just pretend
   10306             :          * there were none before.
   10307             :          */
   10308          20 :         datum = (Datum) 0;
   10309          20 :         isnull = true;
   10310             :     }
   10311             :     else
   10312             :     {
   10313             :         /* Get the old reloptions */
   10314          34 :         datum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions,
   10315             :                                 &isnull);
   10316             :     }
   10317             : 
   10318             :     /* Generate new proposed reloptions (text array) */
   10319          54 :     newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
   10320             :                                      defList, NULL, validnsps, false,
   10321             :                                      operation == AT_ResetRelOptions);
   10322             : 
   10323             :     /* Validate */
   10324          53 :     switch (rel->rd_rel->relkind)
   10325             :     {
   10326             :         case RELKIND_RELATION:
   10327             :         case RELKIND_TOASTVALUE:
   10328             :         case RELKIND_MATVIEW:
   10329             :         case RELKIND_PARTITIONED_TABLE:
   10330          14 :             (void) heap_reloptions(rel->rd_rel->relkind, newOptions, true);
   10331          14 :             break;
   10332             :         case RELKIND_VIEW:
   10333          35 :             (void) view_reloptions(newOptions, true);
   10334          32 :             break;
   10335             :         case RELKIND_INDEX:
   10336           4 :             (void) index_reloptions(rel->rd_amroutine->amoptions, newOptions, true);
   10337           4 :             break;
   10338             :         default:
   10339           0 :             ereport(ERROR,
   10340             :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   10341             :                      errmsg("\"%s\" is not a table, view, materialized view, index, or TOAST table",
   10342             :                             RelationGetRelationName(rel))));
   10343             :             break;
   10344             :     }
   10345             : 
   10346             :     /* Special-case validation of view options */
   10347          50 :     if (rel->rd_rel->relkind == RELKIND_VIEW)
   10348             :     {
   10349          32 :         Query      *view_query = get_view_query(rel);
   10350          32 :         List       *view_options = untransformRelOptions(newOptions);
   10351             :         ListCell   *cell;
   10352          32 :         bool        check_option = false;
   10353             : 
   10354          43 :         foreach(cell, view_options)
   10355             :         {
   10356          11 :             DefElem    *defel = (DefElem *) lfirst(cell);
   10357             : 
   10358          11 :             if (pg_strcasecmp(defel->defname, "check_option") == 0)
   10359           4 :                 check_option = true;
   10360             :         }
   10361             : 
   10362             :         /*
   10363             :          * If the check option is specified, look to see if the view is
   10364             :          * actually auto-updatable or not.
   10365             :          */
   10366          32 :         if (check_option)
   10367             :         {
   10368           4 :             const char *view_updatable_error =
   10369             :             view_query_is_auto_updatable(view_query, true);
   10370             : 
   10371           4 :             if (view_updatable_error)
   10372           0 :                 ereport(ERROR,
   10373             :                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   10374             :                          errmsg("WITH CHECK OPTION is supported only on automatically updatable views"),
   10375             :                          errhint("%s", view_updatable_error)));
   10376             :         }
   10377             :     }
   10378             : 
   10379             :     /*
   10380             :      * All we need do here is update the pg_class row; the new options will be
   10381             :      * propagated into relcaches during post-commit cache inval.
   10382             :      */
   10383          50 :     memset(repl_val, 0, sizeof(repl_val));
   10384          50 :     memset(repl_null, false, sizeof(repl_null));
   10385          50 :     memset(repl_repl, false, sizeof(repl_repl));
   10386             : 
   10387          50 :     if (newOptions != (Datum) 0)
   10388          23 :         repl_val[Anum_pg_class_reloptions - 1] = newOptions;
   10389             :     else
   10390          27 :         repl_null[Anum_pg_class_reloptions - 1] = true;
   10391             : 
   10392          50 :     repl_repl[Anum_pg_class_reloptions - 1] = true;
   10393             : 
   10394          50 :     newtuple = heap_modify_tuple(tuple, RelationGetDescr(pgclass),
   10395             :                                  repl_val, repl_null, repl_repl);
   10396             : 
   10397          50 :     CatalogTupleUpdate(pgclass, &newtuple->t_self, newtuple);
   10398             : 
   10399          50 :     InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
   10400             : 
   10401          50 :     heap_freetuple(newtuple);
   10402             : 
   10403          50 :     ReleaseSysCache(tuple);
   10404             : 
   10405             :     /* repeat the whole exercise for the toast table, if there's one */
   10406          50 :     if (OidIsValid(rel->rd_rel->reltoastrelid))
   10407             :     {
   10408             :         Relation    toastrel;
   10409           7 :         Oid         toastid = rel->rd_rel->reltoastrelid;
   10410             : 
   10411           7 :         toastrel = heap_open(toastid, lockmode);
   10412             : 
   10413             :         /* Fetch heap tuple */
   10414           7 :         tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(toastid));
   10415           7 :         if (!HeapTupleIsValid(tuple))
   10416           0 :             elog(ERROR, "cache lookup failed for relation %u", toastid);
   10417             : 
   10418           7 :         if (operation == AT_ReplaceRelOptions)
   10419             :         {
   10420             :             /*
   10421             :              * If we're supposed to replace the reloptions list, we just
   10422             :              * pretend there were none before.
   10423             :              */
   10424           0 :             datum = (Datum) 0;
   10425           0 :             isnull = true;
   10426             :         }
   10427             :         else
   10428             :         {
   10429             :             /* Get the old reloptions */
   10430           7 :             datum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions,
   10431             :                                     &isnull);
   10432             :         }
   10433             : 
   10434           7 :         newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
   10435             :                                          defList, "toast", validnsps, false,
   10436             :                                          operation == AT_ResetRelOptions);
   10437             : 
   10438           7 :         (void) heap_reloptions(RELKIND_TOASTVALUE, newOptions, true);
   10439             : 
   10440           7 :         memset(repl_val, 0, sizeof(repl_val));
   10441           7 :         memset(repl_null, false, sizeof(repl_null));
   10442           7 :         memset(repl_repl, false, sizeof(repl_repl));
   10443             : 
   10444           7 :         if (newOptions != (Datum) 0)
   10445           4 :             repl_val[Anum_pg_class_reloptions - 1] = newOptions;
   10446             :         else
   10447           3 :             repl_null[Anum_pg_class_reloptions - 1] = true;
   10448             : 
   10449           7 :         repl_repl[Anum_pg_class_reloptions - 1] = true;
   10450             : 
   10451           7 :         newtuple = heap_modify_tuple(tuple, RelationGetDescr(pgclass),
   10452             :                                      repl_val, repl_null, repl_repl);
   10453             : 
   10454           7 :         CatalogTupleUpdate(pgclass, &newtuple->t_self, newtuple);
   10455             : 
   10456           7 :         InvokeObjectPostAlterHookArg(RelationRelationId,
   10457             :                                      RelationGetRelid(toastrel), 0,
   10458             :                                      InvalidOid, true);
   10459             : 
   10460           7 :         heap_freetuple(newtuple);
   10461             : 
   10462           7 :         ReleaseSysCache(tuple);
   10463             : 
   10464           7 :         heap_close(toastrel, NoLock);
   10465             :     }
   10466             : 
   10467          50 :     heap_close(pgclass, RowExclusiveLock);
   10468             : }
   10469             : 
   10470             : /*
   10471             :  * Execute ALTER TABLE SET TABLESPACE for cases where there is no tuple
   10472             :  * rewriting to be done, so we just want to copy the data as fast as possible.
   10473             :  */
   10474             : static void
   10475           9 : ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode)
   10476             : {
   10477             :     Relation    rel;
   10478             :     Oid         oldTableSpace;
   10479             :     Oid         reltoastrelid;
   10480             :     Oid         newrelfilenode;
   10481             :     RelFileNode newrnode;
   10482             :     SMgrRelation dstrel;
   10483             :     Relation    pg_class;
   10484             :     HeapTuple   tuple;
   10485             :     Form_pg_class rd_rel;
   10486             :     ForkNumber  forkNum;
   10487           9 :     List       *reltoastidxids = NIL;
   10488             :     ListCell   *lc;
   10489             : 
   10490             :     /*
   10491             :      * Need lock here in case we are recursing to toast table or index
   10492             :      */
   10493           9 :     rel = relation_open(tableOid, lockmode);
   10494             : 
   10495             :     /*
   10496             :      * No work if no change in tablespace.
   10497             :      */
   10498           9 :     oldTableSpace = rel->rd_rel->reltablespace;
   10499          18 :     if (newTableSpace == oldTableSpace ||
   10500          16 :         (newTableSpace == MyDatabaseTableSpace && oldTableSpace == 0))
   10501             :     {
   10502           0 :         InvokeObjectPostAlterHook(RelationRelationId,
   10503             :                                   RelationGetRelid(rel), 0);
   10504             : 
   10505           0 :         relation_close(rel, NoLock);
   10506           9 :         return;
   10507             :     }
   10508             : 
   10509             :     /*
   10510             :      * We cannot support moving mapped relations into different tablespaces.
   10511             :      * (In particular this eliminates all shared catalogs.)
   10512             :      */
   10513           9 :     if (RelationIsMapped(rel))
   10514           0 :         ereport(ERROR,
   10515             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   10516             :                  errmsg("cannot move system relation \"%s\"",
   10517             :                         RelationGetRelationName(rel))));
   10518             : 
   10519             :     /* Can't move a non-shared relation into pg_global */
   10520           9 :     if (newTableSpace == GLOBALTABLESPACE_OID)
   10521           0 :         ereport(ERROR,
   10522             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
   10523             :                  errmsg("only shared relations can be placed in pg_global tablespace")));
   10524             : 
   10525             :     /*
   10526             :      * Don't allow moving temp tables of other backends ... their local buffer
   10527             :      * manager is not going to cope.
   10528             :      */
   10529           9 :     if (RELATION_IS_OTHER_TEMP(rel))
   10530           0 :         ereport(ERROR,
   10531             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   10532             :                  errmsg("cannot move temporary tables of other sessions")));
   10533             : 
   10534           9 :     reltoastrelid = rel->rd_rel->reltoastrelid;
   10535             :     /* Fetch the list of indexes on toast relation if necessary */
   10536           9 :     if (OidIsValid(reltoastrelid))
   10537             :     {
   10538           0 :         Relation    toastRel = relation_open(reltoastrelid, lockmode);
   10539             : 
   10540           0 :         reltoastidxids = RelationGetIndexList(toastRel);
   10541           0 :         relation_close(toastRel, lockmode);
   10542             :     }
   10543             : 
   10544             :     /* Get a modifiable copy of the relation's pg_class row */
   10545           9 :     pg_class = heap_open(RelationRelationId, RowExclusiveLock);
   10546             : 
   10547           9 :     tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(tableOid));
   10548           9 :     if (!HeapTupleIsValid(tuple))
   10549           0 :         elog(ERROR, "cache lookup failed for relation %u", tableOid);
   10550           9 :     rd_rel = (Form_pg_class) GETSTRUCT(tuple);
   10551             : 
   10552             :     /*
   10553             :      * Since we copy the file directly without looking at the shared buffers,
   10554             :      * we'd better first flush out any pages of the source relation that are
   10555             :      * in shared buffers.  We assume no new changes will be made while we are
   10556             :      * holding exclusive lock on the rel.
   10557             :      */
   10558           9 :     FlushRelationBuffers(rel);
   10559             : 
   10560             :     /*
   10561             :      * Relfilenodes are not unique in databases across tablespaces, so we need
   10562             :      * to allocate a new one in the new tablespace.
   10563             :      */
   10564           9 :     newrelfilenode = GetNewRelFileNode(newTableSpace, NULL,
   10565           9 :                                        rel->rd_rel->relpersistence);
   10566             : 
   10567             :     /* Open old and new relation */
   10568           9 :     newrnode = rel->rd_node;
   10569           9 :     newrnode.relNode = newrelfilenode;
   10570           9 :     newrnode.spcNode = newTableSpace;
   10571           9 :     dstrel = smgropen(newrnode, rel->rd_backend);
   10572             : 
   10573           9 :     RelationOpenSmgr(rel);
   10574             : 
   10575             :     /*
   10576             :      * Create and copy all forks of the relation, and schedule unlinking of
   10577             :      * old physical files.
   10578             :      *
   10579             :      * NOTE: any conflict in relfilenode value will be caught in
   10580             :      * RelationCreateStorage().
   10581             :      */
   10582           9 :     RelationCreateStorage(newrnode, rel->rd_rel->relpersistence);
   10583             : 
   10584             :     /* copy main fork */
   10585           9 :     copy_relation_data(rel->rd_smgr, dstrel, MAIN_FORKNUM,
   10586           9 :                        rel->rd_rel->relpersistence);
   10587             : 
   10588             :     /* copy those extra forks that exist */
   10589          36 :     for (forkNum = MAIN_FORKNUM + 1; forkNum <= MAX_FORKNUM; forkNum++)
   10590             :     {
   10591          27 :         if (smgrexists(rel->rd_smgr, forkNum))
   10592             :         {
   10593           0 :             smgrcreate(dstrel, forkNum, false);
   10594             : 
   10595             :             /*
   10596             :              * WAL log creation if the relation is persistent, or this is the
   10597             :              * init fork of an unlogged relation.
   10598             :              */
   10599           0 :             if (rel->rd_rel->relpersistence == RELPERSISTENCE_PERMANENT ||
   10600           0 :                 (rel->rd_rel->relpersistence == RELPERSISTENCE_UNLOGGED &&
   10601             :                  forkNum == INIT_FORKNUM))
   10602           0 :                 log_smgrcreate(&newrnode, forkNum);
   10603           0 :             copy_relation_data(rel->rd_smgr, dstrel, forkNum,
   10604           0 :                                rel->rd_rel->relpersistence);
   10605             :         }
   10606             :     }
   10607             : 
   10608             :     /* drop old relation, and close new one */
   10609           9 :     RelationDropStorage(rel);
   10610           9 :     smgrclose(dstrel);
   10611             : 
   10612             :     /* update the pg_class row */
   10613           9 :     rd_rel->reltablespace = (newTableSpace == MyDatabaseTableSpace) ? InvalidOid : newTableSpace;
   10614           9 :     rd_rel->relfilenode = newrelfilenode;
   10615           9 :     CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
   10616             : 
   10617           9 :     InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
   10618             : 
   10619           9 :     heap_freetuple(tuple);
   10620             : 
   10621           9 :     heap_close(pg_class, RowExclusiveLock);
   10622             : 
   10623           9 :     relation_close(rel, NoLock);
   10624             : 
   10625             :     /* Make sure the reltablespace change is visible */
   10626           9 :     CommandCounterIncrement();
   10627             : 
   10628             :     /* Move associated toast relation and/or indexes, too */
   10629           9 :     if (OidIsValid(reltoastrelid))
   10630           0 :         ATExecSetTableSpace(reltoastrelid, newTableSpace, lockmode);
   10631           9 :     foreach(lc, reltoastidxids)
   10632           0 :         ATExecSetTableSpace(lfirst_oid(lc), newTableSpace, lockmode);
   10633             : 
   10634             :     /* Clean up */
   10635           9 :     list_free(reltoastidxids);
   10636             : }
   10637             : 
   10638             : /*
   10639             :  * Alter Table ALL ... SET TABLESPACE
   10640             :  *
   10641             :  * Allows a user to move all objects of some type in a given tablespace in the
   10642             :  * current database to another tablespace.  Objects can be chosen based on the
   10643             :  * owner of the object also, to allow users to move only their objects.
   10644             :  * The user must have CREATE rights on the new tablespace, as usual.   The main
   10645             :  * permissions handling is done by the lower-level table move function.
   10646             :  *
   10647             :  * All to-be-moved objects are locked first. If NOWAIT is specified and the
   10648             :  * lock can't be acquired then we ereport(ERROR).
   10649             :  */
   10650             : Oid
   10651           3 : AlterTableMoveAll(AlterTableMoveAllStmt *stmt)
   10652             : {
   10653           3 :     List       *relations = NIL;
   10654             :     ListCell   *l;
   10655             :     ScanKeyData key[1];
   10656             :     Relation    rel;
   10657             :     HeapScanDesc scan;
   10658             :     HeapTuple   tuple;
   10659             :     Oid         orig_tablespaceoid;
   10660             :     Oid         new_tablespaceoid;
   10661           3 :     List       *role_oids = roleSpecsToIds(stmt->roles);
   10662             : 
   10663             :     /* Ensure we were not asked to move something we can't */
   10664           3 :     if (stmt->objtype != OBJECT_TABLE && stmt->objtype != OBJECT_INDEX &&
   10665           0 :         stmt->objtype != OBJECT_MATVIEW)
   10666           0 :         ereport(ERROR,
   10667             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
   10668             :                  errmsg("only tables, indexes, and materialized views exist in tablespaces")));
   10669             : 
   10670             :     /* Get the orig and new tablespace OIDs */
   10671           3 :     orig_tablespaceoid = get_tablespace_oid(stmt->orig_tablespacename, false);
   10672           3 :     new_tablespaceoid = get_tablespace_oid(stmt->new_tablespacename, false);
   10673             : 
   10674             :     /* Can't move shared relations in to or out of pg_global */
   10675             :     /* This is also checked by ATExecSetTableSpace, but nice to stop earlier */
   10676           3 :     if (orig_tablespaceoid == GLOBALTABLESPACE_OID ||
   10677             :         new_tablespaceoid == GLOBALTABLESPACE_OID)
   10678           0 :         ereport(ERROR,
   10679             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
   10680             :                  errmsg("cannot move relations in to or out of pg_global tablespace")));
   10681             : 
   10682             :     /*
   10683             :      * Must have CREATE rights on the new tablespace, unless it is the
   10684             :      * database default tablespace (which all users implicitly have CREATE
   10685             :      * rights on).
   10686             :      */
   10687           3 :     if (OidIsValid(new_tablespaceoid) && new_tablespaceoid != MyDatabaseTableSpace)
   10688             :     {
   10689             :         AclResult   aclresult;
   10690             : 
   10691           0 :         aclresult = pg_tablespace_aclcheck(new_tablespaceoid, GetUserId(),
   10692             :                                            ACL_CREATE);
   10693           0 :         if (aclresult != ACLCHECK_OK)
   10694           0 :             aclcheck_error(aclresult, ACL_KIND_TABLESPACE,
   10695           0 :                            get_tablespace_name(new_tablespaceoid));
   10696             :     }
   10697             : 
   10698             :     /*
   10699             :      * Now that the checks are done, check if we should set either to
   10700             :      * InvalidOid because it is our database's default tablespace.
   10701             :      */
   10702           3 :     if (orig_tablespaceoid == MyDatabaseTableSpace)
   10703           0 :         orig_tablespaceoid = InvalidOid;
   10704             : 
   10705           3 :     if (new_tablespaceoid == MyDatabaseTableSpace)
   10706           3 :         new_tablespaceoid = InvalidOid;
   10707             : 
   10708             :     /* no-op */
   10709           3 :     if (orig_tablespaceoid == new_tablespaceoid)
   10710           0 :         return new_tablespaceoid;
   10711             : 
   10712             :     /*
   10713             :      * Walk the list of objects in the tablespace and move them. This will
   10714             :      * only find objects in our database, of course.
   10715             :      */
   10716           3 :     ScanKeyInit(&key[0],
   10717             :                 Anum_pg_class_reltablespace,
   10718             :                 BTEqualStrategyNumber, F_OIDEQ,
   10719             :                 ObjectIdGetDatum(orig_tablespaceoid));
   10720             : 
   10721           3 :     rel = heap_open(RelationRelationId, AccessShareLock);
   10722           3 :     scan = heap_beginscan_catalog(rel, 1, key);
   10723          16 :     while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
   10724             :     {
   10725          10 :         Oid         relOid = HeapTupleGetOid(tuple);
   10726             :         Form_pg_class relForm;
   10727             : 
   10728          10 :         relForm = (Form_pg_class) GETSTRUCT(tuple);
   10729             : 
   10730             :         /*
   10731             :          * Do not move objects in pg_catalog as part of this, if an admin
   10732             :          * really wishes to do so, they can issue the individual ALTER
   10733             :          * commands directly.
   10734             :          *
   10735             :          * Also, explicitly avoid any shared tables, temp tables, or TOAST
   10736             :          * (TOAST will be moved with the main table).
   10737             :          */
   10738          20 :         if (IsSystemNamespace(relForm->relnamespace) || relForm->relisshared ||
   10739          20 :             isAnyTempNamespace(relForm->relnamespace) ||
   10740          10 :             relForm->relnamespace == PG_TOAST_NAMESPACE)
   10741           0 :             continue;
   10742             : 
   10743             :         /* Only move the object type requested */
   10744          17 :         if ((stmt->objtype == OBJECT_TABLE &&
   10745          10 :              relForm->relkind != RELKIND_RELATION &&
   10746          10 :              relForm->relkind != RELKIND_PARTITIONED_TABLE) ||
   10747          10 :             (stmt->objtype == OBJECT_INDEX &&
   10748          10 :              relForm->relkind != RELKIND_INDEX) ||
   10749           7 :             (stmt->objtype == OBJECT_MATVIEW &&
   10750           0 :              relForm->relkind != RELKIND_MATVIEW))
   10751           3 :             continue;
   10752             : 
   10753             :         /* Check if we are only moving objects owned by certain roles */
   10754           7 :         if (role_oids != NIL && !list_member_oid(role_oids, relForm->relowner))
   10755           0 :             continue;
   10756             : 
   10757             :         /*
   10758             :          * Handle permissions-checking here since we are locking the tables
   10759             :          * and also to avoid doing a bunch of work only to fail part-way. Note
   10760             :          * that permissions will also be checked by AlterTableInternal().
   10761             :          *
   10762             :          * Caller must be considered an owner on the table to move it.
   10763             :          */
   10764           7 :         if (!pg_class_ownercheck(relOid, GetUserId()))
   10765           0 :             aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
   10766           0 :                            NameStr(relForm->relname));
   10767             : 
   10768           7 :         if (stmt->nowait &&
   10769           0 :             !ConditionalLockRelationOid(relOid, AccessExclusiveLock))
   10770           0 :             ereport(ERROR,
   10771             :                     (errcode(ERRCODE_OBJECT_IN_USE),
   10772             :                      errmsg("aborting because lock on relation \"%s.%s\" is not available",
   10773             :                             get_namespace_name(relForm->relnamespace),
   10774             :                             NameStr(relForm->relname))));
   10775             :         else
   10776           7 :             LockRelationOid(relOid, AccessExclusiveLock);
   10777             : 
   10778             :         /* Add to our list of objects to move */
   10779           7 :         relations = lappend_oid(relations, relOid);
   10780             :     }
   10781             : 
   10782           3 :     heap_endscan(scan);
   10783           3 :     heap_close(rel, AccessShareLock);
   10784             : 
   10785           3 :     if (relations == NIL)
   10786           1 :         ereport(NOTICE,
   10787             :                 (errcode(ERRCODE_NO_DATA_FOUND),
   10788             :                  errmsg("no matching relations in tablespace \"%s\" found",
   10789             :                         orig_tablespaceoid == InvalidOid ? "(database default)" :
   10790             :                         get_tablespace_name(orig_tablespaceoid))));
   10791             : 
   10792             :     /* Everything is locked, loop through and move all of the relations. */
   10793          10 :     foreach(l, relations)
   10794             :     {
   10795           7 :         List       *cmds = NIL;
   10796           7 :         AlterTableCmd *cmd = makeNode(AlterTableCmd);
   10797             : 
   10798           7 :         cmd->subtype = AT_SetTableSpace;
   10799           7 :         cmd->name = stmt->new_tablespacename;
   10800             : 
   10801           7 :         cmds = lappend(cmds, cmd);
   10802             : 
   10803           7 :         EventTriggerAlterTableStart((Node *) stmt);
   10804             :         /* OID is set by AlterTableInternal */
   10805           7 :         AlterTableInternal(lfirst_oid(l), cmds, false);
   10806           7 :         EventTriggerAlterTableEnd();
   10807             :     }
   10808             : 
   10809           3 :     return new_tablespaceoid;
   10810             : }
   10811             : 
   10812             : /*
   10813             :  * Copy data, block by block
   10814             :  */
   10815             : static void
   10816           9 : copy_relation_data(SMgrRelation src, SMgrRelation dst,
   10817             :                    ForkNumber forkNum, char relpersistence)
   10818             : {
   10819             :     char       *buf;
   10820             :     Page        page;
   10821             :     bool        use_wal;
   10822             :     bool        copying_initfork;
   10823             :     BlockNumber nblocks;
   10824             :     BlockNumber blkno;
   10825             : 
   10826             :     /*
   10827             :      * palloc the buffer so that it's MAXALIGN'd.  If it were just a local
   10828             :      * char[] array, the compiler might align it on any byte boundary, which
   10829             :      * can seriously hurt transfer speed to and from the kernel; not to
   10830             :      * mention possibly making log_newpage's accesses to the page header fail.
   10831             :      */
   10832           9 :     buf = (char *) palloc(BLCKSZ);
   10833           9 :     page = (Page) buf;
   10834             : 
   10835             :     /*
   10836             :      * The init fork for an unlogged relation in many respects has to be
   10837             :      * treated the same as normal relation, changes need to be WAL logged and
   10838             :      * it needs to be synced to disk.
   10839             :      */
   10840           9 :     copying_initfork = relpersistence == RELPERSISTENCE_UNLOGGED &&
   10841             :         forkNum == INIT_FORKNUM;
   10842             : 
   10843             :     /*
   10844             :      * We need to log the copied data in WAL iff WAL archiving/streaming is
   10845             :      * enabled AND it's a permanent relation.
   10846             :      */
   10847          18 :     use_wal = XLogIsNeeded() &&
   10848           0 :         (relpersistence == RELPERSISTENCE_PERMANENT || copying_initfork);
   10849             : 
   10850           9 :     nblocks = smgrnblocks(src, forkNum);
   10851             : 
   10852          21 :     for (blkno = 0; blkno < nblocks; blkno++)
   10853             :     {
   10854             :         /* If we got a cancel signal during the copy of the data, quit */
   10855          12 :         CHECK_FOR_INTERRUPTS();
   10856             : 
   10857          12 :         smgrread(src, forkNum, blkno, buf);
   10858             : 
   10859          12 :         if (!PageIsVerified(page, blkno))
   10860           0 :             ereport(ERROR,
   10861             :                     (errcode(ERRCODE_DATA_CORRUPTED),
   10862             :                      errmsg("invalid page in block %u of relation %s",
   10863             :                             blkno,
   10864             :                             relpathbackend(src->smgr_rnode.node,
   10865             :                                            src->smgr_rnode.backend,
   10866             :                                            forkNum))));
   10867             : 
   10868             :         /*
   10869             :          * WAL-log the copied page. Unfortunately we don't know what kind of a
   10870             :          * page this is, so we have to log the full page including any unused
   10871             :          * space.
   10872             :          */
   10873          12 :         if (use_wal)
   10874          12 :             log_newpage(&dst->smgr_rnode.node, forkNum, blkno, page, false);
   10875             : 
   10876          12 :         PageSetChecksumInplace(page, blkno);
   10877             : 
   10878             :         /*
   10879             :          * Now write the page.  We say isTemp = true even if it's not a temp
   10880             :          * rel, because there's no need for smgr to schedule an fsync for this
   10881             :          * write; we'll do it ourselves below.
   10882             :          */
   10883          12 :         smgrextend(dst, forkNum, blkno, buf, true);
   10884             :     }
   10885             : 
   10886           9 :     pfree(buf);
   10887             : 
   10888             :     /*
   10889             :      * If the rel is WAL-logged, must fsync before commit.  We use heap_sync
   10890             :      * to ensure that the toast table gets fsync'd too.  (For a temp or
   10891             :      * unlogged rel we don't care since the data will be gone after a crash
   10892             :      * anyway.)
   10893             :      *
   10894             :      * It's obvious that we must do this when not WAL-logging the copy. It's
   10895             :      * less obvious that we have to do it even if we did WAL-log the copied
   10896             :      * pages. The reason is that since we're copying outside shared buffers, a
   10897             :      * CHECKPOINT occurring during the copy has no way to flush the previously
   10898             :      * written data to disk (indeed it won't know the new rel even exists).  A
   10899             :      * crash later on would replay WAL from the checkpoint, therefore it
   10900             :      * wouldn't replay our earlier WAL entries. If we do not fsync those pages
   10901             :      * here, they might still not be on disk when the crash occurs.
   10902             :      */
   10903           9 :     if (relpersistence == RELPERSISTENCE_PERMANENT || copying_initfork)
   10904           9 :         smgrimmedsync(dst, forkNum);
   10905           9 : }
   10906             : 
   10907             : /*
   10908             :  * ALTER TABLE ENABLE/DISABLE TRIGGER
   10909             :  *
   10910             :  * We just pass this off to trigger.c.
   10911             :  */
   10912             : static void
   10913           6 : ATExecEnableDisableTrigger(Relation rel, char *trigname,
   10914             :                            char fires_when, bool skip_system, LOCKMODE lockmode)
   10915             : {
   10916           6 :     EnableDisableTrigger(rel, trigname, fires_when, skip_system);
   10917           6 : }
   10918             : 
   10919             : /*
   10920             :  * ALTER TABLE ENABLE/DISABLE RULE
   10921             :  *
   10922             :  * We just pass this off to rewriteDefine.c.
   10923             :  */
   10924             : static void
   10925           0 : ATExecEnableDisableRule(Relation rel, char *rulename,
   10926             :                         char fires_when, LOCKMODE lockmode)
   10927             : {
   10928           0 :     EnableDisableRule(rel, rulename, fires_when);
   10929           0 : }
   10930             : 
   10931             : /*
   10932             :  * ALTER TABLE INHERIT
   10933             :  *
   10934             :  * Add a parent to the child's parents. This verifies that all the columns and
   10935             :  * check constraints of the parent appear in the child and that they have the
   10936             :  * same data types and expressions.
   10937             :  */
   10938             : static void
   10939          25 : ATPrepAddInherit(Relation child_rel)
   10940             : {
   10941          25 :     if (child_rel->rd_rel->reloftype)
   10942           1 :         ereport(ERROR,
   10943             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   10944             :                  errmsg("cannot change inheritance of typed table")));
   10945             : 
   10946          24 :     if (child_rel->rd_rel->relispartition)
   10947           1 :         ereport(ERROR,
   10948             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   10949             :                  errmsg("cannot change inheritance of a partition")));
   10950             : 
   10951          23 :     if (child_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
   10952           1 :         ereport(ERROR,
   10953             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   10954             :                  errmsg("cannot change inheritance of partitioned table")));
   10955          22 : }
   10956             : 
   10957             : /*
   10958             :  * Return the address of the new parent relation.
   10959             :  */
   10960             : static ObjectAddress
   10961          22 : ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode)
   10962             : {
   10963             :     Relation    parent_rel;
   10964             :     List       *children;
   10965             :     ObjectAddress address;
   10966             :     const char *trigger_name;
   10967             : 
   10968             :     /*
   10969             :      * A self-exclusive lock is needed here.  See the similar case in
   10970             :      * MergeAttributes() for a full explanation.
   10971             :      */
   10972          22 :     parent_rel = heap_openrv(parent, ShareUpdateExclusiveLock);
   10973             : 
   10974             :     /*
   10975             :      * Must be owner of both parent and child -- child was checked by
   10976             :      * ATSimplePermissions call in ATPrepCmd
   10977             :      */
   10978          22 :     ATSimplePermissions(parent_rel, ATT_TABLE | ATT_FOREIGN_TABLE);
   10979             : 
   10980             :     /* Permanent rels cannot inherit from temporary ones */
   10981          23 :     if (parent_rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
   10982           1 :         child_rel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
   10983           0 :         ereport(ERROR,
   10984             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   10985             :                  errmsg("cannot inherit from temporary relation \"%s\"",
   10986             :                         RelationGetRelationName(parent_rel))));
   10987             : 
   10988             :     /* If parent rel is temp, it must belong to this session */
   10989          23 :     if (parent_rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
   10990           1 :         !parent_rel->rd_islocaltemp)
   10991           0 :         ereport(ERROR,
   10992             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   10993             :                  errmsg("cannot inherit from temporary relation of another session")));
   10994             : 
   10995             :     /* Ditto for the child */
   10996          23 :     if (child_rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
   10997           1 :         !child_rel->rd_islocaltemp)
   10998           0 :         ereport(ERROR,
   10999             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   11000             :                  errmsg("cannot inherit to temporary relation of another session")));
   11001             : 
   11002             :     /* Prevent partitioned tables from becoming inheritance parents */
   11003          22 :     if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
   11004           1 :         ereport(ERROR,
   11005             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   11006             :                  errmsg("cannot inherit from partitioned table \"%s\"",
   11007             :                         parent->relname)));
   11008             : 
   11009             :     /* Likewise for partitions */
   11010          21 :     if (parent_rel->rd_rel->relispartition)
   11011           1 :         ereport(ERROR,
   11012             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   11013             :                  errmsg("cannot inherit from a partition")));
   11014             : 
   11015             :     /*
   11016             :      * Prevent circularity by seeing if proposed parent inherits from child.
   11017             :      * (In particular, this disallows making a rel inherit from itself.)
   11018             :      *
   11019             :      * This is not completely bulletproof because of race conditions: in
   11020             :      * multi-level inheritance trees, someone else could concurrently be
   11021             :      * making another inheritance link that closes the loop but does not join
   11022             :      * either of the rels we have locked.  Preventing that seems to require
   11023             :      * exclusive locks on the entire inheritance tree, which is a cure worse
   11024             :      * than the disease.  find_all_inheritors() will cope with circularity
   11025             :      * anyway, so don't sweat it too much.
   11026             :      *
   11027             :      * We use weakest lock we can on child's children, namely AccessShareLock.
   11028             :      */
   11029          20 :     children = find_all_inheritors(RelationGetRelid(child_rel),
   11030             :                                    AccessShareLock, NULL);
   11031             : 
   11032          20 :     if (list_member_oid(children, RelationGetRelid(parent_rel)))
   11033           2 :         ereport(ERROR,
   11034             :                 (errcode(ERRCODE_DUPLICATE_TABLE),
   11035             :                  errmsg("circular inheritance not allowed"),
   11036             :                  errdetail("\"%s\" is already a child of \"%s\".",
   11037             :                            parent->relname,
   11038             :                            RelationGetRelationName(child_rel))));
   11039             : 
   11040             :     /* If parent has OIDs then child must have OIDs */
   11041          18 :     if (parent_rel->rd_rel->relhasoids && !child_rel->rd_rel->relhasoids)
   11042           1 :         ereport(ERROR,
   11043             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   11044             :                  errmsg("table \"%s\" without OIDs cannot inherit from table \"%s\" with OIDs",
   11045             :                         RelationGetRelationName(child_rel),
   11046             :                         RelationGetRelationName(parent_rel))));
   11047             : 
   11048             :     /*
   11049             :      * If child_rel has row-level triggers with transition tables, we
   11050             :      * currently don't allow it to become an inheritance child.  See also
   11051             :      * prohibitions in ATExecAttachPartition() and CreateTrigger().
   11052             :      */
   11053          17 :     trigger_name = FindTriggerIncompatibleWithInheritance(child_rel->trigdesc);
   11054          17 :     if (trigger_name != NULL)
   11055           1 :         ereport(ERROR,
   11056             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   11057             :                  errmsg("trigger \"%s\" prevents table \"%s\" from becoming an inheritance child",
   11058             :                         trigger_name, RelationGetRelationName(child_rel)),
   11059             :                  errdetail("ROW triggers with transition tables are not supported in inheritance hierarchies")));
   11060             : 
   11061             :     /* OK to create inheritance */
   11062          16 :     CreateInheritance(child_rel, parent_rel);
   11063             : 
   11064          11 :     ObjectAddressSet(address, RelationRelationId,
   11065             :                      RelationGetRelid(parent_rel));
   11066             : 
   11067             :     /* keep our lock on the parent relation until commit */
   11068          11 :     heap_close(parent_rel, NoLock);
   11069             : 
   11070          11 :     return address;
   11071             : }
   11072             : 
   11073             : /*
   11074             :  * CreateInheritance
   11075             :  *      Catalog manipulation portion of creating inheritance between a child
   11076             :  *      table and a parent table.
   11077             :  *
   11078             :  * Common to ATExecAddInherit() and ATExecAttachPartition().
   11079             :  */
   11080             : static void
   11081          57 : CreateInheritance(Relation child_rel, Relation parent_rel)
   11082             : {
   11083             :     Relation    catalogRelation;
   11084             :     SysScanDesc scan;
   11085             :     ScanKeyData key;
   11086             :     HeapTuple   inheritsTuple;
   11087             :     int32       inhseqno;
   11088             : 
   11089             :     /* Note: get RowExclusiveLock because we will write pg_inherits below. */
   11090          57 :     catalogRelation = heap_open(InheritsRelationId, RowExclusiveLock);
   11091             : 
   11092             :     /*
   11093             :      * Check for duplicates in the list of parents, and determine the highest
   11094             :      * inhseqno already present; we'll use the next one for the new parent.
   11095             :      * Also, if proposed child is a partition, it cannot already be
   11096             :      * inheriting.
   11097             :      *
   11098             :      * Note: we do not reject the case where the child already inherits from
   11099             :      * the parent indirectly; CREATE TABLE doesn't reject comparable cases.
   11100             :      */
   11101          57 :     ScanKeyInit(&key,
   11102             :                 Anum_pg_inherits_inhrelid,
   11103             :                 BTEqualStrategyNumber, F_OIDEQ,
   11104             :                 ObjectIdGetDatum(RelationGetRelid(child_rel)));
   11105          57 :     scan = systable_beginscan(catalogRelation, InheritsRelidSeqnoIndexId,
   11106             :                               true, NULL, 1, &key);
   11107             : 
   11108             :     /* inhseqno sequences start at 1 */
   11109          57 :     inhseqno = 0;
   11110         119 :     while (HeapTupleIsValid(inheritsTuple = systable_getnext(scan)))
   11111             :     {
   11112           6 :         Form_pg_inherits inh = (Form_pg_inherits) GETSTRUCT(inheritsTuple);
   11113             : 
   11114           6 :         if (inh->inhparent == RelationGetRelid(parent_rel))
   11115           1 :             ereport(ERROR,
   11116             :                     (errcode(ERRCODE_DUPLICATE_TABLE),
   11117             :                      errmsg("relation \"%s\" would be inherited from more than once",
   11118             :                             RelationGetRelationName(parent_rel))));
   11119             : 
   11120           5 :         if (inh->inhseqno > inhseqno)
   11121           5 :             inhseqno = inh->inhseqno;
   11122             :     }
   11123          56 :     systable_endscan(scan);
   11124             : 
   11125             :     /* Match up the columns and bump attinhcount as needed */
   11126          56 :     MergeAttributesIntoExisting(child_rel, parent_rel);
   11127             : 
   11128             :     /* Match up the constraints and bump coninhcount as needed */
   11129          50 :     MergeConstraintsIntoExisting(child_rel, parent_rel);
   11130             : 
   11131             :     /*
   11132             :      * OK, it looks valid.  Make the catalog entries that show inheritance.
   11133             :      */
   11134          45 :     StoreCatalogInheritance1(RelationGetRelid(child_rel),
   11135             :                              RelationGetRelid(parent_rel),
   11136             :                              inhseqno + 1,
   11137             :                              catalogRelation,
   11138          45 :                              parent_rel->rd_rel->relkind ==
   11139             :                              RELKIND_PARTITIONED_TABLE);
   11140             : 
   11141             :     /* Now we're done with pg_inherits */
   11142          45 :     heap_close(catalogRelation, RowExclusiveLock);
   11143          45 : }
   11144             : 
   11145             : /*
   11146             :  * Obtain the source-text form of the constraint expression for a check
   11147             :  * constraint, given its pg_constraint tuple
   11148             :  */
   11149             : static char *
   11150          14 : decompile_conbin(HeapTuple contup, TupleDesc tupdesc)
   11151             : {
   11152             :     Form_pg_constraint con;
   11153             :     bool        isnull;
   11154             :     Datum       attr;
   11155             :     Datum       expr;
   11156             : 
   11157          14 :     con = (Form_pg_constraint) GETSTRUCT(contup);
   11158          14 :     attr = heap_getattr(contup, Anum_pg_constraint_conbin, tupdesc, &isnull);
   11159          14 :     if (isnull)
   11160           0 :         elog(ERROR, "null conbin for constraint %u", HeapTupleGetOid(contup));
   11161             : 
   11162          14 :     expr = DirectFunctionCall2(pg_get_expr, attr,
   11163             :                                ObjectIdGetDatum(con->conrelid));
   11164          14 :     return TextDatumGetCString(expr);
   11165             : }
   11166             : 
   11167             : /*
   11168             :  * Determine whether two check constraints are functionally equivalent
   11169             :  *
   11170             :  * The test we apply is to see whether they reverse-compile to the same
   11171             :  * source string.  This insulates us from issues like whether attributes
   11172             :  * have the same physical column numbers in parent and child relations.
   11173             :  */
   11174             : static bool
   11175           7 : constraints_equivalent(HeapTuple a, HeapTuple b, TupleDesc tupleDesc)
   11176             : {
   11177           7 :     Form_pg_constraint acon = (Form_pg_constraint) GETSTRUCT(a);
   11178           7 :     Form_pg_constraint bcon = (Form_pg_constraint) GETSTRUCT(b);
   11179             : 
   11180          14 :     if (acon->condeferrable != bcon->condeferrable ||
   11181          14 :         acon->condeferred != bcon->condeferred ||
   11182           7 :         strcmp(decompile_conbin(a, tupleDesc),
   11183           7 :                decompile_conbin(b, tupleDesc)) != 0)
   11184           1 :         return false;
   11185             :     else
   11186           6 :         return true;
   11187             : }
   11188             : 
   11189             : /*
   11190             :  * Check columns in child table match up with columns in parent, and increment
   11191             :  * their attinhcount.
   11192             :  *
   11193             :  * Called by CreateInheritance
   11194             :  *
   11195             :  * Currently all parent columns must be found in child. Missing columns are an
   11196             :  * error.  One day we might consider creating new columns like CREATE TABLE
   11197             :  * does.  However, that is widely unpopular --- in the common use case of
   11198             :  * partitioned tables it's a foot-gun.
   11199             :  *
   11200             :  * The data type must match exactly. If the parent column is NOT NULL then
   11201             :  * the child must be as well. Defaults are not compared, however.
   11202             :  */
   11203             : static void
   11204          56 : MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel)
   11205             : {
   11206             :     Relation    attrrel;
   11207             :     AttrNumber  parent_attno;
   11208             :     int         parent_natts;
   11209             :     TupleDesc   tupleDesc;
   11210             :     HeapTuple   tuple;
   11211          56 :     bool        child_is_partition = false;
   11212             : 
   11213          56 :     attrrel = heap_open(AttributeRelationId, RowExclusiveLock);
   11214             : 
   11215          56 :     tupleDesc = RelationGetDescr(parent_rel);
   11216          56 :     parent_natts = tupleDesc->natts;
   11217             : 
   11218             :     /* If parent_rel is a partitioned table, child_rel must be a partition */
   11219          56 :     if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
   11220          41 :         child_is_partition = true;
   11221             : 
   11222         182 :     for (parent_attno = 1; parent_attno <= parent_natts; parent_attno++)
   11223             :     {
   11224         132 :         Form_pg_attribute attribute = TupleDescAttr(tupleDesc,
   11225             :                                                     parent_attno - 1);
   11226         132 :         char       *attributeName = NameStr(attribute->attname);
   11227             : 
   11228             :         /* Ignore dropped columns in the parent. */
   11229         132 :         if (attribute->attisdropped)
   11230          11 :             continue;
   11231             : 
   11232             :         /* Find same column in child (matching on column name). */
   11233         121 :         tuple = SearchSysCacheCopyAttName(RelationGetRelid(child_rel),
   11234             :                                           attributeName);
   11235         121 :         if (HeapTupleIsValid(tuple))
   11236             :         {
   11237             :             /* Check they are same type, typmod, and collation */
   11238         119 :             Form_pg_attribute childatt = (Form_pg_attribute) GETSTRUCT(tuple);
   11239             : 
   11240         237 :             if (attribute->atttypid != childatt->atttypid ||
   11241         118 :                 attribute->atttypmod != childatt->atttypmod)
   11242           2 :                 ereport(ERROR,
   11243             :                         (errcode(ERRCODE_DATATYPE_MISMATCH),
   11244             :                          errmsg("child table \"%s\" has different type for column \"%s\"",
   11245             :                                 RelationGetRelationName(child_rel),
   11246             :                                 attributeName)));
   11247             : 
   11248         117 :             if (attribute->attcollation != childatt->attcollation)
   11249           1 :                 ereport(ERROR,
   11250             :                         (errcode(ERRCODE_COLLATION_MISMATCH),
   11251             :                          errmsg("child table \"%s\" has different collation for column \"%s\"",
   11252             :                                 RelationGetRelationName(child_rel),
   11253             :                                 attributeName)));
   11254             : 
   11255             :             /*
   11256             :              * Check child doesn't discard NOT NULL property.  (Other
   11257             :              * constraints are checked elsewhere.)
   11258             :              */
   11259         116 :             if (attribute->attnotnull && !childatt->attnotnull)
   11260           1 :                 ereport(ERROR,
   11261             :                         (errcode(ERRCODE_DATATYPE_MISMATCH),
   11262             :                          errmsg("column \"%s\" in child table must be marked NOT NULL",
   11263             :                                 attributeName)));
   11264             : 
   11265             :             /*
   11266             :              * OK, bump the child column's inheritance count.  (If we fail
   11267             :              * later on, this change will just roll back.)
   11268             :              */
   11269         115 :             childatt->attinhcount++;
   11270             : 
   11271             :             /*
   11272             :              * In case of partitions, we must enforce that value of attislocal
   11273             :              * is same in all partitions. (Note: there are only inherited
   11274             :              * attributes in partitions)
   11275             :              */
   11276         115 :             if (child_is_partition)
   11277             :             {
   11278          86 :                 Assert(childatt->attinhcount == 1);
   11279          86 :                 childatt->attislocal = false;
   11280             :             }
   11281             : 
   11282         115 :             CatalogTupleUpdate(attrrel, &tuple->t_self, tuple);
   11283         115 :             heap_freetuple(tuple);
   11284             :         }
   11285             :         else
   11286             :         {
   11287           2 :             ereport(ERROR,
   11288             :                     (errcode(ERRCODE_DATATYPE_MISMATCH),
   11289             :                      errmsg("child table is missing column \"%s\"",
   11290             :                             attributeName)));
   11291             :         }
   11292             :     }
   11293             : 
   11294             :     /*
   11295             :      * If the parent has an OID column, so must the child, and we'd better
   11296             :      * update the child's attinhcount and attislocal the same as for normal
   11297             :      * columns.  We needn't check data type or not-nullness though.
   11298             :      */
   11299          50 :     if (tupleDesc->tdhasoid)
   11300             :     {
   11301             :         /*
   11302             :          * Here we match by column number not name; the match *must* be the
   11303             :          * system column, not some random column named "oid".
   11304             :          */
   11305           2 :         tuple = SearchSysCacheCopy2(ATTNUM,
   11306             :                                     ObjectIdGetDatum(RelationGetRelid(child_rel)),
   11307             :                                     Int16GetDatum(ObjectIdAttributeNumber));
   11308           2 :         if (HeapTupleIsValid(tuple))
   11309             :         {
   11310           2 :             Form_pg_attribute childatt = (Form_pg_attribute) GETSTRUCT(tuple);
   11311             : 
   11312             :             /* See comments above; these changes should be the same */
   11313           2 :             childatt->attinhcount++;
   11314             : 
   11315           2 :             if (child_is_partition)
   11316             :             {
   11317           0 :                 Assert(childatt->attinhcount == 1);
   11318           0 :                 childatt->attislocal = false;
   11319             :             }
   11320             : 
   11321           2 :             CatalogTupleUpdate(attrrel, &tuple->t_self, tuple);
   11322           2 :             heap_freetuple(tuple);
   11323             :         }
   11324             :         else
   11325             :         {
   11326           0 :             ereport(ERROR,
   11327             :                     (errcode(ERRCODE_DATATYPE_MISMATCH),
   11328             :                      errmsg("child table is missing column \"%s\"",
   11329             :                             "oid")));
   11330             :         }
   11331             :     }
   11332             : 
   11333          50 :     heap_close(attrrel, RowExclusiveLock);
   11334          50 : }
   11335             : 
   11336             : /*
   11337             :  * Check constraints in child table match up with constraints in parent,
   11338             :  * and increment their coninhcount.
   11339             :  *
   11340             :  * Constraints that are marked ONLY in the parent are ignored.
   11341             :  *
   11342             :  * Called by CreateInheritance
   11343             :  *
   11344             :  * Currently all constraints in parent must be present in the child. One day we
   11345             :  * may consider adding new constraints like CREATE TABLE does.
   11346             :  *
   11347             :  * XXX This is O(N^2) which may be an issue with tables with hundreds of
   11348             :  * constraints. As long as tables have more like 10 constraints it shouldn't be
   11349             :  * a problem though. Even 100 constraints ought not be the end of the world.
   11350             :  *
   11351             :  * XXX See MergeWithExistingConstraint too if you change this code.
   11352             :  */
   11353             : static void
   11354          50 : MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel)
   11355             : {
   11356             :     Relation    catalog_relation;
   11357             :     TupleDesc   tuple_desc;
   11358             :     SysScanDesc parent_scan;
   11359             :     ScanKeyData parent_key;
   11360             :     HeapTuple   parent_tuple;
   11361          50 :     bool        child_is_partition = false;
   11362             : 
   11363          50 :     catalog_relation = heap_open(ConstraintRelationId, RowExclusiveLock);
   11364          50 :     tuple_desc = RelationGetDescr(catalog_relation);
   11365             : 
   11366             :     /* If parent_rel is a partitioned table, child_rel must be a partition */
   11367          50 :     if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
   11368          37 :         child_is_partition = true;
   11369             : 
   11370             :     /* Outer loop scans through the parent's constraint definitions */
   11371          50 :     ScanKeyInit(&parent_key,
   11372             :                 Anum_pg_constraint_conrelid,
   11373             :                 BTEqualStrategyNumber, F_OIDEQ,
   11374             :                 ObjectIdGetDatum(RelationGetRelid(parent_rel)));
   11375          50 :     parent_scan = systable_beginscan(catalog_relation, ConstraintRelidIndexId,
   11376             :                                      true, NULL, 1, &parent_key);
   11377             : 
   11378         107 :     while (HeapTupleIsValid(parent_tuple = systable_getnext(parent_scan)))
   11379             :     {
   11380          12 :         Form_pg_constraint parent_con = (Form_pg_constraint) GETSTRUCT(parent_tuple);
   11381             :         SysScanDesc child_scan;
   11382             :         ScanKeyData child_key;
   11383             :         HeapTuple   child_tuple;
   11384          12 :         bool        found = false;
   11385             : 
   11386          12 :         if (parent_con->contype != CONSTRAINT_CHECK)
   11387           1 :             continue;
   11388             : 
   11389             :         /* if the parent's constraint is marked NO INHERIT, it's not inherited */
   11390          12 :         if (parent_con->connoinherit)
   11391           1 :             continue;
   11392             : 
   11393             :         /* Search for a child constraint matching this one */
   11394          11 :         ScanKeyInit(&child_key,
   11395             :                     Anum_pg_constraint_conrelid,
   11396             :                     BTEqualStrategyNumber, F_OIDEQ,
   11397             :                     ObjectIdGetDatum(RelationGetRelid(child_rel)));
   11398          11 :         child_scan = systable_beginscan(catalog_relation, ConstraintRelidIndexId,
   11399             :                                         true, NULL, 1, &child_key);
   11400             : 
   11401          11 :         while (HeapTupleIsValid(child_tuple = systable_getnext(child_scan)))
   11402             :         {
   11403           8 :             Form_pg_constraint child_con = (Form_pg_constraint) GETSTRUCT(child_tuple);
   11404             :             HeapTuple   child_copy;
   11405             : 
   11406           8 :             if (child_con->contype != CONSTRAINT_CHECK)
   11407           0 :                 continue;
   11408             : 
   11409           8 :             if (strcmp(NameStr(parent_con->conname),
   11410           8 :                        NameStr(child_con->conname)) != 0)
   11411           1 :                 continue;
   11412             : 
   11413           7 :             if (!constraints_equivalent(parent_tuple, child_tuple, tuple_desc))
   11414           1 :                 ereport(ERROR,
   11415             :                         (errcode(ERRCODE_DATATYPE_MISMATCH),
   11416             :                          errmsg("child table \"%s\" has different definition for check constraint \"%s\"",
   11417             :                                 RelationGetRelationName(child_rel),
   11418             :                                 NameStr(parent_con->conname))));
   11419             : 
   11420             :             /* If the child constraint is "no inherit" then cannot merge */
   11421           6 :             if (child_con->connoinherit)
   11422           0 :                 ereport(ERROR,
   11423             :                         (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
   11424             :                          errmsg("constraint \"%s\" conflicts with non-inherited constraint on child table \"%s\"",
   11425             :                                 NameStr(child_con->conname),
   11426             :                                 RelationGetRelationName(child_rel))));
   11427             : 
   11428             :             /*
   11429             :              * If the child constraint is "not valid" then cannot merge with a
   11430             :              * valid parent constraint
   11431             :              */
   11432           6 :             if (parent_con->convalidated && !child_con->convalidated)
   11433           0 :                 ereport(ERROR,
   11434             :                         (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
   11435             :                          errmsg("constraint \"%s\" conflicts with NOT VALID constraint on child table \"%s\"",
   11436             :                                 NameStr(child_con->conname),
   11437             :                                 RelationGetRelationName(child_rel))));
   11438             : 
   11439             :             /*
   11440             :              * OK, bump the child constraint's inheritance count.  (If we fail
   11441             :              * later on, this change will just roll back.)
   11442             :              */
   11443           6 :             child_copy = heap_copytuple(child_tuple);
   11444           6 :             child_con = (Form_pg_constraint) GETSTRUCT(child_copy);
   11445           6 :             child_con->coninhcount++;
   11446             : 
   11447             :             /*
   11448             :              * In case of partitions, an inherited constraint must be
   11449             :              * inherited only once since it cannot have multiple parents and
   11450             :              * it is never considered local.
   11451             :              */
   11452           6 :             if (child_is_partition)
   11453             :             {
   11454           4 :                 Assert(child_con->coninhcount == 1);
   11455           4 :                 child_con->conislocal = false;
   11456             :             }
   11457             : 
   11458           6 :             CatalogTupleUpdate(catalog_relation, &child_copy->t_self, child_copy);
   11459           6 :             heap_freetuple(child_copy);
   11460             : 
   11461           6 :             found = true;
   11462           6 :             break;
   11463             :         }
   11464             : 
   11465          10 :         systable_endscan(child_scan);
   11466             : 
   11467          10 :         if (!found)
   11468           4 :             ereport(ERROR,
   11469             :                     (errcode(ERRCODE_DATATYPE_MISMATCH),
   11470             :                      errmsg("child table is missing constraint \"%s\"",
   11471             :                             NameStr(parent_con->conname))));
   11472             :     }
   11473             : 
   11474          45 :     systable_endscan(parent_scan);
   11475          45 :     heap_close(catalog_relation, RowExclusiveLock);
   11476          45 : }
   11477             : 
   11478             : /*
   11479             :  * ALTER TABLE NO INHERIT
   11480             :  *
   11481             :  * Return value is the address of the relation that is no longer parent.
   11482             :  */
   11483             : static ObjectAddress
   11484           5 : ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode)
   11485             : {
   11486             :     ObjectAddress address;
   11487             :     Relation    parent_rel;
   11488             : 
   11489           5 :     if (rel->rd_rel->relispartition)
   11490           0 :         ereport(ERROR,
   11491             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   11492             :                  errmsg("cannot change inheritance of a partition")));
   11493             : 
   11494             :     /*
   11495             :      * AccessShareLock on the parent is probably enough, seeing that DROP
   11496             :      * TABLE doesn't lock parent tables at all.  We need some lock since we'll
   11497             :      * be inspecting the parent's schema.
   11498             :      */
   11499           5 :     parent_rel = heap_openrv(parent, AccessShareLock);
   11500             : 
   11501             :     /*
   11502             :      * We don't bother to check ownership of the parent table --- ownership of
   11503             :      * the child is presumed enough rights.
   11504             :      */
   11505             : 
   11506             :     /* Off to RemoveInheritance() where most of the work happens */
   11507           5 :     RemoveInheritance(rel, parent_rel);
   11508             : 
   11509             :     /* keep our lock on the parent relation until commit */
   11510           4 :     heap_close(parent_rel, NoLock);
   11511             : 
   11512           4 :     ObjectAddressSet(address, RelationRelationId,
   11513             :                      RelationGetRelid(parent_rel));
   11514             : 
   11515           4 :     return address;
   11516             : }
   11517             : 
   11518             : /*
   11519             :  * RemoveInheritance
   11520             :  *
   11521             :  * Drop a parent from the child's parents. This just adjusts the attinhcount
   11522             :  * and attislocal of the columns and removes the pg_inherit and pg_depend
   11523             :  * entries.
   11524             :  *
   11525             :  * If attinhcount goes to 0 then attislocal gets set to true. If it goes back
   11526             :  * up attislocal stays true, which means if a child is ever removed from a
   11527             :  * parent then its columns will never be automatically dropped which may
   11528             :  * surprise. But at least we'll never surprise by dropping columns someone
   11529             :  * isn't expecting to be dropped which would actually mean data loss.
   11530             :  *
   11531             :  * coninhcount and conislocal for inherited constraints are adjusted in
   11532             :  * exactly the same way.
   11533             :  *
   11534             :  * Common to ATExecDropInherit() and ATExecDetachPartition().
   11535             :  */
   11536             : static void
   11537          15 : RemoveInheritance(Relation child_rel, Relation parent_rel)
   11538             : {
   11539             :     Relation    catalogRelation;
   11540             :     SysScanDesc scan;
   11541             :     ScanKeyData key[3];
   11542             :     HeapTuple   inheritsTuple,
   11543             :                 attributeTuple,
   11544             :                 constraintTuple;
   11545             :     List       *connames;
   11546          15 :     bool        found = false;
   11547          15 :     bool        child_is_partition = false;
   11548             : 
   11549             :     /* If parent_rel is a partitioned table, child_rel must be a partition */
   11550          15 :     if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
   11551          10 :         child_is_partition = true;
   11552             : 
   11553             :     /*
   11554             :      * Find and destroy the pg_inherits entry linking the two, or error out if
   11555             :      * there is none.
   11556             :      */
   11557          15 :     catalogRelation = heap_open(InheritsRelationId, RowExclusiveLock);
   11558          15 :     ScanKeyInit(&key[0],
   11559             :                 Anum_pg_inherits_inhrelid,
   11560             :                 BTEqualStrategyNumber, F_OIDEQ,
   11561             :                 ObjectIdGetDatum(RelationGetRelid(child_rel)));
   11562          15 :     scan = systable_beginscan(catalogRelation, InheritsRelidSeqnoIndexId,
   11563             :                               true, NULL, 1, key);
   11564             : 
   11565          15 :     while (HeapTupleIsValid(inheritsTuple = systable_getnext(scan)))
   11566             :     {
   11567             :         Oid         inhparent;
   11568             : 
   11569          16 :         inhparent = ((Form_pg_inherits) GETSTRUCT(inheritsTuple))->inhparent;
   11570          16 :         if (inhparent == RelationGetRelid(parent_rel))
   11571             :         {
   11572          12 :             CatalogTupleDelete(catalogRelation, &inheritsTuple->t_self);
   11573          12 :             found = true;
   11574          12 :             break;
   11575             :         }
   11576             :     }
   11577             : 
   11578          15 :     systable_endscan(scan);
   11579          15 :     heap_close(catalogRelation, RowExclusiveLock);
   11580             : 
   11581          15 :     if (!found)
   11582             :     {
   11583           3 :         if (child_is_partition)
   11584           2 :             ereport(ERROR,
   11585             :                     (errcode(ERRCODE_UNDEFINED_TABLE),
   11586             :                      errmsg("relation \"%s\" is not a partition of relation \"%s\"",
   11587             :                             RelationGetRelationName(child_rel),
   11588             :                             RelationGetRelationName(parent_rel))));
   11589             :         else
   11590           1 :             ereport(ERROR,
   11591             :                     (errcode(ERRCODE_UNDEFINED_TABLE),
   11592             :                      errmsg("relation \"%s\" is not a parent of relation \"%s\"",
   11593             :                             RelationGetRelationName(parent_rel),
   11594             :                             RelationGetRelationName(child_rel))));
   11595             :     }
   11596             : 
   11597             :     /*
   11598             :      * Search through child columns looking for ones matching parent rel
   11599             :      */
   11600          12 :     catalogRelation = heap_open(AttributeRelationId, RowExclusiveLock);
   11601          12 :     ScanKeyInit(&key[0],
   11602             :                 Anum_pg_attribute_attrelid,
   11603             :                 BTEqualStrategyNumber, F_OIDEQ,
   11604             :                 ObjectIdGetDatum(RelationGetRelid(child_rel)));
   11605          12 :     scan = systable_beginscan(catalogRelation, AttributeRelidNumIndexId,
   11606             :                               true, NULL, 1, key);
   11607         123 :     while (HeapTupleIsValid(attributeTuple = systable_getnext(scan)))
   11608             :     {
   11609          99 :         Form_pg_attribute att = (Form_pg_attribute) GETSTRUCT(attributeTuple);
   11610             : 
   11611             :         /* Ignore if dropped or not inherited */
   11612          99 :         if (att->attisdropped)
   11613           0 :             continue;
   11614          99 :         if (att->attinhcount <= 0)
   11615          75 :             continue;
   11616             : 
   11617          24 :         if (SearchSysCacheExistsAttName(RelationGetRelid(parent_rel),
   11618          24 :                                         NameStr(att->attname)))
   11619             :         {
   11620             :             /* Decrement inhcount and possibly set islocal to true */
   11621          22 :             HeapTuple   copyTuple = heap_copytuple(attributeTuple);
   11622          22 :             Form_pg_attribute copy_att = (Form_pg_attribute) GETSTRUCT(copyTuple);
   11623             : 
   11624          22 :             copy_att->attinhcount--;
   11625          22 :             if (copy_att->attinhcount == 0)
   11626          22 :                 copy_att->attislocal = true;
   11627             : 
   11628          22 :             CatalogTupleUpdate(catalogRelation, &copyTuple->t_self, copyTuple);
   11629          22 :             heap_freetuple(copyTuple);
   11630             :         }
   11631             :     }
   11632          12 :     systable_endscan(scan);
   11633          12 :     heap_close(catalogRelation, RowExclusiveLock);
   11634             : 
   11635             :     /*
   11636             :      * Likewise, find inherited check constraints and disinherit them. To do
   11637             :      * this, we first need a list of the names of the parent's check
   11638             :      * constraints.  (We cheat a bit by only checking for name matches,
   11639             :      * assuming that the expressions will match.)
   11640             :      */
   11641          12 :     catalogRelation = heap_open(ConstraintRelationId, RowExclusiveLock);
   11642          12 :     ScanKeyInit(&key[0],
   11643             :                 Anum_pg_constraint_conrelid,
   11644             :                 BTEqualStrategyNumber, F_OIDEQ,
   11645             :                 ObjectIdGetDatum(RelationGetRelid(parent_rel)));
   11646          12 :     scan = systable_beginscan(catalogRelation, ConstraintRelidIndexId,
   11647             :                               true, NULL, 1, key);
   11648             : 
   11649          12 :     connames = NIL;
   11650             : 
   11651          26 :     while (HeapTupleIsValid(constraintTuple = systable_getnext(scan)))
   11652             :     {
   11653           2 :         Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(constraintTuple);
   11654             : 
   11655           2 :         if (con->contype == CONSTRAINT_CHECK)
   11656           2 :             connames = lappend(connames, pstrdup(NameStr(con->conname)));
   11657             :     }
   11658             : 
   11659          12 :     systable_endscan(scan);
   11660             : 
   11661             :     /* Now scan the child's constraints */
   11662          12 :     ScanKeyInit(&key[0],
   11663             :                 Anum_pg_constraint_conrelid,
   11664             :                 BTEqualStrategyNumber, F_OIDEQ,
   11665             :                 ObjectIdGetDatum(RelationGetRelid(child_rel)));
   11666          12 :     scan = systable_beginscan(catalogRelation, ConstraintRelidIndexId,
   11667             :                               true, NULL, 1, key);
   11668             : 
   11669          34 :     while (HeapTupleIsValid(constraintTuple = systable_getnext(scan)))
   11670             :     {
   11671          10 :         Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(constraintTuple);
   11672             :         bool        match;
   11673             :         ListCell   *lc;
   11674             : 
   11675          10 :         if (con->contype != CONSTRAINT_CHECK)
   11676           0 :             continue;
   11677             : 
   11678          10 :         match = false;
   11679          12 :         foreach(lc, connames)
   11680             :         {
   11681           4 :             if (strcmp(NameStr(con->conname), (char *) lfirst(lc)) == 0)
   11682             :             {
   11683           2 :                 match = true;
   11684           2 :                 break;
   11685             :             }
   11686             :         }
   11687             : 
   11688          10 :         if (match)
   11689             :         {
   11690             :             /* Decrement inhcount and possibly set islocal to true */
   11691           2 :             HeapTuple   copyTuple = heap_copytuple(constraintTuple);
   11692           2 :             Form_pg_constraint copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
   11693             : 
   11694           2 :             if (copy_con->coninhcount <= 0) /* shouldn't happen */
   11695           0 :                 elog(ERROR, "relation %u has non-inherited constraint \"%s\"",
   11696             :                      RelationGetRelid(child_rel), NameStr(copy_con->conname));
   11697             : 
   11698           2 :             copy_con->coninhcount--;
   11699           2 :             if (copy_con->coninhcount == 0)
   11700           2 :                 copy_con->conislocal = true;
   11701             : 
   11702           2 :             CatalogTupleUpdate(catalogRelation, &copyTuple->t_self, copyTuple);
   11703           2 :             heap_freetuple(copyTuple);
   11704             :         }
   11705             :     }
   11706             : 
   11707          12 :     systable_endscan(scan);
   11708          12 :     heap_close(catalogRelation, RowExclusiveLock);
   11709             : 
   11710          12 :     drop_parent_dependency(RelationGetRelid(child_rel),
   11711             :                            RelationRelationId,
   11712             :                            RelationGetRelid(parent_rel),
   11713             :                            child_dependency_type(child_is_partition));
   11714             : 
   11715             :     /*
   11716             :      * Post alter hook of this inherits. Since object_access_hook doesn't take
   11717             :      * multiple object identifiers, we relay oid of parent relation using
   11718             :      * auxiliary_id argument.
   11719             :      */
   11720          12 :     InvokeObjectPostAlterHookArg(InheritsRelationId,
   11721             :                                  RelationGetRelid(child_rel), 0,
   11722             :                                  RelationGetRelid(parent_rel), false);
   11723          12 : }
   11724             : 
   11725             : /*
   11726             :  * Drop the dependency created by StoreCatalogInheritance1 (CREATE TABLE
   11727             :  * INHERITS/ALTER TABLE INHERIT -- refclassid will be RelationRelationId) or
   11728             :  * heap_create_with_catalog (CREATE TABLE OF/ALTER TABLE OF -- refclassid will
   11729             :  * be TypeRelationId).  There's no convenient way to do this, so go trawling
   11730             :  * through pg_depend.
   11731             :  */
   11732             : static void
   11733          14 : drop_parent_dependency(Oid relid, Oid refclassid, Oid refobjid,
   11734             :                        DependencyType deptype)
   11735             : {
   11736             :     Relation    catalogRelation;
   11737             :     SysScanDesc scan;
   11738             :     ScanKeyData key[3];
   11739             :     HeapTuple   depTuple;
   11740             : 
   11741          14 :     catalogRelation = heap_open(DependRelationId, RowExclusiveLock);
   11742             : 
   11743          14 :     ScanKeyInit(&key[0],
   11744             :                 Anum_pg_depend_classid,
   11745             :                 BTEqualStrategyNumber, F_OIDEQ,
   11746             :                 ObjectIdGetDatum(RelationRelationId));
   11747          14 :     ScanKeyInit(&key[1],
   11748             :                 Anum_pg_depend_objid,
   11749             :                 BTEqualStrategyNumber, F_OIDEQ,
   11750             :                 ObjectIdGetDatum(relid));
   11751          14 :     ScanKeyInit(&key[2],
   11752             :                 Anum_pg_depend_objsubid,
   11753             :                 BTEqualStrategyNumber, F_INT4EQ,
   11754             :                 Int32GetDatum(0));
   11755             : 
   11756          14 :     scan = systable_beginscan(catalogRelation, DependDependerIndexId, true,
   11757             :                               NULL, 3, key);
   11758             : 
   11759          60 :     while (HeapTupleIsValid(depTuple = systable_getnext(scan)))
   11760             :     {
   11761          32 :         Form_pg_depend dep = (Form_pg_depend) GETSTRUCT(depTuple);
   11762             : 
   11763          48 :         if (dep->refclassid == refclassid &&
   11764          30 :             dep->refobjid == refobjid &&
   11765          28 :             dep->refobjsubid == 0 &&
   11766          14 :             dep->deptype == deptype)
   11767          14 :             CatalogTupleDelete(catalogRelation, &depTuple->t_self);
   11768             :     }
   11769             : 
   11770          14 :     systable_endscan(scan);
   11771          14 :     heap_close(catalogRelation, RowExclusiveLock);
   11772          14 : }
   11773             : 
   11774             : /*
   11775             :  * ALTER TABLE OF
   11776             :  *
   11777             :  * Attach a table to a composite type, as though it had been created with CREATE
   11778             :  * TABLE OF.  All attname, atttypid, atttypmod and attcollation must match.  The
   11779             :  * subject table must not have inheritance parents.  These restrictions ensure
   11780             :  * that you cannot create a configuration impossible with CREATE TABLE OF alone.
   11781             :  *
   11782             :  * The address of the type is returned.
   11783             :  */
   11784             : static ObjectAddress
   11785           9 : ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode)
   11786             : {
   11787           9 :     Oid         relid = RelationGetRelid(rel);
   11788             :     Type        typetuple;
   11789             :     Oid         typeid;
   11790             :     Relation    inheritsRelation,
   11791             :                 relationRelation;
   11792             :     SysScanDesc scan;
   11793             :     ScanKeyData key;
   11794             :     AttrNumber  table_attno,
   11795             :                 type_attno;
   11796             :     TupleDesc   typeTupleDesc,
   11797             :                 tableTupleDesc;
   11798             :     ObjectAddress tableobj,
   11799             :                 typeobj;
   11800             :     HeapTuple   classtuple;
   11801             : 
   11802             :     /* Validate the type. */
   11803           9 :     typetuple = typenameType(NULL, ofTypename, NULL);
   11804           9 :     check_of_type(typetuple);
   11805           9 :     typeid = HeapTupleGetOid(typetuple);
   11806             : 
   11807             :     /* Fail if the table has any inheritance parents. */
   11808           9 :     inheritsRelation = heap_open(InheritsRelationId, AccessShareLock);
   11809           9 :     ScanKeyInit(&key,
   11810             :                 Anum_pg_inherits_inhrelid,
   11811             :                 BTEqualStrategyNumber, F_OIDEQ,
   11812             :                 ObjectIdGetDatum(relid));
   11813           9 :     scan = systable_beginscan(inheritsRelation, InheritsRelidSeqnoIndexId,
   11814             :                               true, NULL, 1, &key);
   11815           9 :     if (HeapTupleIsValid(systable_getnext(scan)))
   11816           1 :         ereport(ERROR,
   11817             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   11818             :                  errmsg("typed tables cannot inherit")));
   11819           8 :     systable_endscan(scan);
   11820           8 :     heap_close(inheritsRelation, AccessShareLock);
   11821             : 
   11822             :     /*
   11823             :      * Check the tuple descriptors for compatibility.  Unlike inheritance, we
   11824             :      * require that the order also match.  However, attnotnull need not match.
   11825             :      * Also unlike inheritance, we do not require matching relhasoids.
   11826             :      */
   11827           8 :     typeTupleDesc = lookup_rowtype_tupdesc(typeid, -1);
   11828           8 :     tableTupleDesc = RelationGetDescr(rel);
   11829           8 :     table_attno = 1;
   11830          26 :     for (type_attno = 1; type_attno <= typeTupleDesc->natts; type_attno++)
   11831             :     {
   11832             :         Form_pg_attribute type_attr,
   11833             :                     table_attr;
   11834             :         const char *type_attname,
   11835             :                    *table_attname;
   11836             : 
   11837             :         /* Get the next non-dropped type attribute. */
   11838          22 :         type_attr = TupleDescAttr(typeTupleDesc, type_attno - 1);
   11839          22 :         if (type_attr->attisdropped)
   11840           7 :             continue;
   11841          15 :         type_attname = NameStr(type_attr->attname);
   11842             : 
   11843             :         /* Get the next non-dropped table attribute. */
   11844             :         do
   11845             :         {
   11846          17 :             if (table_attno > tableTupleDesc->natts)
   11847           1 :                 ereport(ERROR,
   11848             :                         (errcode(ERRCODE_DATATYPE_MISMATCH),
   11849             :                          errmsg("table is missing column \"%s\"",
   11850             :                                 type_attname)));
   11851          16 :             table_attr = TupleDescAttr(tableTupleDesc, table_attno - 1);
   11852          16 :             table_attno++;
   11853          16 :         } while (table_attr->attisdropped);
   11854          14 :         table_attname = NameStr(table_attr->attname);
   11855             : 
   11856             :         /* Compare name. */
   11857          14 :         if (strncmp(table_attname, type_attname, NAMEDATALEN) != 0)
   11858           1 :             ereport(ERROR,
   11859             :                     (errcode(ERRCODE_DATATYPE_MISMATCH),
   11860             :                      errmsg("table has column \"%s\" where type requires \"%s\"",
   11861             :                             table_attname, type_attname)));
   11862             : 
   11863             :         /* Compare type. */
   11864          25 :         if (table_attr->atttypid != type_attr->atttypid ||
   11865          23 :             table_attr->atttypmod != type_attr->atttypmod ||
   11866          11 :             table_attr->attcollation != type_attr->attcollation)
   11867           2 :             ereport(ERROR,
   11868             :                     (errcode(ERRCODE_DATATYPE_MISMATCH),
   11869             :                      errmsg("table \"%s\" has different type for column \"%s\"",
   11870             :                             RelationGetRelationName(rel), type_attname)));
   11871             :     }
   11872           4 :     DecrTupleDescRefCount(typeTupleDesc);
   11873             : 
   11874             :     /* Any remaining columns at the end of the table had better be dropped. */
   11875           4 :     for (; table_attno <= tableTupleDesc->natts; table_attno++)
   11876             :     {
   11877           1 :         Form_pg_attribute table_attr = TupleDescAttr(tableTupleDesc,
   11878             :                                                      table_attno - 1);
   11879             : 
   11880           1 :         if (!table_attr->attisdropped)
   11881           1 :             ereport(ERROR,
   11882             :                     (errcode(ERRCODE_DATATYPE_MISMATCH),
   11883             :                      errmsg("table has extra column \"%s\"",
   11884             :                             NameStr(table_attr->attname))));
   11885             :     }
   11886             : 
   11887             :     /* If the table was already typed, drop the existing dependency. */
   11888           3 :     if (rel->rd_rel->reloftype)
   11889           1 :         drop_parent_dependency(relid, TypeRelationId, rel->rd_rel->reloftype,
   11890             :                                DEPENDENCY_NORMAL);
   11891             : 
   11892             :     /* Record a dependency on the new type. */
   11893           3 :     tableobj.classId = RelationRelationId;
   11894           3 :     tableobj.objectId = relid;
   11895           3 :     tableobj.objectSubId = 0;
   11896           3 :     typeobj.classId = TypeRelationId;
   11897           3 :     typeobj.objectId = typeid;
   11898           3 :     typeobj.objectSubId = 0;
   11899           3 :     recordDependencyOn(&tableobj, &typeobj, DEPENDENCY_NORMAL);
   11900             : 
   11901             :     /* Update pg_class.reloftype */
   11902           3 :     relationRelation = heap_open(RelationRelationId, RowExclusiveLock);
   11903           3 :     classtuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
   11904           3 :     if (!HeapTupleIsValid(classtuple))
   11905           0 :         elog(ERROR, "cache lookup failed for relation %u", relid);
   11906           3 :     ((Form_pg_class) GETSTRUCT(classtuple))->reloftype = typeid;
   11907           3 :     CatalogTupleUpdate(relationRelation, &classtuple->t_self, classtuple);
   11908             : 
   11909           3 :     InvokeObjectPostAlterHook(RelationRelationId, relid, 0);
   11910             : 
   11911           3 :     heap_freetuple(classtuple);
   11912           3 :     heap_close(relationRelation, RowExclusiveLock);
   11913             : 
   11914           3 :     ReleaseSysCache(typetuple);
   11915             : 
   11916           3 :     return typeobj;
   11917             : }
   11918             : 
   11919             : /*
   11920             :  * ALTER TABLE NOT OF
   11921             :  *
   11922             :  * Detach a typed table from its originating type.  Just clear reloftype and
   11923             :  * remove the dependency.
   11924             :  */
   11925             : static void
   11926           1 : ATExecDropOf(Relation rel, LOCKMODE lockmode)
   11927             : {
   11928           1 :     Oid         relid = RelationGetRelid(rel);
   11929             :     Relation    relationRelation;
   11930             :     HeapTuple   tuple;
   11931             : 
   11932           1 :     if (!OidIsValid(rel->rd_rel->reloftype))
   11933           0 :         ereport(ERROR,
   11934             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   11935             :                  errmsg("\"%s\" is not a typed table",
   11936             :                         RelationGetRelationName(rel))));
   11937             : 
   11938             :     /*
   11939             :      * We don't bother to check ownership of the type --- ownership of the
   11940             :      * table is presumed enough rights.  No lock required on the type, either.
   11941             :      */
   11942             : 
   11943           1 :     drop_parent_dependency(relid, TypeRelationId, rel->rd_rel->reloftype,
   11944             :                            DEPENDENCY_NORMAL);
   11945             : 
   11946             :     /* Clear pg_class.reloftype */
   11947           1 :     relationRelation = heap_open(RelationRelationId, RowExclusiveLock);
   11948           1 :     tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
   11949           1 :     if (!HeapTupleIsValid(tuple))
   11950           0 :         elog(ERROR, "cache lookup failed for relation %u", relid);
   11951           1 :     ((Form_pg_class) GETSTRUCT(tuple))->reloftype = InvalidOid;
   11952           1 :     CatalogTupleUpdate(relationRelation, &tuple->t_self, tuple);
   11953             : 
   11954           1 :     InvokeObjectPostAlterHook(RelationRelationId, relid, 0);
   11955             : 
   11956           1 :     heap_freetuple(tuple);
   11957           1 :     heap_close(relationRelation, RowExclusiveLock);
   11958           1 : }
   11959             : 
   11960             : /*
   11961             :  * relation_mark_replica_identity: Update a table's replica identity
   11962             :  *
   11963             :  * Iff ri_type = REPLICA_IDENTITY_INDEX, indexOid must be the Oid of a suitable
   11964             :  * index. Otherwise, it should be InvalidOid.
   11965             :  */
   11966             : static void
   11967           8 : relation_mark_replica_identity(Relation rel, char ri_type, Oid indexOid,
   11968             :                                bool is_internal)
   11969             : {
   11970             :     Relation    pg_index;
   11971             :     Relation    pg_class;
   11972             :     HeapTuple   pg_class_tuple;
   11973             :     HeapTuple   pg_index_tuple;
   11974             :     Form_pg_class pg_class_form;
   11975             :     Form_pg_index pg_index_form;
   11976             : 
   11977             :     ListCell   *index;
   11978             : 
   11979             :     /*
   11980             :      * Check whether relreplident has changed, and update it if so.
   11981             :      */
   11982           8 :     pg_class = heap_open(RelationRelationId, RowExclusiveLock);
   11983           8 :     pg_class_tuple = SearchSysCacheCopy1(RELOID,
   11984             :                                          ObjectIdGetDatum(RelationGetRelid(rel)));
   11985           8 :     if (!HeapTupleIsValid(pg_class_tuple))
   11986           0 :         elog(ERROR, "cache lookup failed for relation \"%s\"",
   11987             :              RelationGetRelationName(rel));
   11988           8 :     pg_class_form = (Form_pg_class) GETSTRUCT(pg_class_tuple);
   11989           8 :     if (pg_class_form->relreplident != ri_type)
   11990             :     {
   11991           4 :         pg_class_form->relreplident = ri_type;
   11992           4 :         CatalogTupleUpdate(pg_class, &pg_class_tuple->t_self, pg_class_tuple);
   11993             :     }
   11994           8 :     heap_close(pg_class, RowExclusiveLock);
   11995           8 :     heap_freetuple(pg_class_tuple);
   11996             : 
   11997             :     /*
   11998             :      * Check whether the correct index is marked indisreplident; if so, we're
   11999             :      * done.
   12000             :      */
   12001           8 :     if (OidIsValid(indexOid))
   12002             :     {
   12003           5 :         Assert(ri_type == REPLICA_IDENTITY_INDEX);
   12004             : 
   12005           5 :         pg_index_tuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexOid));
   12006           5 :         if (!HeapTupleIsValid(pg_index_tuple))
   12007           0 :             elog(ERROR, "cache lookup failed for index %u", indexOid);
   12008           5 :         pg_index_form = (Form_pg_index) GETSTRUCT(pg_index_tuple);
   12009             : 
   12010           5 :         if (pg_index_form->indisreplident)
   12011             :         {
   12012           1 :             ReleaseSysCache(pg_index_tuple);
   12013           9 :             return;
   12014             :         }
   12015           4 :         ReleaseSysCache(pg_index_tuple);
   12016             :     }
   12017             : 
   12018             :     /*
   12019             :      * Clear the indisreplident flag from any index that had it previously,
   12020             :      * and set it for any index that should have it now.
   12021             :      */
   12022           7 :     pg_index = heap_open(IndexRelationId, RowExclusiveLock);
   12023          77 :     foreach(index, RelationGetIndexList(rel))
   12024             :     {
   12025          70 :         Oid         thisIndexOid = lfirst_oid(index);
   12026          70 :         bool        dirty = false;
   12027             : 
   12028          70 :         pg_index_tuple = SearchSysCacheCopy1(INDEXRELID,
   12029             :                                              ObjectIdGetDatum(thisIndexOid));
   12030          70 :         if (!HeapTupleIsValid(pg_index_tuple))
   12031           0 :             elog(ERROR, "cache lookup failed for index %u", thisIndexOid);
   12032          70 :         pg_index_form = (Form_pg_index) GETSTRUCT(pg_index_tuple);
   12033             : 
   12034             :         /*
   12035             :          * Unset the bit if set.  We know it's wrong because we checked this
   12036             :          * earlier.
   12037             :          */
   12038          70 :         if (pg_index_form->indisreplident)
   12039             :         {
   12040           4 :             dirty = true;
   12041           4 :             pg_index_form->indisreplident = false;
   12042             :         }
   12043          66 :         else if (thisIndexOid == indexOid)
   12044             :         {
   12045           4 :             dirty = true;
   12046           4 :             pg_index_form->indisreplident = true;
   12047             :         }
   12048             : 
   12049          70 :         if (dirty)
   12050             :         {
   12051           8 :             CatalogTupleUpdate(pg_index, &pg_index_tuple->t_self, pg_index_tuple);
   12052           8 :             InvokeObjectPostAlterHookArg(IndexRelationId, thisIndexOid, 0,
   12053             :                                          InvalidOid, is_internal);
   12054             :         }
   12055          70 :         heap_freetuple(pg_index_tuple);
   12056             :     }
   12057             : 
   12058           7 :     heap_close(pg_index, RowExclusiveLock);
   12059             : }
   12060             : 
   12061             : /*
   12062             :  * ALTER TABLE <name> REPLICA IDENTITY ...
   12063             :  */
   12064             : static void
   12065          15 : ATExecReplicaIdentity(Relation rel, ReplicaIdentityStmt *stmt, LOCKMODE lockmode)
   12066             : {
   12067             :     Oid         indexOid;
   12068             :     Relation    indexRel;
   12069             :     int         key;
   12070             : 
   12071          15 :     if (stmt->identity_type == REPLICA_IDENTITY_DEFAULT)
   12072             :     {
   12073           1 :         relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
   12074           1 :         return;
   12075             :     }
   12076          14 :     else if (stmt->identity_type == REPLICA_IDENTITY_FULL)
   12077             :     {
   12078           1 :         relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
   12079           1 :         return;
   12080             :     }
   12081          13 :     else if (stmt->identity_type == REPLICA_IDENTITY_NOTHING)
   12082             :     {
   12083           1 :         relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
   12084           1 :         return;
   12085             :     }
   12086          12 :     else if (stmt->identity_type == REPLICA_IDENTITY_INDEX)
   12087             :     {
   12088             :          /* fallthrough */ ;
   12089             :     }
   12090             :     else
   12091           0 :         elog(ERROR, "unexpected identity type %u", stmt->identity_type);
   12092             : 
   12093             : 
   12094             :     /* Check that the index exists */
   12095          12 :     indexOid = get_relname_relid(stmt->name, rel->rd_rel->relnamespace);
   12096          12 :     if (!OidIsValid(indexOid))
   12097           0 :         ereport(ERROR,
   12098             :                 (errcode(ERRCODE_UNDEFINED_OBJECT),
   12099             :                  errmsg("index \"%s\" for table \"%s\" does not exist",
   12100             :                         stmt->name, RelationGetRelationName(rel))));
   12101             : 
   12102          12 :     indexRel = index_open(indexOid, ShareLock);
   12103             : 
   12104             :     /* Check that the index is on the relation we're altering. */
   12105          24 :     if (indexRel->rd_index == NULL ||
   12106          12 :         indexRel->rd_index->indrelid != RelationGetRelid(rel))
   12107           1 :         ereport(ERROR,
   12108             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   12109             :                  errmsg("\"%s\" is not an index for table \"%s\"",
   12110             :                         RelationGetRelationName(indexRel),
   12111             :                         RelationGetRelationName(rel))));
   12112             :     /* The AM must support uniqueness, and the index must in fact be unique. */
   12113          21 :     if (!indexRel->rd_amroutine->amcanunique ||
   12114          10 :         !indexRel->rd_index->indisunique)
   12115           2 :         ereport(ERROR,
   12116             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   12117             :                  errmsg("cannot use non-unique index \"%s\" as replica identity",
   12118             :                         RelationGetRelationName(indexRel))));
   12119             :     /* Deferred indexes are not guaranteed to be always unique. */
   12120           9 :     if (!indexRel->rd_index->indimmediate)
   12121           1 :         ereport(ERROR,
   12122             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   12123             :                  errmsg("cannot use non-immediate index \"%s\" as replica identity",
   12124             :                         RelationGetRelationName(indexRel))));
   12125             :     /* Expression indexes aren't supported. */
   12126           8 :     if (RelationGetIndexExpressions(indexRel) != NIL)
   12127           1 :         ereport(ERROR,
   12128             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   12129             :                  errmsg("cannot use expression index \"%s\" as replica identity",
   12130             :                         RelationGetRelationName(indexRel))));
   12131             :     /* Predicate indexes aren't supported. */
   12132           7 :     if (RelationGetIndexPredicate(indexRel) != NIL)
   12133           1 :         ereport(ERROR,
   12134             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   12135             :                  errmsg("cannot use partial index \"%s\" as replica identity",
   12136             :                         RelationGetRelationName(indexRel))));
   12137             :     /* And neither are invalid indexes. */
   12138           6 :     if (!IndexIsValid(indexRel->rd_index))
   12139           0 :         ereport(ERROR,
   12140             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   12141             :                  errmsg("cannot use invalid index \"%s\" as replica identity",
   12142             :                         RelationGetRelationName(indexRel))));
   12143             : 
   12144             :     /* Check index for nullable columns. */
   12145          15 :     for (key = 0; key < indexRel->rd_index->indnatts; key++)
   12146             :     {
   12147          10 :         int16       attno = indexRel->rd_index->indkey.values[key];
   12148             :         Form_pg_attribute attr;
   12149             : 
   12150             :         /* Allow OID column to be indexed; it's certainly not nullable */
   12151          10 :         if (attno == ObjectIdAttributeNumber)
   12152           1 :             continue;
   12153             : 
   12154             :         /*
   12155             :          * Reject any other system columns.  (Going forward, we'll disallow
   12156             :          * indexes containing such columns in the first place, but they might
   12157             :          * exist in older branches.)
   12158             :          */
   12159           9 :         if (attno <= 0)
   12160           0 :             ereport(ERROR,
   12161             :                     (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
   12162             :                      errmsg("index \"%s\" cannot be used as replica identity because column %d is a system column",
   12163             :                             RelationGetRelationName(indexRel), attno)));
   12164             : 
   12165           9 :         attr = TupleDescAttr(rel->rd_att, attno - 1);
   12166           9 :         if (!attr->attnotnull)
   12167           1 :             ereport(ERROR,
   12168             :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   12169             :                      errmsg("index \"%s\" cannot be used as replica identity because column \"%s\" is nullable",
   12170             :                             RelationGetRelationName(indexRel),
   12171             :                             NameStr(attr->attname))));
   12172             :     }
   12173             : 
   12174             :     /* This index is suitable for use as a replica identity. Mark it. */
   12175           5 :     relation_mark_replica_identity(rel, stmt->identity_type, indexOid, true);
   12176             : 
   12177           5 :     index_close(indexRel, NoLock);
   12178             : }
   12179             : 
   12180             : /*
   12181             :  * ALTER TABLE ENABLE/DISABLE ROW LEVEL SECURITY
   12182             :  */
   12183             : static void
   12184          37 : ATExecEnableRowSecurity(Relation rel)
   12185             : {
   12186             :     Relation    pg_class;
   12187             :     Oid         relid;
   12188             :     HeapTuple   tuple;
   12189             : 
   12190          37 :     relid = RelationGetRelid(rel);
   12191             : 
   12192          37 :     pg_class = heap_open(RelationRelationId, RowExclusiveLock);
   12193             : 
   12194          37 :     tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
   12195             : 
   12196          37 :     if (!HeapTupleIsValid(tuple))
   12197           0 :         elog(ERROR, "cache lookup failed for relation %u", relid);
   12198             : 
   12199          37 :     ((Form_pg_class) GETSTRUCT(tuple))->relrowsecurity = true;
   12200          37 :     CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
   12201             : 
   12202          37 :     heap_close(pg_class, RowExclusiveLock);
   12203          37 :     heap_freetuple(tuple);
   12204          37 : }
   12205             : 
   12206             : static void
   12207           2 : ATExecDisableRowSecurity(Relation rel)
   12208             : {
   12209             :     Relation    pg_class;
   12210             :     Oid         relid;
   12211             :     HeapTuple   tuple;
   12212             : 
   12213           2 :     relid = RelationGetRelid(rel);
   12214             : 
   12215             :     /* Pull the record for this relation and update it */
   12216           2 :     pg_class = heap_open(RelationRelationId, RowExclusiveLock);
   12217             : 
   12218           2 :     tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
   12219             : 
   12220           2 :     if (!HeapTupleIsValid(tuple))
   12221           0 :         elog(ERROR, "cache lookup failed for relation %u", relid);
   12222             : 
   12223           2 :     ((Form_pg_class) GETSTRUCT(tuple))->relrowsecurity = false;
   12224           2 :     CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
   12225             : 
   12226           2 :     heap_close(pg_class, RowExclusiveLock);
   12227           2 :     heap_freetuple(tuple);
   12228           2 : }
   12229             : 
   12230             : /*
   12231             :  * ALTER TABLE FORCE/NO FORCE ROW LEVEL SECURITY
   12232             :  */
   12233             : static void
   12234          14 : ATExecForceNoForceRowSecurity(Relation rel, bool force_rls)
   12235             : {
   12236             :     Relation    pg_class;
   12237             :     Oid         relid;
   12238             :     HeapTuple   tuple;
   12239             : 
   12240          14 :     relid = RelationGetRelid(rel);
   12241             : 
   12242          14 :     pg_class = heap_open(RelationRelationId, RowExclusiveLock);
   12243             : 
   12244          14 :     tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
   12245             : 
   12246          14 :     if (!HeapTupleIsValid(tuple))
   12247           0 :         elog(ERROR, "cache lookup failed for relation %u", relid);
   12248             : 
   12249          14 :     ((Form_pg_class) GETSTRUCT(tuple))->relforcerowsecurity = force_rls;
   12250          14 :     CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
   12251             : 
   12252          14 :     heap_close(pg_class, RowExclusiveLock);
   12253          14 :     heap_freetuple(tuple);
   12254          14 : }
   12255             : 
   12256             : /*
   12257             :  * ALTER FOREIGN TABLE <name> OPTIONS (...)
   12258             :  */
   12259             : static void
   12260           1 : ATExecGenericOptions(Relation rel, List *options)
   12261             : {
   12262             :     Relation    ftrel;
   12263             :     ForeignServer *server;
   12264             :     ForeignDataWrapper *fdw;
   12265             :     HeapTuple   tuple;
   12266             :     bool        isnull;
   12267             :     Datum       repl_val[Natts_pg_foreign_table];
   12268             :     bool        repl_null[Natts_pg_foreign_table];
   12269             :     bool        repl_repl[Natts_pg_foreign_table];
   12270             :     Datum       datum;
   12271             :     Form_pg_foreign_table tableform;
   12272             : 
   12273           1 :     if (options == NIL)
   12274           1 :         return;
   12275             : 
   12276           1 :     ftrel = heap_open(ForeignTableRelationId, RowExclusiveLock);
   12277             : 
   12278           1 :     tuple = SearchSysCacheCopy1(FOREIGNTABLEREL, rel->rd_id);
   12279           1 :     if (!HeapTupleIsValid(tuple))
   12280           0 :         ereport(ERROR,
   12281             :                 (errcode(ERRCODE_UNDEFINED_OBJECT),
   12282             :                  errmsg("foreign table \"%s\" does not exist",
   12283             :                         RelationGetRelationName(rel))));
   12284           1 :     tableform = (Form_pg_foreign_table) GETSTRUCT(tuple);
   12285           1 :     server = GetForeignServer(tableform->ftserver);
   12286           1 :     fdw = GetForeignDataWrapper(server->fdwid);
   12287             : 
   12288           1 :     memset(repl_val, 0, sizeof(repl_val));
   12289           1 :     memset(repl_null, false, sizeof(repl_null));
   12290           1 :     memset(repl_repl, false, sizeof(repl_repl));
   12291             : 
   12292             :     /* Extract the current options */
   12293           1 :     datum = SysCacheGetAttr(FOREIGNTABLEREL,
   12294             :                             tuple,
   12295             :                             Anum_pg_foreign_table_ftoptions,
   12296             :                             &isnull);
   12297           1 :     if (isnull)
   12298           0 :         datum = PointerGetDatum(NULL);
   12299             : 
   12300             :     /* Transform the options */
   12301           1 :     datum = transformGenericOptions(ForeignTableRelationId,
   12302             :                                     datum,
   12303             :                                     options,
   12304             :                                     fdw->fdwvalidator);
   12305             : 
   12306           1 :     if (PointerIsValid(DatumGetPointer(datum)))
   12307           1 :         repl_val[Anum_pg_foreign_table_ftoptions - 1] = datum;
   12308             :     else
   12309           0 :         repl_null[Anum_pg_foreign_table_ftoptions - 1] = true;
   12310             : 
   12311           1 :     repl_repl[Anum_pg_foreign_table_ftoptions - 1] = true;
   12312             : 
   12313             :     /* Everything looks good - update the tuple */
   12314             : 
   12315           1 :     tuple = heap_modify_tuple(tuple, RelationGetDescr(ftrel),
   12316             :                               repl_val, repl_null, repl_repl);
   12317             : 
   12318           1 :     CatalogTupleUpdate(ftrel, &tuple->t_self, tuple);
   12319             : 
   12320             :     /*
   12321             :      * Invalidate relcache so that all sessions will refresh any cached plans
   12322             :      * that might depend on the old options.
   12323             :      */
   12324           1 :     CacheInvalidateRelcache(rel);
   12325             : 
   12326           1 :     InvokeObjectPostAlterHook(ForeignTableRelationId,
   12327             :                               RelationGetRelid(rel), 0);
   12328             : 
   12329           1 :     heap_close(ftrel, RowExclusiveLock);
   12330             : 
   12331           1 :     heap_freetuple(tuple);
   12332             : }
   12333             : 
   12334             : /*
   12335             :  * Preparation phase for SET LOGGED/UNLOGGED
   12336             :  *
   12337             :  * This verifies that we're not trying to change a temp table.  Also,
   12338             :  * existing foreign key constraints are checked to avoid ending up with
   12339             :  * permanent tables referencing unlogged tables.
   12340             :  *
   12341             :  * Return value is false if the operation is a no-op (in which case the
   12342             :  * checks are skipped), otherwise true.
   12343             :  */
   12344             : static bool
   12345           9 : ATPrepChangePersistence(Relation rel, bool toLogged)
   12346             : {
   12347             :     Relation    pg_constraint;
   12348             :     HeapTuple   tuple;
   12349             :     SysScanDesc scan;
   12350             :     ScanKeyData skey[1];
   12351             : 
   12352             :     /*
   12353             :      * Disallow changing status for a temp table.  Also verify whether we can
   12354             :      * get away with doing nothing; in such cases we don't need to run the
   12355             :      * checks below, either.
   12356             :      */
   12357           9 :     switch (rel->rd_rel->relpersistence)
   12358             :     {
   12359             :         case RELPERSISTENCE_TEMP:
   12360           0 :             ereport(ERROR,
   12361             :                     (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
   12362             :                      errmsg("cannot change logged status of table \"%s\" because it is temporary",
   12363             :                             RelationGetRelationName(rel)),
   12364             :                      errtable(rel)));
   12365             :             break;
   12366             :         case RELPERSISTENCE_PERMANENT:
   12367           5 :             if (toLogged)
   12368             :                 /* nothing to do */
   12369           1 :                 return false;
   12370           4 :             break;
   12371             :         case RELPERSISTENCE_UNLOGGED:
   12372           4 :             if (!toLogged)
   12373             :                 /* nothing to do */
   12374           1 :                 return false;
   12375           3 :             break;
   12376             :     }
   12377             : 
   12378             :     /*
   12379             :      * Check that the table is not part any publication when changing to
   12380             :      * UNLOGGED as UNLOGGED tables can't be published.
   12381             :      */
   12382          11 :     if (!toLogged &&
   12383           4 :         list_length(GetRelationPublications(RelationGetRelid(rel))) > 0)
   12384           0 :         ereport(ERROR,
   12385             :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
   12386             :                  errmsg("cannot change table \"%s\" to unlogged because it is part of a publication",
   12387             :                         RelationGetRelationName(rel)),
   12388             :                  errdetail("Unlogged relations cannot be replicated.")));
   12389             : 
   12390             :     /*
   12391             :      * Check existing foreign key constraints to preserve the invariant that
   12392             :      * permanent tables cannot reference unlogged ones.  Self-referencing
   12393             :      * foreign keys can safely be ignored.
   12394             :      */
   12395           7 :     pg_constraint = heap_open(ConstraintRelationId, AccessShareLock);
   12396             : 
   12397             :     /*
   12398             :      * Scan conrelid if changing to permanent, else confrelid.  This also
   12399             :      * determines whether a useful index exists.
   12400             :      */
   12401           7 :     ScanKeyInit(&skey[0],
   12402             :                 toLogged ? Anum_pg_constraint_conrelid :
   12403             :                 Anum_pg_constraint_confrelid,
   12404             :                 BTEqualStrategyNumber, F_OIDEQ,
   12405             :                 ObjectIdGetDatum(RelationGetRelid(rel)));
   12406           7 :     scan = systable_beginscan(pg_constraint,
   12407             :                               toLogged ? ConstraintRelidIndexId : InvalidOid,
   12408             :                               true, NULL, 1, skey);
   12409             : 
   12410          19 :     while (HeapTupleIsValid(tuple = systable_getnext(scan)))
   12411             :     {
   12412           7 :         Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tuple);
   12413             : 
   12414           7 :         if (con->contype == CONSTRAINT_FOREIGN)
   12415             :         {
   12416             :             Oid         foreignrelid;
   12417             :             Relation    foreignrel;
   12418             : 
   12419             :             /* the opposite end of what we used as scankey */
   12420           5 :             foreignrelid = toLogged ? con->confrelid : con->conrelid;
   12421             : 
   12422             :             /* ignore if self-referencing */
   12423           5 :             if (RelationGetRelid(rel) == foreignrelid)
   12424           2 :                 continue;
   12425             : 
   12426           3 :             foreignrel = relation_open(foreignrelid, AccessShareLock);
   12427             : 
   12428           3 :             if (toLogged)
   12429             :             {
   12430           1 :                 if (foreignrel->rd_rel->relpersistence != RELPERSISTENCE_PERMANENT)
   12431           1 :                     ereport(ERROR,
   12432             :                             (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
   12433             :                              errmsg("could not change table \"%s\" to logged because it references unlogged table \"%s\"",
   12434             :                                     RelationGetRelationName(rel),
   12435             :                                     RelationGetRelationName(foreignrel)),
   12436             :                              errtableconstraint(rel, NameStr(con->conname))));
   12437             :             }
   12438             :             else
   12439             :             {
   12440           2 :                 if (foreignrel->rd_rel->relpersistence == RELPERSISTENCE_PERMANENT)
   12441           1 :                     ereport(ERROR,
   12442             :                             (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
   12443             :                              errmsg("could not change table \"%s\" to unlogged because it references logged table \"%s\"",
   12444             :                                     RelationGetRelationName(rel),
   12445             :                                     RelationGetRelationName(foreignrel)),
   12446             :                              errtableconstraint(rel, NameStr(con->conname))));
   12447             :             }
   12448             : 
   12449           1 :             relation_close(foreignrel, AccessShareLock);
   12450             :         }
   12451             :     }
   12452             : 
   12453           5 :     systable_endscan(scan);
   12454             : 
   12455           5 :     heap_close(pg_constraint, AccessShareLock);
   12456             : 
   12457           5 :     return true;
   12458             : }
   12459             : 
   12460             : /*
   12461             :  * Execute ALTER TABLE SET SCHEMA
   12462             :  */
   12463             : ObjectAddress
   12464          13 : AlterTableNamespace(AlterObjectSchemaStmt *stmt, Oid *oldschema)
   12465             : {
   12466             :     Relation    rel;
   12467             :     Oid         relid;
   12468             :     Oid         oldNspOid;
   12469             :     Oid         nspOid;
   12470             :     RangeVar   *newrv;
   12471             :     ObjectAddresses *objsMoved;
   12472             :     ObjectAddress myself;
   12473             : 
   12474          13 :     relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
   12475          13 :                                      stmt->missing_ok, false,
   12476             :                                      RangeVarCallbackForAlterRelation,
   12477             :                                      (void *) stmt);
   12478             : 
   12479          13 :     if (!OidIsValid(relid))
   12480             :     {
   12481           2 :         ereport(NOTICE,
   12482             :                 (errmsg("relation \"%s\" does not exist, skipping",
   12483             :                         stmt->relation->relname)));
   12484           2 :         return InvalidObjectAddress;
   12485             :     }
   12486             : 
   12487          11 :     rel = relation_open(relid, NoLock);
   12488             : 
   12489          11 :     oldNspOid = RelationGetNamespace(rel);
   12490             : 
   12491             :     /* If it's an owned sequence, disallow moving it by itself. */
   12492          11 :     if (rel->rd_rel->relkind == RELKIND_SEQUENCE)
   12493             :     {
   12494             :         Oid         tableId;
   12495             :         int32       colId;
   12496             : 
   12497           0 :         if (sequenceIsOwned(relid, DEPENDENCY_AUTO, &tableId, &colId) ||
   12498           0 :             sequenceIsOwned(relid, DEPENDENCY_INTERNAL, &tableId, &colId))
   12499           0 :             ereport(ERROR,
   12500             :                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   12501             :                      errmsg("cannot move an owned sequence into another schema"),
   12502             :                      errdetail("Sequence \"%s\" is linked to table \"%s\".",
   12503             :                                RelationGetRelationName(rel),
   12504             :                                get_rel_name(tableId))));
   12505             :     }
   12506             : 
   12507             :     /* Get and lock schema OID and check its permissions. */
   12508          11 :     newrv = makeRangeVar(stmt->newschema, RelationGetRelationName(rel), -1);
   12509          11 :     nspOid = RangeVarGetAndCheckCreationNamespace(newrv, NoLock, NULL);
   12510             : 
   12511             :     /* common checks on switching namespaces */
   12512          11 :     CheckSetNamespace(oldNspOid, nspOid);
   12513             : 
   12514          11 :     objsMoved = new_object_addresses();
   12515          11 :     AlterTableNamespaceInternal(rel, oldNspOid, nspOid, objsMoved);
   12516          10 :     free_object_addresses(objsMoved);
   12517             : 
   12518          10 :     ObjectAddressSet(myself, RelationRelationId, relid);
   12519             : 
   12520          10 :     if (oldschema)
   12521          10 :         *oldschema = oldNspOid;
   12522             : 
   12523             :     /* close rel, but keep lock until commit */
   12524          10 :     relation_close(rel, NoLock);
   12525             : 
   12526          10 :     return myself;
   12527             : }
   12528             : 
   12529             : /*
   12530             :  * The guts of relocating a table or materialized view to another namespace:
   12531             :  * besides moving the relation itself, its dependent objects are relocated to
   12532             :  * the new schema.
   12533             :  */
   12534             : void
   12535          11 : AlterTableNamespaceInternal(Relation rel, Oid oldNspOid, Oid nspOid,
   12536             :                             ObjectAddresses *objsMoved)
   12537             : {
   12538             :     Relation    classRel;
   12539             : 
   12540          11 :     Assert(objsMoved != NULL);
   12541             : 
   12542             :     /* OK, modify the pg_class row and pg_depend entry */
   12543          11 :     classRel = heap_open(RelationRelationId, RowExclusiveLock);
   12544             : 
   12545          11 :     AlterRelationNamespaceInternal(classRel, RelationGetRelid(rel), oldNspOid,
   12546             :                                    nspOid, true, objsMoved);
   12547             : 
   12548             :     /* Fix the table's row type too */
   12549          10 :     AlterTypeNamespaceInternal(rel->rd_rel->reltype,
   12550             :                                nspOid, false, false, objsMoved);
   12551             : 
   12552             :     /* Fix other dependent stuff */
   12553          13 :     if (rel->rd_rel->relkind == RELKIND_RELATION ||
   12554           5 :         rel->rd_rel->relkind == RELKIND_MATVIEW ||
   12555           2 :         rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
   12556             :     {
   12557           8 :         AlterIndexNamespaces(classRel, rel, oldNspOid, nspOid, objsMoved);
   12558           8 :         AlterSeqNamespaces(classRel, rel, oldNspOid, nspOid,
   12559             :                            objsMoved, AccessExclusiveLock);
   12560           8 :         AlterConstraintNamespaces(RelationGetRelid(rel), oldNspOid, nspOid,
   12561             :                                   false, objsMoved);
   12562             :     }
   12563             : 
   12564          10 :     heap_close(classRel, RowExclusiveLock);
   12565          10 : }
   12566             : 
   12567             : /*
   12568             :  * The guts of relocating a relation to another namespace: fix the pg_class
   12569             :  * entry, and the pg_depend entry if any.  Caller must already have
   12570             :  * opened and write-locked pg_class.
   12571             :  */
   12572             : void
   12573          22 : AlterRelationNamespaceInternal(Relation classRel, Oid relOid,
   12574             :                                Oid oldNspOid, Oid newNspOid,
   12575             :                                bool hasDependEntry,
   12576             :                                ObjectAddresses *objsMoved)
   12577             : {
   12578             :     HeapTuple   classTup;
   12579             :     Form_pg_class classForm;
   12580             :     ObjectAddress thisobj;
   12581          22 :     bool        already_done = false;
   12582             : 
   12583          22 :     classTup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relOid));
   12584          22 :     if (!HeapTupleIsValid(classTup))
   12585           0 :         elog(ERROR, "cache lookup failed for relation %u", relOid);
   12586          22 :     classForm = (Form_pg_class) GETSTRUCT(classTup);
   12587             : 
   12588          22 :     Assert(classForm->relnamespace == oldNspOid);
   12589             : 
   12590          22 :     thisobj.classId = RelationRelationId;
   12591          22 :     thisobj.objectId = relOid;
   12592          22 :     thisobj.objectSubId = 0;
   12593             : 
   12594             :     /*
   12595             :      * If the object has already been moved, don't move it again.  If it's
   12596             :      * already in the right place, don't move it, but still fire the object
   12597             :      * access hook.
   12598             :      */
   12599          22 :     already_done = object_address_present(&thisobj, objsMoved);
   12600          22 :     if (!already_done && oldNspOid != newNspOid)
   12601             :     {
   12602             :         /* check for duplicate name (more friendly than unique-index failure) */
   12603          15 :         if (get_relname_relid(NameStr(classForm->relname),
   12604             :                               newNspOid) != InvalidOid)
   12605           0 :             ereport(ERROR,
   12606             :                     (errcode(ERRCODE_DUPLICATE_TABLE),
   12607             :                      errmsg("relation \"%s\" already exists in schema \"%s\"",
   12608             :                             NameStr(classForm->relname),
   12609             :                             get_namespace_name(newNspOid))));
   12610             : 
   12611             :         /* classTup is a copy, so OK to scribble on */
   12612          15 :         classForm->relnamespace = newNspOid;
   12613             : 
   12614          15 :         CatalogTupleUpdate(classRel, &classTup->t_self, classTup);
   12615             : 
   12616             :         /* Update dependency on schema if caller said so */
   12617          25 :         if (hasDependEntry &&
   12618          11 :             changeDependencyFor(RelationRelationId,
   12619             :                                 relOid,
   12620             :                                 NamespaceRelationId,
   12621             :                                 oldNspOid,
   12622             :                                 newNspOid) != 1)
   12623           0 :             elog(ERROR, "failed to change schema dependency for relation \"%s\"",
   12624             :                  NameStr(classForm->relname));
   12625             :     }
   12626          21 :     if (!already_done)
   12627             :     {
   12628          21 :         add_exact_object_address(&thisobj, objsMoved);
   12629             : 
   12630          21 :         InvokeObjectPostAlterHook(RelationRelationId, relOid, 0);
   12631             :     }
   12632             : 
   12633          21 :     heap_freetuple(classTup);
   12634          21 : }
   12635             : 
   12636             : /*
   12637             :  * Move all indexes for the specified relation to another namespace.
   12638             :  *
   12639             :  * Note: we assume adequate permission checking was done by the caller,
   12640             :  * and that the caller has a suitable lock on the owning relation.
   12641             :  */
   12642             : static void
   12643           8 : AlterIndexNamespaces(Relation classRel, Relation rel,
   12644             :                      Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved)
   12645             : {
   12646             :     List       *indexList;
   12647             :     ListCell   *l;
   12648             : 
   12649           8 :     indexList = RelationGetIndexList(rel);
   12650             : 
   12651          13 :     foreach(l, indexList)
   12652             :     {
   12653           5 :         Oid         indexOid = lfirst_oid(l);
   12654             :         ObjectAddress thisobj;
   12655             : 
   12656           5 :         thisobj.classId = RelationRelationId;
   12657           5 :         thisobj.objectId = indexOid;
   12658           5 :         thisobj.objectSubId = 0;
   12659             : 
   12660             :         /*
   12661             :          * Note: currently, the index will not have its own dependency on the
   12662             :          * namespace, so we don't need to do changeDependencyFor(). There's no
   12663             :          * row type in pg_type, either.
   12664             :          *
   12665             :          * XXX this objsMoved test may be pointless -- surely we have a single
   12666             :          * dependency link from a relation to each index?
   12667             :          */
   12668           5 :         if (!object_address_present(&thisobj, objsMoved))
   12669             :         {
   12670           5 :             AlterRelationNamespaceInternal(classRel, indexOid,
   12671             :                                            oldNspOid, newNspOid,
   12672             :                                            false, objsMoved);
   12673           5 :             add_exact_object_address(&thisobj, objsMoved);
   12674             :         }
   12675             :     }
   12676             : 
   12677           8 :     list_free(indexList);
   12678           8 : }
   12679             : 
   12680             : /*
   12681             :  * Move all identity and SERIAL-column sequences of the specified relation to another
   12682             :  * namespace.
   12683             :  *
   12684             :  * Note: we assume adequate permission checking was done by the caller,
   12685             :  * and that the caller has a suitable lock on the owning relation.
   12686             :  */
   12687             : static void
   12688           8 : AlterSeqNamespaces(Relation classRel, Relation rel,
   12689             :                    Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved,
   12690             :                    LOCKMODE lockmode)
   12691             : {
   12692             :     Relation    depRel;
   12693             :     SysScanDesc scan;
   12694             :     ScanKeyData key[2];
   12695             :     HeapTuple   tup;
   12696             : 
   12697             :     /*
   12698             :      * SERIAL sequences are those having an auto dependency on one of the
   12699             :      * table's columns (we don't care *which* column, exactly).
   12700             :      */
   12701           8 :     depRel = heap_open(DependRelationId, AccessShareLock);
   12702             : 
   12703           8 :     ScanKeyInit(&key[0],
   12704             :                 Anum_pg_depend_refclassid,
   12705             :                 BTEqualStrategyNumber, F_OIDEQ,
   12706             :                 ObjectIdGetDatum(RelationRelationId));
   12707           8 :     ScanKeyInit(&key[1],
   12708             :                 Anum_pg_depend_refobjid,
   12709             :                 BTEqualStrategyNumber, F_OIDEQ,
   12710             :                 ObjectIdGetDatum(RelationGetRelid(rel)));
   12711             :     /* we leave refobjsubid unspecified */
   12712             : 
   12713           8 :     scan = systable_beginscan(depRel, DependReferenceIndexId, true,
   12714             :                               NULL, 2, key);
   12715             : 
   12716          69 :     while (HeapTupleIsValid(tup = systable_getnext(scan)))
   12717             :     {
   12718          53 :         Form_pg_depend depForm = (Form_pg_depend) GETSTRUCT(tup);
   12719             :         Relation    seqRel;
   12720             : 
   12721             :         /* skip dependencies other than auto dependencies on columns */
   12722          91 :         if (depForm->refobjsubid == 0 ||
   12723          42 :             depForm->classid != RelationRelationId ||
   12724           8 :             depForm->objsubid != 0 ||
   12725           4 :             !(depForm->deptype == DEPENDENCY_AUTO || depForm->deptype == DEPENDENCY_INTERNAL))
   12726          49 :             continue;
   12727             : 
   12728             :         /* Use relation_open just in case it's an index */
   12729           4 :         seqRel = relation_open(depForm->objid, lockmode);
   12730             : 
   12731             :         /* skip non-sequence relations */
   12732           4 :         if (RelationGetForm(seqRel)->relkind != RELKIND_SEQUENCE)
   12733             :         {
   12734             :             /* No need to keep the lock */
   12735           0 :             relation_close(seqRel, lockmode);
   12736           0 :             continue;
   12737             :         }
   12738             : 
   12739             :         /* Fix the pg_class and pg_depend entries */
   12740           4 :         AlterRelationNamespaceInternal(classRel, depForm->objid,
   12741             :                                        oldNspOid, newNspOid,
   12742             :                                        true, objsMoved);
   12743             : 
   12744             :         /*
   12745             :          * Sequences have entries in pg_type. We need to be careful to move
   12746             :          * them to the new namespace, too.
   12747             :          */
   12748           4 :         AlterTypeNamespaceInternal(RelationGetForm(seqRel)->reltype,
   12749             :                                    newNspOid, false, false, objsMoved);
   12750             : 
   12751             :         /* Now we can close it.  Keep the lock till end of transaction. */
   12752           4 :         relation_close(seqRel, NoLock);
   12753             :     }
   12754             : 
   12755           8 :     systable_endscan(scan);
   12756             : 
   12757           8 :     relation_close(depRel, AccessShareLock);
   12758           8 : }
   12759             : 
   12760             : 
   12761             : /*
   12762             :  * This code supports
   12763             :  *  CREATE TEMP TABLE ... ON COMMIT { DROP | PRESERVE ROWS | DELETE ROWS }
   12764             :  *
   12765             :  * Because we only support this for TEMP tables, it's sufficient to remember
   12766             :  * the state in a backend-local data structure.
   12767             :  */
   12768             : 
   12769             : /*
   12770             :  * Register a newly-created relation's ON COMMIT action.
   12771             :  */
   12772             : void
   12773           8 : register_on_commit_action(Oid relid, OnCommitAction action)
   12774             : {
   12775             :     OnCommitItem *oc;
   12776             :     MemoryContext oldcxt;
   12777             : 
   12778             :     /*
   12779             :      * We needn't bother registering the relation unless there is an ON COMMIT
   12780             :      * action we need to take.
   12781             :      */
   12782           8 :     if (action == ONCOMMIT_NOOP || action == ONCOMMIT_PRESERVE_ROWS)
   12783           8 :         return;
   12784             : 
   12785           8 :     oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
   12786             : 
   12787           8 :     oc = (OnCommitItem *) palloc(sizeof(OnCommitItem));
   12788           8 :     oc->relid = relid;
   12789           8 :     oc->oncommit = action;
   12790           8 :     oc->creating_subid = GetCurrentSubTransactionId();
   12791           8 :     oc->deleting_subid = InvalidSubTransactionId;
   12792             : 
   12793           8 :     on_commits = lcons(oc, on_commits);
   12794             : 
   12795           8 :     MemoryContextSwitchTo(oldcxt);
   12796             : }
   12797             : 
   12798             : /*
   12799             :  * Unregister any ON COMMIT action when a relation is deleted.
   12800             :  *
   12801             :  * Actually, we only mark the OnCommitItem entry as to be deleted after commit.
   12802             :  */
   12803             : void
   12804        2146 : remove_on_commit_action(Oid relid)
   12805             : {
   12806             :     ListCell   *l;
   12807             : 
   12808        2153 :     foreach(l, on_commits)
   12809             :     {
   12810          14 :         OnCommitItem *oc = (OnCommitItem *) lfirst(l);
   12811             : 
   12812          14 :         if (oc->relid == relid)
   12813             :         {
   12814           7 :             oc->deleting_subid = GetCurrentSubTransactionId();
   12815           7 :             break;
   12816             :         }
   12817             :     }
   12818        2146 : }
   12819             : 
   12820             : /*
   12821             :  * Perform ON COMMIT actions.
   12822             :  *
   12823             :  * This is invoked just before actually committing, since it's possible
   12824             :  * to encounter errors.
   12825             :  */
   12826             : void
   12827       22914 : PreCommit_on_commit_actions(void)
   12828             : {
   12829             :     ListCell   *l;
   12830       22914 :     List       *oids_to_truncate = NIL;
   12831             : 
   12832       22958 :     foreach(l, on_commits)
   12833             :     {
   12834          44 :         OnCommitItem *oc = (OnCommitItem *) lfirst(l);
   12835             : 
   12836             :         /* Ignore entry if already dropped in this xact */
   12837          44 :         if (oc->deleting_subid != InvalidSubTransactionId)
   12838           5 :             continue;
   12839             : 
   12840          39 :         switch (oc->oncommit)
   12841             :         {
   12842             :             case ONCOMMIT_NOOP:
   12843             :             case ONCOMMIT_PRESERVE_ROWS:
   12844             :                 /* Do nothing (there shouldn't be such entries, actually) */
   12845           0 :                 break;
   12846             :             case ONCOMMIT_DELETE_ROWS:
   12847             : 
   12848             :                 /*
   12849             :                  * If this transaction hasn't accessed any temporary
   12850             :                  * relations, we can skip truncating ON COMMIT DELETE ROWS
   12851             :                  * tables, as they must still be empty.
   12852             :                  */
   12853          37 :                 if ((MyXactFlags & XACT_FLAGS_ACCESSEDTEMPREL))
   12854          16 :                     oids_to_truncate = lappend_oid(oids_to_truncate, oc->relid);
   12855          37 :                 break;
   12856             :             case ONCOMMIT_DROP:
   12857             :                 {
   12858             :                     ObjectAddress object;
   12859             : 
   12860           2 :                     object.classId = RelationRelationId;
   12861           2 :                     object.objectId = oc->relid;
   12862           2 :                     object.objectSubId = 0;
   12863             : 
   12864             :                     /*
   12865             :                      * Since this is an automatic drop, rather than one
   12866             :                      * directly initiated by the user, we pass the
   12867             :                      * PERFORM_DELETION_INTERNAL flag.
   12868             :                      */
   12869           2 :                     performDeletion(&object,
   12870             :                                     DROP_CASCADE, PERFORM_DELETION_INTERNAL);
   12871             : 
   12872             :                     /*
   12873             :                      * Note that table deletion will call
   12874             :                      * remove_on_commit_action, so the entry should get marked
   12875             :                      * as deleted.
   12876             :                      */
   12877           2 :                     Assert(oc->deleting_subid != InvalidSubTransactionId);
   12878           2 :                     break;
   12879             :                 }
   12880             :         }
   12881             :     }
   12882       22914 :     if (oids_to_truncate != NIL)
   12883             :     {
   12884          15 :         heap_truncate(oids_to_truncate);
   12885          14 :         CommandCounterIncrement();  /* XXX needed? */
   12886             :     }
   12887       22913 : }
   12888             : 
   12889             : /*
   12890             :  * Post-commit or post-abort cleanup for ON COMMIT management.
   12891             :  *
   12892             :  * All we do here is remove no-longer-needed OnCommitItem entries.
   12893             :  *
   12894             :  * During commit, remove entries that were deleted during this transaction;
   12895             :  * during abort, remove those created during this transaction.
   12896             :  */
   12897             : void
   12898       26218 : AtEOXact_on_commit_actions(bool isCommit)
   12899             : {
   12900             :     ListCell   *cur_item;
   12901             :     ListCell   *prev_item;
   12902             : 
   12903       26218 :     prev_item = NULL;
   12904       26218 :     cur_item = list_head(on_commits);
   12905             : 
   12906       52480 :     while (cur_item != NULL)
   12907             :     {
   12908          44 :         OnCommitItem *oc = (OnCommitItem *) lfirst(cur_item);
   12909             : 
   12910          46 :         if (isCommit ? oc->deleting_subid != InvalidSubTransactionId :
   12911           2 :             oc->creating_subid != InvalidSubTransactionId)
   12912             :         {
   12913             :             /* cur_item must be removed */
   12914           8 :             on_commits = list_delete_cell(on_commits, cur_item, prev_item);
   12915           8 :             pfree(oc);
   12916           8 :             if (prev_item)
   12917           0 :                 cur_item = lnext(prev_item);
   12918             :             else
   12919           8 :                 cur_item = list_head(on_commits);
   12920             :         }
   12921             :         else
   12922             :         {
   12923             :             /* cur_item must be preserved */
   12924          36 :             oc->creating_subid = InvalidSubTransactionId;
   12925          36 :             oc->deleting_subid = InvalidSubTransactionId;
   12926          36 :             prev_item = cur_item;
   12927          36 :             cur_item = lnext(prev_item);
   12928             :         }
   12929             :     }
   12930       26218 : }
   12931             : 
   12932             : /*
   12933             :  * Post-subcommit or post-subabort cleanup for ON COMMIT management.
   12934             :  *
   12935             :  * During subabort, we can immediately remove entries created during this
   12936             :  * subtransaction.  During subcommit, just relabel entries marked during
   12937             :  * this subtransaction as being the parent's responsibility.
   12938             :  */
   12939             : void
   12940         372 : AtEOSubXact_on_commit_actions(bool isCommit, SubTransactionId mySubid,
   12941             :                               SubTransactionId parentSubid)
   12942             : {
   12943             :     ListCell   *cur_item;
   12944             :     ListCell   *prev_item;
   12945             : 
   12946         372 :     prev_item = NULL;
   12947         372 :     cur_item = list_head(on_commits);
   12948             : 
   12949         744 :     while (cur_item != NULL)
   12950             :     {
   12951           0 :         OnCommitItem *oc = (OnCommitItem *) lfirst(cur_item);
   12952             : 
   12953           0 :         if (!isCommit && oc->creating_subid == mySubid)
   12954             :         {
   12955             :             /* cur_item must be removed */
   12956           0 :             on_commits = list_delete_cell(on_commits, cur_item, prev_item);
   12957           0 :             pfree(oc);
   12958           0 :             if (prev_item)
   12959           0 :                 cur_item = lnext(prev_item);
   12960             :             else
   12961           0 :                 cur_item = list_head(on_commits);
   12962             :         }
   12963             :         else
   12964             :         {
   12965             :             /* cur_item must be preserved */
   12966           0 :             if (oc->creating_subid == mySubid)
   12967           0 :                 oc->creating_subid = parentSubid;
   12968           0 :             if (oc->deleting_subid == mySubid)
   12969           0 :                 oc->deleting_subid = isCommit ? parentSubid : InvalidSubTransactionId;
   12970           0 :             prev_item = cur_item;
   12971           0 :             cur_item = lnext(prev_item);
   12972             :         }
   12973             :     }
   12974         372 : }
   12975             : 
   12976             : /*
   12977             :  * This is intended as a callback for RangeVarGetRelidExtended().  It allows
   12978             :  * the relation to be locked only if (1) it's a plain table, materialized
   12979             :  * view, or TOAST table and (2) the current user is the owner (or the
   12980             :  * superuser).  This meets the permission-checking needs of CLUSTER, REINDEX
   12981             :  * TABLE, and REFRESH MATERIALIZED VIEW; we expose it here so that it can be
   12982             :  * used by all.
   12983             :  */
   12984             : void
   12985          36 : RangeVarCallbackOwnsTable(const RangeVar *relation,
   12986             :                           Oid relId, Oid oldRelId, void *arg)
   12987             : {
   12988             :     char        relkind;
   12989             : 
   12990             :     /* Nothing to do if the relation was not found. */
   12991          36 :     if (!OidIsValid(relId))
   12992           0 :         return;
   12993             : 
   12994             :     /*
   12995             :      * If the relation does exist, check whether it's an index.  But note that
   12996             :      * the relation might have been dropped between the time we did the name
   12997             :      * lookup and now.  In that case, there's nothing to do.
   12998             :      */
   12999          36 :     relkind = get_rel_relkind(relId);
   13000          36 :     if (!relkind)
   13001           0 :         return;
   13002          36 :     if (relkind != RELKIND_RELATION && relkind != RELKIND_TOASTVALUE &&
   13003           0 :         relkind != RELKIND_MATVIEW && relkind != RELKIND_PARTITIONED_TABLE)
   13004           0 :         ereport(ERROR,
   13005             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   13006             :                  errmsg("\"%s\" is not a table or materialized view", relation->relname)));
   13007             : 
   13008             :     /* Check permissions */
   13009          36 :     if (!pg_class_ownercheck(relId, GetUserId()))
   13010           0 :         aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, relation->relname);
   13011             : }
   13012             : 
   13013             : /*
   13014             :  * Callback to RangeVarGetRelidExtended(), similar to
   13015             :  * RangeVarCallbackOwnsTable() but without checks on the type of the relation.
   13016             :  */
   13017             : void
   13018         657 : RangeVarCallbackOwnsRelation(const RangeVar *relation,
   13019             :                              Oid relId, Oid oldRelId, void *arg)
   13020             : {
   13021             :     HeapTuple   tuple;
   13022             : 
   13023             :     /* Nothing to do if the relation was not found. */
   13024         657 :     if (!OidIsValid(relId))
   13025         657 :         return;
   13026             : 
   13027         656 :     tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relId));
   13028         656 :     if (!HeapTupleIsValid(tuple))   /* should not happen */
   13029           0 :         elog(ERROR, "cache lookup failed for relation %u", relId);
   13030             : 
   13031         656 :     if (!pg_class_ownercheck(relId, GetUserId()))
   13032           1 :         aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
   13033           1 :                        relation->relname);
   13034             : 
   13035        1310 :     if (!allowSystemTableMods &&
   13036         655 :         IsSystemClass(relId, (Form_pg_class) GETSTRUCT(tuple)))
   13037           0 :         ereport(ERROR,
   13038             :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
   13039             :                  errmsg("permission denied: \"%s\" is a system catalog",
   13040             :                         relation->relname)));
   13041             : 
   13042         655 :     ReleaseSysCache(tuple);
   13043             : }
   13044             : 
   13045             : /*
   13046             :  * Common RangeVarGetRelid callback for rename, set schema, and alter table
   13047             :  * processing.
   13048             :  */
   13049             : static void
   13050        1086 : RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid, Oid oldrelid,
   13051             :                                  void *arg)
   13052             : {
   13053        1086 :     Node       *stmt = (Node *) arg;
   13054             :     ObjectType  reltype;
   13055             :     HeapTuple   tuple;
   13056             :     Form_pg_class classform;
   13057             :     AclResult   aclresult;
   13058             :     char        relkind;
   13059             : 
   13060        1086 :     tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
   13061        1086 :     if (!HeapTupleIsValid(tuple))
   13062        1118 :         return;                 /* concurrently dropped */
   13063        1051 :     classform = (Form_pg_class) GETSTRUCT(tuple);
   13064        1051 :     relkind = classform->relkind;
   13065             : 
   13066             :     /* Must own relation. */
   13067        1051 :     if (!pg_class_ownercheck(relid, GetUserId()))
   13068           0 :         aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, rv->relname);
   13069             : 
   13070             :     /* No system table modifications unless explicitly allowed. */
   13071        1051 :     if (!allowSystemTableMods && IsSystemClass(relid, classform))
   13072           3 :         ereport(ERROR,
   13073             :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
   13074             :                  errmsg("permission denied: \"%s\" is a system catalog",
   13075             :                         rv->relname)));
   13076             : 
   13077             :     /*
   13078             :      * Extract the specified relation type from the statement parse tree.
   13079             :      *
   13080             :      * Also, for ALTER .. RENAME, check permissions: the user must (still)
   13081             :      * have CREATE rights on the containing namespace.
   13082             :      */
   13083        1048 :     if (IsA(stmt, RenameStmt))
   13084             :     {
   13085          24 :         aclresult = pg_namespace_aclcheck(classform->relnamespace,
   13086             :                                           GetUserId(), ACL_CREATE);
   13087          24 :         if (aclresult != ACLCHECK_OK)
   13088           0 :             aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
   13089           0 :                            get_namespace_name(classform->relnamespace));
   13090          24 :         reltype = ((RenameStmt *) stmt)->renameType;
   13091             :     }
   13092        1024 :     else if (IsA(stmt, AlterObjectSchemaStmt))
   13093          11 :         reltype = ((AlterObjectSchemaStmt *) stmt)->objectType;
   13094             : 
   13095        1013 :     else if (IsA(stmt, AlterTableStmt))
   13096        1013 :         reltype = ((AlterTableStmt *) stmt)->relkind;
   13097             :     else
   13098             :     {
   13099           0 :         reltype = OBJECT_TABLE; /* placate compiler */
   13100           0 :         elog(ERROR, "unrecognized node type: %d", (int) nodeTag(stmt));
   13101             :     }
   13102             : 
   13103             :     /*
   13104             :      * For compatibility with prior releases, we allow ALTER TABLE to be used
   13105             :      * with most other types of relations (but not composite types). We allow
   13106             :      * similar flexibility for ALTER INDEX in the case of RENAME, but not
   13107             :      * otherwise.  Otherwise, the user must select the correct form of the
   13108             :      * command for the relation at issue.
   13109             :      */
   13110        1048 :     if (reltype == OBJECT_SEQUENCE && relkind != RELKIND_SEQUENCE)
   13111           0 :         ereport(ERROR,
   13112             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   13113             :                  errmsg("\"%s\" is not a sequence", rv->relname)));
   13114             : 
   13115        1048 :     if (reltype == OBJECT_VIEW && relkind != RELKIND_VIEW)
   13116           0 :         ereport(ERROR,
   13117             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   13118             :                  errmsg("\"%s\" is not a view", rv->relname)));
   13119             : 
   13120        1048 :     if (reltype == OBJECT_MATVIEW && relkind != RELKIND_MATVIEW)
   13121           0 :         ereport(ERROR,
   13122             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   13123             :                  errmsg("\"%s\" is not a materialized view", rv->relname)));
   13124             : 
   13125        1048 :     if (reltype == OBJECT_FOREIGN_TABLE && relkind != RELKIND_FOREIGN_TABLE)
   13126           0 :         ereport(ERROR,
   13127             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   13128             :                  errmsg("\"%s\" is not a foreign table", rv->relname)));
   13129             : 
   13130        1048 :     if (reltype == OBJECT_TYPE && relkind != RELKIND_COMPOSITE_TYPE)
   13131           0 :         ereport(ERROR,
   13132             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   13133             :                  errmsg("\"%s\" is not a composite type", rv->relname)));
   13134             : 
   13135        1048 :     if (reltype == OBJECT_INDEX && relkind != RELKIND_INDEX
   13136           0 :         && !IsA(stmt, RenameStmt))
   13137           0 :         ereport(ERROR,
   13138             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   13139             :                  errmsg("\"%s\" is not an index", rv->relname)));
   13140             : 
   13141             :     /*
   13142             :      * Don't allow ALTER TABLE on composite types. We want people to use ALTER
   13143             :      * TYPE for that.
   13144             :      */
   13145        1048 :     if (reltype != OBJECT_TYPE && relkind == RELKIND_COMPOSITE_TYPE)
   13146           0 :         ereport(ERROR,
   13147             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   13148             :                  errmsg("\"%s\" is a composite type", rv->relname),
   13149             :                  errhint("Use ALTER TYPE instead.")));
   13150             : 
   13151             :     /*
   13152             :      * Don't allow ALTER TABLE .. SET SCHEMA on relations that can't be moved
   13153             :      * to a different schema, such as indexes and TOAST tables.
   13154             :      */
   13155        1048 :     if (IsA(stmt, AlterObjectSchemaStmt) &&
   13156           3 :         relkind != RELKIND_RELATION &&
   13157           2 :         relkind != RELKIND_VIEW &&
   13158           1 :         relkind != RELKIND_MATVIEW &&
   13159           1 :         relkind != RELKIND_SEQUENCE &&
   13160           0 :         relkind != RELKIND_FOREIGN_TABLE &&
   13161             :         relkind != RELKIND_PARTITIONED_TABLE)
   13162           0 :         ereport(ERROR,
   13163             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   13164             :                  errmsg("\"%s\" is not a table, view, materialized view, sequence, or foreign table",
   13165             :                         rv->relname)));
   13166             : 
   13167        1048 :     ReleaseSysCache(tuple);
   13168             : }
   13169             : 
   13170             : /*
   13171             :  * Transform any expressions present in the partition key
   13172             :  *
   13173             :  * Returns a transformed PartitionSpec, as well as the strategy code
   13174             :  */
   13175             : static PartitionSpec *
   13176          93 : transformPartitionSpec(Relation rel, PartitionSpec *partspec, char *strategy)
   13177             : {
   13178             :     PartitionSpec *newspec;
   13179             :     ParseState *pstate;
   13180             :     RangeTblEntry *rte;
   13181             :     ListCell   *l;
   13182             : 
   13183          93 :     newspec = makeNode(PartitionSpec);
   13184             : 
   13185          93 :     newspec->strategy = partspec->strategy;
   13186          93 :     newspec->partParams = NIL;
   13187          93 :     newspec->location = partspec->location;
   13188             : 
   13189             :     /* Parse partitioning strategy name */
   13190          93 :     if (pg_strcasecmp(partspec->strategy, "list") == 0)
   13191          46 :         *strategy = PARTITION_STRATEGY_LIST;
   13192          47 :     else if (pg_strcasecmp(partspec->strategy, "range") == 0)
   13193          46 :         *strategy = PARTITION_STRATEGY_RANGE;
   13194             :     else
   13195           1 :         ereport(ERROR,
   13196             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
   13197             :                  errmsg("unrecognized partitioning strategy \"%s\"",
   13198             :                         partspec->strategy)));
   13199             : 
   13200             :     /* Check valid number of columns for strategy */
   13201         138 :     if (*strategy == PARTITION_STRATEGY_LIST &&
   13202          46 :         list_length(partspec->partParams) != 1)
   13203           1 :         ereport(ERROR,
   13204             :                 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
   13205             :                  errmsg("cannot use \"list\" partition strategy with more than one column")));
   13206             : 
   13207             :     /*
   13208             :      * Create a dummy ParseState and insert the target relation as its sole
   13209             :      * rangetable entry.  We need a ParseState for transformExpr.
   13210             :      */
   13211          91 :     pstate = make_parsestate(NULL);
   13212          91 :     rte = addRangeTableEntryForRelation(pstate, rel, NULL, false, true);
   13213          91 :     addRTEtoQuery(pstate, rte, true, true, true);
   13214             : 
   13215             :     /* take care of any partition expressions */
   13216         197 :     foreach(l, partspec->partParams)
   13217             :     {
   13218         111 :         PartitionElem *pelem = castNode(PartitionElem, lfirst(l));
   13219             :         ListCell   *lc;
   13220             : 
   13221             :         /* Check for PARTITION BY ... (foo, foo) */
   13222         136 :         foreach(lc, newspec->partParams)
   13223             :         {
   13224          26 :             PartitionElem *pparam = castNode(PartitionElem, lfirst(lc));
   13225             : 
   13226          39 :             if (pelem->name && pparam->name &&
   13227          13 :                 strcmp(pelem->name, pparam->name) == 0)
   13228           1 :                 ereport(ERROR,
   13229             :                         (errcode(ERRCODE_DUPLICATE_COLUMN),
   13230             :                          errmsg("column \"%s\" appears more than once in partition key",
   13231             :                                 pelem->name),
   13232             :                          parser_errposition(pstate, pelem->location)));
   13233             :         }
   13234             : 
   13235         110 :         if (pelem->expr)
   13236             :         {
   13237             :             /* Copy, to avoid scribbling on the input */
   13238          21 :             pelem = copyObject(pelem);
   13239             : 
   13240             :             /* Now do parse transformation of the expression */
   13241          21 :             pelem->expr = transformExpr(pstate, pelem->expr,
   13242             :                                         EXPR_KIND_PARTITION_EXPRESSION);
   13243             : 
   13244             :             /* we have to fix its collations too */
   13245          17 :             assign_expr_collations(pstate, pelem->expr);
   13246             :         }
   13247             : 
   13248         106 :         newspec->partParams = lappend(newspec->partParams, pelem);
   13249             :     }
   13250             : 
   13251          86 :     return newspec;
   13252             : }
   13253             : 
   13254             : /*
   13255             :  * Compute per-partition-column information from a list of PartitionElems.
   13256             :  * Expressions in the PartitionElems must be parse-analyzed already.
   13257             :  */
   13258             : static void
   13259          86 : ComputePartitionAttrs(Relation rel, List *partParams, AttrNumber *partattrs,
   13260             :                       List **partexprs, Oid *partopclass, Oid *partcollation)
   13261             : {
   13262             :     int         attn;
   13263             :     ListCell   *lc;
   13264             : 
   13265          86 :     attn = 0;
   13266         180 :     foreach(lc, partParams)
   13267             :     {
   13268         105 :         PartitionElem *pelem = castNode(PartitionElem, lfirst(lc));
   13269             :         Oid         atttype;
   13270             :         Oid         attcollation;
   13271             : 
   13272         105 :         if (pelem->name != NULL)
   13273             :         {
   13274             :             /* Simple attribute reference */
   13275             :             HeapTuple   atttuple;
   13276             :             Form_pg_attribute attform;
   13277             : 
   13278          88 :             atttuple = SearchSysCacheAttName(RelationGetRelid(rel),
   13279          88 :                                              pelem->name);
   13280          88 :             if (!HeapTupleIsValid(atttuple))
   13281           2 :                 ereport(ERROR,
   13282             :                         (errcode(ERRCODE_UNDEFINED_COLUMN),
   13283             :                          errmsg("column \"%s\" named in partition key does not exist",
   13284             :                                 pelem->name)));
   13285          86 :             attform = (Form_pg_attribute) GETSTRUCT(atttuple);
   13286             : 
   13287          86 :             if (attform->attnum <= 0)
   13288           1 :                 ereport(ERROR,
   13289             :                         (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
   13290             :                          errmsg("cannot use system column \"%s\" in partition key",
   13291             :                                 pelem->name)));
   13292             : 
   13293          85 :             partattrs[attn] = attform->attnum;
   13294          85 :             atttype = attform->atttypid;
   13295          85 :             attcollation = attform->attcollation;
   13296          85 :             ReleaseSysCache(atttuple);
   13297             :         }
   13298             :         else
   13299             :         {
   13300             :             /* Expression */
   13301          17 :             Node       *expr = pelem->expr;
   13302             : 
   13303          17 :             Assert(expr != NULL);
   13304          17 :             atttype = exprType(expr);
   13305          17 :             attcollation = exprCollation(expr);
   13306             : 
   13307             :             /*
   13308             :              * Strip any top-level COLLATE clause.  This ensures that we treat
   13309             :              * "x COLLATE y" and "(x COLLATE y)" alike.
   13310             :              */
   13311          34 :             while (IsA(expr, CollateExpr))
   13312           0 :                 expr = (Node *) ((CollateExpr *) expr)->arg;
   13313             : 
   13314          19 :             if (IsA(expr, Var) &&
   13315           2 :                 ((Var *) expr)->varattno > 0)
   13316             :             {
   13317             :                 /*
   13318             :                  * User wrote "(column)" or "(column COLLATE something)".
   13319             :                  * Treat it like simple attribute anyway.
   13320             :                  */
   13321           1 :                 partattrs[attn] = ((Var *) expr)->varattno;
   13322             :             }
   13323             :             else
   13324             :             {
   13325          16 :                 Bitmapset  *expr_attrs = NULL;
   13326             :                 int         i;
   13327             : 
   13328          16 :                 partattrs[attn] = 0;    /* marks the column as expression */
   13329          16 :                 *partexprs = lappend(*partexprs, expr);
   13330             : 
   13331             :                 /*
   13332             :                  * Try to simplify the expression before checking for
   13333             :                  * mutability.  The main practical value of doing it in this
   13334             :                  * order is that an inline-able SQL-language function will be
   13335             :                  * accepted if its expansion is immutable, whether or not the
   13336             :                  * function itself is marked immutable.
   13337             :                  *
   13338             :                  * Note that expression_planner does not change the passed in
   13339             :                  * expression destructively and we have already saved the
   13340             :                  * expression to be stored into the catalog above.
   13341             :                  */
   13342          16 :                 expr = (Node *) expression_planner((Expr *) expr);
   13343             : 
   13344             :                 /*
   13345             :                  * Partition expression cannot contain mutable functions,
   13346             :                  * because a given row must always map to the same partition
   13347             :                  * as long as there is no change in the partition boundary
   13348             :                  * structure.
   13349             :                  */
   13350          16 :                 if (contain_mutable_functions(expr))
   13351           1 :                     ereport(ERROR,
   13352             :                             (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
   13353             :                              errmsg("functions in partition key expression must be marked IMMUTABLE")));
   13354             : 
   13355             :                 /*
   13356             :                  * transformPartitionSpec() should have already rejected
   13357             :                  * subqueries, aggregates, window functions, and SRFs, based
   13358             :                  * on the EXPR_KIND_ for partition expressions.
   13359             :                  */
   13360             : 
   13361             :                 /*
   13362             :                  * Cannot have expressions containing whole-row references or
   13363             :                  * system column references.
   13364             :                  */
   13365          15 :                 pull_varattnos(expr, 1, &expr_attrs);
   13366          15 :                 if (bms_is_member(0 - FirstLowInvalidHeapAttributeNumber,
   13367             :                                   expr_attrs))
   13368           1 :                     ereport(ERROR,
   13369             :                             (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
   13370             :                              errmsg("partition key expressions cannot contain whole-row references")));
   13371         126 :                 for (i = FirstLowInvalidHeapAttributeNumber; i < 0; i++)
   13372             :                 {
   13373         112 :                     if (bms_is_member(i - FirstLowInvalidHeapAttributeNumber,
   13374             :                                       expr_attrs))
   13375           0 :                         ereport(ERROR,
   13376             :                                 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
   13377             :                                  errmsg("partition key expressions cannot contain system column references")));
   13378             :                 }
   13379             : 
   13380             :                 /*
   13381             :                  * While it is not exactly *wrong* for a partition expression
   13382             :                  * to be a constant, it seems better to reject such keys.
   13383             :                  */
   13384          14 :                 if (IsA(expr, Const))
   13385           2 :                     ereport(ERROR,
   13386             :                             (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
   13387             :                              errmsg("cannot use constant expression as partition key")));
   13388             :             }
   13389             :         }
   13390             : 
   13391             :         /*
   13392             :          * Apply collation override if any
   13393             :          */
   13394          98 :         if (pelem->collation)
   13395           2 :             attcollation = get_collation_oid(pelem->collation, false);
   13396             : 
   13397             :         /*
   13398             :          * Check we have a collation iff it's a collatable type.  The only
   13399             :          * expected failures here are (1) COLLATE applied to a noncollatable
   13400             :          * type, or (2) partition expression had an unresolved collation. But
   13401             :          * we might as well code this to be a complete consistency check.
   13402             :          */
   13403          98 :         if (type_is_collatable(atttype))
   13404             :         {
   13405          19 :             if (!OidIsValid(attcollation))
   13406           0 :                 ereport(ERROR,
   13407             :                         (errcode(ERRCODE_INDETERMINATE_COLLATION),
   13408             :                          errmsg("could not determine which collation to use for partition expression"),
   13409             :                          errhint("Use the COLLATE clause to set the collation explicitly.")));
   13410             :         }
   13411             :         else
   13412             :         {
   13413          79 :             if (OidIsValid(attcollation))
   13414           0 :                 ereport(ERROR,
   13415             :                         (errcode(ERRCODE_DATATYPE_MISMATCH),
   13416             :                          errmsg("collations are not supported by type %s",
   13417             :                                 format_type_be(atttype))));
   13418             :         }
   13419             : 
   13420          98 :         partcollation[attn] = attcollation;
   13421             : 
   13422             :         /*
   13423             :          * Identify a btree opclass to use. Currently, we use only btree
   13424             :          * operators, which seems enough for list and range partitioning.
   13425             :          */
   13426          98 :         if (!pelem->opclass)
   13427             :         {
   13428          95 :             partopclass[attn] = GetDefaultOpClass(atttype, BTREE_AM_OID);
   13429             : 
   13430          95 :             if (!OidIsValid(partopclass[attn]))
   13431           2 :                 ereport(ERROR,
   13432             :                         (errcode(ERRCODE_UNDEFINED_OBJECT),
   13433             :                          errmsg("data type %s has no default btree operator class",
   13434             :                                 format_type_be(atttype)),
   13435             :                          errhint("You must specify a btree operator class or define a default btree operator class for the data type.")));
   13436             :         }
   13437             :         else
   13438           3 :             partopclass[attn] = ResolveOpClass(pelem->opclass,
   13439             :                                                atttype,
   13440             :                                                "btree",
   13441             :                                                BTREE_AM_OID);
   13442             : 
   13443          94 :         attn++;
   13444             :     }
   13445          75 : }
   13446             : 
   13447             : /*
   13448             :  * PartConstraintImpliedByRelConstraint
   13449             :  *      Does scanrel's existing constraints imply the partition constraint?
   13450             :  *
   13451             :  * Existing constraints includes its check constraints and column-level
   13452             :  * NOT NULL constraints and partConstraint describes the partition constraint.
   13453             :  */
   13454             : static bool
   13455          33 : PartConstraintImpliedByRelConstraint(Relation scanrel,
   13456             :                                      List *partConstraint)
   13457             : {
   13458          33 :     List       *existConstraint = NIL;
   13459          33 :     TupleConstr *constr = RelationGetDescr(scanrel)->constr;
   13460             :     int         num_check,
   13461             :                 i;
   13462             : 
   13463          33 :     if (constr && constr->has_not_null)
   13464             :     {
   13465          19 :         int         natts = scanrel->rd_att->natts;
   13466             : 
   13467          71 :         for (i = 1; i <= natts; i++)
   13468             :         {
   13469          52 :             Form_pg_attribute att = TupleDescAttr(scanrel->rd_att, i - 1);
   13470             : 
   13471          52 :             if (att->attnotnull && !att->attisdropped)
   13472             :             {
   13473          33 :                 NullTest   *ntest = makeNode(NullTest);
   13474             : 
   13475          33 :                 ntest->arg = (Expr *) makeVar(1,
   13476             :                                               i,
   13477             :                                               att->atttypid,
   13478             :                                               att->atttypmod,
   13479             :                                               att->attcollation,
   13480             :                                               0);
   13481          33 :                 ntest->nulltesttype = IS_NOT_NULL;
   13482             : 
   13483             :                 /*
   13484             :                  * argisrow=false is correct even for a composite column,
   13485             :                  * because attnotnull does not represent a SQL-spec IS NOT
   13486             :                  * NULL test in such a case, just IS DISTINCT FROM NULL.
   13487             :                  */
   13488          33 :                 ntest->argisrow = false;
   13489          33 :                 ntest->location = -1;
   13490          33 :                 existConstraint = lappend(existConstraint, ntest);
   13491             :             }
   13492             :         }
   13493             :     }
   13494             : 
   13495          33 :     num_check = (constr != NULL) ? constr->num_check : 0;
   13496          51 :     for (i = 0; i < num_check; i++)
   13497             :     {
   13498             :         Node       *cexpr;
   13499             : 
   13500             :         /*
   13501             :          * If this constraint hasn't been fully validated yet, we must ignore
   13502             :          * it here.
   13503             :          */
   13504          18 :         if (!constr->check[i].ccvalid)
   13505           0 :             continue;
   13506             : 
   13507          18 :         cexpr = stringToNode(constr->check[i].ccbin);
   13508             : 
   13509             :         /*
   13510             :          * Run each expression through const-simplification and
   13511             :          * canonicalization.  It is necessary, because we will be comparing it
   13512             :          * to similarly-processed partition constraint expressions, and may
   13513             :          * fail to detect valid matches without this.
   13514             :          */
   13515          18 :         cexpr = eval_const_expressions(NULL, cexpr);
   13516          18 :         cexpr = (Node *) canonicalize_qual((Expr *) cexpr);
   13517             : 
   13518          18 :         existConstraint = list_concat(existConstraint,
   13519             :                                       make_ands_implicit((Expr *) cexpr));
   13520             :     }
   13521             : 
   13522          33 :     if (existConstraint != NIL)
   13523          24 :         existConstraint = list_make1(make_ands_explicit(existConstraint));
   13524             : 
   13525             :     /* And away we go ... */
   13526          33 :     return predicate_implied_by(partConstraint, existConstraint, true);
   13527             : }
   13528             : 
   13529             : /*
   13530             :  * ValidatePartitionConstraints
   13531             :  *
   13532             :  * Check whether all rows in the given table obey the given partition
   13533             :  * constraint; if so, it can be attached as a partition.  We do this by
   13534             :  * scanning the table (or all of its leaf partitions) row by row, except when
   13535             :  * the existing constraints are sufficient to prove that the new partitioning
   13536             :  * constraint must already hold.
   13537             :  */
   13538             : static void
   13539          33 : ValidatePartitionConstraints(List **wqueue, Relation scanrel,
   13540             :                              List *scanrel_children,
   13541             :                              List *partConstraint)
   13542             : {
   13543             :     bool        found_whole_row;
   13544             :     ListCell   *lc;
   13545             : 
   13546          33 :     if (partConstraint == NIL)
   13547           7 :         return;
   13548             : 
   13549             :     /*
   13550             :      * Based on the table's existing constraints, determine if we can skip
   13551             :      * scanning the table to validate the partition constraint.
   13552             :      */
   13553          33 :     if (PartConstraintImpliedByRelConstraint(scanrel, partConstraint))
   13554             :     {
   13555           7 :         ereport(INFO,
   13556             :                 (errmsg("partition constraint for table \"%s\" is implied by existing constraints",
   13557             :                         RelationGetRelationName(scanrel))));
   13558           7 :         return;
   13559             :     }
   13560             : 
   13561             :     /* Constraints proved insufficient, so we need to scan the table. */
   13562          58 :     foreach(lc, scanrel_children)
   13563             :     {
   13564             :         AlteredTableInfo *tab;
   13565          32 :         Oid         part_relid = lfirst_oid(lc);
   13566             :         Relation    part_rel;
   13567          32 :         List       *my_partconstr = partConstraint;
   13568             : 
   13569             :         /* Lock already taken */
   13570          32 :         if (part_relid != RelationGetRelid(scanrel))
   13571           6 :             part_rel = heap_open(part_relid, NoLock);
   13572             :         else
   13573          26 :             part_rel = scanrel;
   13574             : 
   13575             :         /*
   13576             :          * Skip if the partition is itself a partitioned table.  We can only
   13577             :          * ever scan RELKIND_RELATION relations.
   13578             :          */
   13579          32 :         if (part_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
   13580             :         {
   13581           6 :             if (part_rel != scanrel)
   13582           0 :                 heap_close(part_rel, NoLock);
   13583           6 :             continue;
   13584             :         }
   13585             : 
   13586          26 :         if (part_rel != scanrel)
   13587             :         {
   13588             :             /*
   13589             :              * Adjust the constraint for scanrel so that it matches this
   13590             :              * partition's attribute numbers.
   13591             :              */
   13592           6 :             my_partconstr = map_partition_varattnos(my_partconstr, 1,
   13593             :                                                     part_rel, scanrel,
   13594             :                                                     &found_whole_row);
   13595             :             /* There can never be a whole-row reference here */
   13596           6 :             if (found_whole_row)
   13597           0 :                 elog(ERROR, "unexpected whole-row reference found in partition key");
   13598             :         }
   13599             : 
   13600             :         /* Grab a work queue entry. */
   13601          26 :         tab = ATGetQueueEntry(wqueue, part_rel);
   13602          26 :         tab->partition_constraint = (Expr *) linitial(my_partconstr);
   13603             : 
   13604             :         /* keep our lock until commit */
   13605          26 :         if (part_rel != scanrel)
   13606           6 :             heap_close(part_rel, NoLock);
   13607             :     }
   13608             : }
   13609             : 
   13610             : /*
   13611             :  * ALTER TABLE <name> ATTACH PARTITION <partition-name> FOR VALUES
   13612             :  *
   13613             :  * Return the address of the newly attached partition.
   13614             :  */
   13615             : static ObjectAddress
   13616          55 : ATExecAttachPartition(List **wqueue, Relation rel, PartitionCmd *cmd)
   13617             : {
   13618             :     Relation    attachrel,
   13619             :                 catalog;
   13620             :     List       *attachrel_children;
   13621             :     List       *partConstraint;
   13622             :     SysScanDesc scan;
   13623             :     ScanKeyData skey;
   13624             :     AttrNumber  attno;
   13625             :     int         natts;
   13626             :     TupleDesc   tupleDesc;
   13627             :     ObjectAddress address;
   13628             :     const char *trigger_name;
   13629             :     bool        found_whole_row;
   13630             : 
   13631          55 :     attachrel = heap_openrv(cmd->name, AccessExclusiveLock);
   13632             : 
   13633             :     /*
   13634             :      * Must be owner of both parent and source table -- parent was checked by
   13635             :      * ATSimplePermissions call in ATPrepCmd
   13636             :      */
   13637          54 :     ATSimplePermissions(attachrel, ATT_TABLE | ATT_FOREIGN_TABLE);
   13638             : 
   13639             :     /* A partition can only have one parent */
   13640          53 :     if (attachrel->rd_rel->relispartition)
   13641           1 :         ereport(ERROR,
   13642             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   13643             :                  errmsg("\"%s\" is already a partition",
   13644             :                         RelationGetRelationName(attachrel))));
   13645             : 
   13646          52 :     if (OidIsValid(attachrel->rd_rel->reloftype))
   13647           1 :         ereport(ERROR,
   13648             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   13649             :                  errmsg("cannot attach a typed table as partition")));
   13650             : 
   13651             :     /*
   13652             :      * Table being attached should not already be part of inheritance; either
   13653             :      * as a child table...
   13654             :      */
   13655          51 :     catalog = heap_open(InheritsRelationId, AccessShareLock);
   13656          51 :     ScanKeyInit(&skey,
   13657             :                 Anum_pg_inherits_inhrelid,
   13658             :                 BTEqualStrategyNumber, F_OIDEQ,
   13659             :                 ObjectIdGetDatum(RelationGetRelid(attachrel)));
   13660          51 :     scan = systable_beginscan(catalog, InheritsRelidSeqnoIndexId, true,
   13661             :                               NULL, 1, &skey);
   13662          51 :     if (HeapTupleIsValid(systable_getnext(scan)))
   13663           1 :         ereport(ERROR,
   13664             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   13665             :                  errmsg("cannot attach inheritance child as partition")));
   13666          50 :     systable_endscan(scan);
   13667             : 
   13668             :     /* ...or as a parent table (except the case when it is partitioned) */
   13669          50 :     ScanKeyInit(&skey,
   13670             :                 Anum_pg_inherits_inhparent,
   13671             :                 BTEqualStrategyNumber, F_OIDEQ,
   13672             :                 ObjectIdGetDatum(RelationGetRelid(attachrel)));
   13673          50 :     scan = systable_beginscan(catalog, InheritsParentIndexId, true, NULL,
   13674             :                               1, &skey);
   13675          62 :     if (HeapTupleIsValid(systable_getnext(scan)) &&
   13676          12 :         attachrel->rd_rel->relkind == RELKIND_RELATION)
   13677           1 :         ereport(ERROR,
   13678             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   13679             :                  errmsg("cannot attach inheritance parent as partition")));
   13680          49 :     systable_endscan(scan);
   13681          49 :     heap_close(catalog, AccessShareLock);
   13682             : 
   13683             :     /*
   13684             :      * Prevent circularity by seeing if rel is a partition of attachrel. (In
   13685             :      * particular, this disallows making a rel a partition of itself.)
   13686             :      *
   13687             :      * We do that by checking if rel is a member of the list of attachRel's
   13688             :      * partitions provided the latter is partitioned at all.  We want to avoid
   13689             :      * having to construct this list again, so we request the strongest lock
   13690             :      * on all partitions.  We need the strongest lock, because we may decide
   13691             :      * to scan them if we find out that the table being attached (or its leaf
   13692             :      * partitions) may contain rows that violate the partition constraint. If
   13693             :      * the table has a constraint that would prevent such rows, which by
   13694             :      * definition is present in all the partitions, we need not scan the
   13695             :      * table, nor its partitions.  But we cannot risk a deadlock by taking a
   13696             :      * weaker lock now and the stronger one only when needed.
   13697             :      */
   13698          49 :     attachrel_children = find_all_inheritors(RelationGetRelid(attachrel),
   13699             :                                              AccessExclusiveLock, NULL);
   13700          49 :     if (list_member_oid(attachrel_children, RelationGetRelid(rel)))
   13701           2 :         ereport(ERROR,
   13702             :                 (errcode(ERRCODE_DUPLICATE_TABLE),
   13703             :                  errmsg("circular inheritance not allowed"),
   13704             :                  errdetail("\"%s\" is already a child of \"%s\".",
   13705             :                            RelationGetRelationName(rel),
   13706             :                            RelationGetRelationName(attachrel))));
   13707             : 
   13708             :     /* Temp parent cannot have a partition that is itself not a temp */
   13709          48 :     if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
   13710           1 :         attachrel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
   13711           1 :         ereport(ERROR,
   13712             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   13713             :                  errmsg("cannot attach a permanent relation as partition of temporary relation \"%s\"",
   13714             :                         RelationGetRelationName(rel))));
   13715             : 
   13716             :     /* If the parent is temp, it must belong to this session */
   13717          46 :     if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
   13718           0 :         !rel->rd_islocaltemp)
   13719           0 :         ereport(ERROR,
   13720             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   13721             :                  errmsg("cannot attach as partition of temporary relation of another session")));
   13722             : 
   13723             :     /* Ditto for the partition */
   13724          46 :     if (attachrel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
   13725           0 :         !attachrel->rd_islocaltemp)
   13726           0 :         ereport(ERROR,
   13727             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   13728             :                  errmsg("cannot attach temporary relation of another session as partition")));
   13729             : 
   13730             :     /* If parent has OIDs then child must have OIDs */
   13731          46 :     if (rel->rd_rel->relhasoids && !attachrel->rd_rel->relhasoids)
   13732           1 :         ereport(ERROR,
   13733             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   13734             :                  errmsg("cannot attach table \"%s\" without OIDs as partition of"
   13735             :                         " table \"%s\" with OIDs", RelationGetRelationName(attachrel),
   13736             :                         RelationGetRelationName(rel))));
   13737             : 
   13738             :     /* OTOH, if parent doesn't have them, do not allow in attachrel either */
   13739          45 :     if (attachrel->rd_rel->relhasoids && !rel->rd_rel->relhasoids)
   13740           1 :         ereport(ERROR,
   13741             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   13742             :                  errmsg("cannot attach table \"%s\" with OIDs as partition of table"
   13743             :                         " \"%s\" without OIDs", RelationGetRelationName(attachrel),
   13744             :                         RelationGetRelationName(rel))));
   13745             : 
   13746             :     /* Check if there are any columns in attachrel that aren't in the parent */
   13747          44 :     tupleDesc = RelationGetDescr(attachrel);
   13748          44 :     natts = tupleDesc->natts;
   13749         155 :     for (attno = 1; attno <= natts; attno++)
   13750             :     {
   13751         113 :         Form_pg_attribute attribute = TupleDescAttr(tupleDesc, attno - 1);
   13752         113 :         char       *attributeName = NameStr(attribute->attname);
   13753             : 
   13754             :         /* Ignore dropped */
   13755         113 :         if (attribute->attisdropped)
   13756          14 :             continue;
   13757             : 
   13758             :         /* Try to find the column in parent (matching on column name) */
   13759          99 :         if (!SearchSysCacheExists2(ATTNAME,
   13760             :                                    ObjectIdGetDatum(RelationGetRelid(rel)),
   13761             :                                    CStringGetDatum(attributeName)))
   13762           2 :             ereport(ERROR,
   13763             :                     (errcode(ERRCODE_DATATYPE_MISMATCH),
   13764             :                      errmsg("table \"%s\" contains column \"%s\" not found in parent \"%s\"",
   13765             :                             RelationGetRelationName(attachrel), attributeName,
   13766             :                             RelationGetRelationName(rel)),
   13767             :                      errdetail("New partition should contain only the columns present in parent.")));
   13768             :     }
   13769             : 
   13770             :     /*
   13771             :      * If child_rel has row-level triggers with transition tables, we
   13772             :      * currently don't allow it to become a partition.  See also prohibitions
   13773             :      * in ATExecAddInherit() and CreateTrigger().
   13774             :      */
   13775          42 :     trigger_name = FindTriggerIncompatibleWithInheritance(attachrel->trigdesc);
   13776          42 :     if (trigger_name != NULL)
   13777           1 :         ereport(ERROR,
   13778             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   13779             :                  errmsg("trigger \"%s\" prevents table \"%s\" from becoming a partition",
   13780             :                         trigger_name, RelationGetRelationName(attachrel)),
   13781             :                  errdetail("ROW triggers with transition tables are not supported on partitions")));
   13782             : 
   13783             :     /* OK to create inheritance.  Rest of the checks performed there */
   13784          41 :     CreateInheritance(attachrel, rel);
   13785             : 
   13786             :     /*
   13787             :      * Check that the new partition's bound is valid and does not overlap any
   13788             :      * of existing partitions of the parent - note that it does not return on
   13789             :      * error.
   13790             :      */
   13791          34 :     check_new_partition_bound(RelationGetRelationName(attachrel), rel,
   13792             :                               cmd->bound);
   13793             : 
   13794             :     /* Update the pg_class entry. */
   13795          33 :     StorePartitionBound(attachrel, rel, cmd->bound);
   13796             : 
   13797             :     /*
   13798             :      * Generate partition constraint from the partition bound specification.
   13799             :      * If the parent itself is a partition, make sure to include its
   13800             :      * constraint as well.
   13801             :      */
   13802          33 :     partConstraint = list_concat(get_qual_from_partbound(attachrel, rel,
   13803             :                                                          cmd->bound),
   13804             :                                  RelationGetPartitionQual(rel));
   13805          33 :     partConstraint = (List *) eval_const_expressions(NULL,
   13806             :                                                      (Node *) partConstraint);
   13807          33 :     partConstraint = (List *) canonicalize_qual((Expr *) partConstraint);
   13808          33 :     partConstraint = list_make1(make_ands_explicit(partConstraint));
   13809             : 
   13810             :     /*
   13811             :      * Adjust the generated constraint to match this partition's attribute
   13812             :      * numbers.
   13813             :      */
   13814          33 :     partConstraint = map_partition_varattnos(partConstraint, 1, attachrel,
   13815             :                                              rel, &found_whole_row);
   13816             :     /* There can never be a whole-row reference here */
   13817          33 :     if (found_whole_row)
   13818           0 :         elog(ERROR, "unexpected whole-row reference found in partition key");
   13819             : 
   13820             :     /* Validate partition constraints against the table being attached. */
   13821          33 :     ValidatePartitionConstraints(wqueue, attachrel, attachrel_children,
   13822             :                                  partConstraint);
   13823             : 
   13824          33 :     ObjectAddressSet(address, RelationRelationId, RelationGetRelid(attachrel));
   13825             : 
   13826             :     /* keep our lock until commit */
   13827          33 :     heap_close(attachrel, NoLock);
   13828             : 
   13829          33 :     return address;
   13830             : }
   13831             : 
   13832             : /*
   13833             :  * ALTER TABLE DETACH PARTITION
   13834             :  *
   13835             :  * Return the address of the relation that is no longer a partition of rel.
   13836             :  */
   13837             : static ObjectAddress
   13838          11 : ATExecDetachPartition(Relation rel, RangeVar *name)
   13839             : {
   13840             :     Relation    partRel,
   13841             :                 classRel;
   13842             :     HeapTuple   tuple,
   13843             :                 newtuple;
   13844             :     Datum       new_val[Natts_pg_class];
   13845             :     bool        isnull,
   13846             :                 new_null[Natts_pg_class],
   13847             :                 new_repl[Natts_pg_class];
   13848             :     ObjectAddress address;
   13849             : 
   13850          11 :     partRel = heap_openrv(name, AccessShareLock);
   13851             : 
   13852             :     /* All inheritance related checks are performed within the function */
   13853          10 :     RemoveInheritance(partRel, rel);
   13854             : 
   13855             :     /* Update pg_class tuple */
   13856           8 :     classRel = heap_open(RelationRelationId, RowExclusiveLock);
   13857           8 :     tuple = SearchSysCacheCopy1(RELOID,
   13858             :                                 ObjectIdGetDatum(RelationGetRelid(partRel)));
   13859           8 :     Assert(((Form_pg_class) GETSTRUCT(tuple))->relispartition);
   13860             : 
   13861           8 :     (void) SysCacheGetAttr(RELOID, tuple, Anum_pg_class_relpartbound,
   13862             :                            &isnull);
   13863           8 :     Assert(!isnull);
   13864             : 
   13865             :     /* Clear relpartbound and reset relispartition */
   13866           8 :     memset(new_val, 0, sizeof(new_val));
   13867           8 :     memset(new_null, false, sizeof(new_null));
   13868           8 :     memset(new_repl, false, sizeof(new_repl));
   13869           8 :     new_val[Anum_pg_class_relpartbound - 1] = (Datum) 0;
   13870           8 :     new_null[Anum_pg_class_relpartbound - 1] = true;
   13871           8 :     new_repl[Anum_pg_class_relpartbound - 1] = true;
   13872           8 :     newtuple = heap_modify_tuple(tuple, RelationGetDescr(classRel),
   13873             :                                  new_val, new_null, new_repl);
   13874             : 
   13875           8 :     ((Form_pg_class) GETSTRUCT(newtuple))->relispartition = false;
   13876           8 :     CatalogTupleUpdate(classRel, &newtuple->t_self, newtuple);
   13877           8 :     heap_freetuple(newtuple);
   13878           8 :     heap_close(classRel, RowExclusiveLock);
   13879             : 
   13880             :     /*
   13881             :      * Invalidate the parent's relcache so that the partition is no longer
   13882             :      * included in its partition descriptor.
   13883             :      */
   13884           8 :     CacheInvalidateRelcache(rel);
   13885             : 
   13886           8 :     ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partRel));
   13887             : 
   13888             :     /* keep our lock until commit */
   13889           8 :     heap_close(partRel, NoLock);
   13890             : 
   13891           8 :     return address;
   13892             : }

Generated by: LCOV version 1.11